Gondwana Game Engine Development (master) - 2.6.1
Gondwana is a cross-platform 2.5D game and rendering engine written in C#/.NET 8. It provides fine-grained control over rendering, timing, and scene composition, with built-in support for parallax, z-ordering, pixel overhang, collision detection, and particle effects. Gondwana targets desktop and web platforms using SkiaSharp for graphics and pluggable audio backends for desktop and browser targets.
Loading...
Searching...
No Matches
TilesheetDefinitionSerializer.cs
3using Newtonsoft.Json;
4
6
10public static class TilesheetDefinitionSerializer
11{
12 private static readonly JsonSerializerSettings Settings = new()
13 {
14 Formatting = Formatting.Indented,
15 NullValueHandling = NullValueHandling.Ignore,
16 DefaultValueHandling = DefaultValueHandling.Include,
17 MissingMemberHandling = MissingMemberHandling.Ignore
18 };
19
25 public static TilesheetDefinition Load(string filePath)
26 {
27 if (string.IsNullOrWhiteSpace(filePath))
28 throw new ArgumentException("GTS file path must be a non-empty string.", nameof(filePath));
29
30 var fullPath = Path.GetFullPath(filePath);
31 if (!File.Exists(fullPath))
32 throw new FileNotFoundException($"GTS file not found: {fullPath}", fullPath);
33
34 try
35 {
36 var definition = FromJson(File.ReadAllText(fullPath), fullPath);
37 ApplyDefaultSource(
38 definition,
39 TilesheetDefinitionSource.LooseDefinitionFile(fullPath));
40 definition.LoadStamp = new(definition, filePath: fullPath);
41 return definition;
42 }
43 catch (JsonException ex)
44 {
45 throw new InvalidDataException($"Failed to deserialize GTS file: {fullPath}", ex);
46 }
47 }
48
54 public static TilesheetDefinition Load(Stream stream)
55 {
56 ArgumentNullException.ThrowIfNull(stream);
57
58 if (!stream.CanRead)
59 throw new ArgumentException("Stream must be readable.", nameof(stream));
60
61 using var reader = new StreamReader(stream, leaveOpen: true);
62 return FromJson(reader.ReadToEnd());
63 }
64
70 public static void Save(string filePath, TilesheetDefinition definition)
71 {
72 if (string.IsNullOrWhiteSpace(filePath))
73 throw new ArgumentException("GTS file path must be a non-empty string.", nameof(filePath));
74
75 ArgumentNullException.ThrowIfNull(definition);
76
77 var fullPath = Path.GetFullPath(filePath);
78 var directory = Path.GetDirectoryName(fullPath);
79 if (!string.IsNullOrWhiteSpace(directory))
80 Directory.CreateDirectory(directory);
81
82 var definitionToSave = CreateDefinitionForSaving(definition, fullPath);
83 File.WriteAllText(fullPath, ToJson(definitionToSave));
84 }
85
91 public static TilesheetDefinition FromJson(string json) =>
92 FromJson(json, sourceDescription: null);
93
99 public static string ToJson(TilesheetDefinition definition)
100 {
101 ArgumentNullException.ThrowIfNull(definition);
102 return JsonConvert.SerializeObject(definition, Settings);
103 }
104
112 public static TilesheetDefinition FromTilesheet(
113 Tilesheet tilesheet,
114 string? baseDirectory = null,
115 bool makePathsRelative = false)
116 {
117 ArgumentNullException.ThrowIfNull(tilesheet);
118
119 return new TilesheetDefinition
120 {
121 Name = tilesheet.Name,
122 Image = CreateImageDefinition(
123 tilesheet,
124 baseDirectory,
125 makePathsRelative),
126 Regions = tilesheet.Regions
127 .Select(CreateRegionDefinition)
128 .ToList(),
129 Mask = CreateMaskDefinition(tilesheet),
130 PremultiplyAlpha = tilesheet.Premultiplied && tilesheet.MaskColor is null,
131 Source = TilesheetDefinitionSource.Generated()
132 };
133 }
134
148 public static void Save(
149 string filePath,
150 Tilesheet tilesheet,
151 bool makePathsRelative = true)
152 {
153 if (string.IsNullOrWhiteSpace(filePath))
154 throw new ArgumentException("GTS file path must be a non-empty string.", nameof(filePath));
155
156 ArgumentNullException.ThrowIfNull(tilesheet);
157
158 var fullPath = Path.GetFullPath(filePath);
159 if (string.IsNullOrWhiteSpace(tilesheet.ImageFilePath) &&
160 string.IsNullOrWhiteSpace(tilesheet.AssetIdentifier?.AssetsFile.SourcePath))
161 {
162 tilesheet.PersistImageToFile(
163 Path.ChangeExtension(fullPath, ".png"));
164 }
165
166 var definition = FromTilesheet(
167 tilesheet,
168 Path.GetDirectoryName(fullPath),
169 makePathsRelative);
170
171 Save(fullPath, definition);
172 }
173
181 public static string ToJson(
182 Tilesheet tilesheet,
183 string? baseDirectory = null,
184 bool makePathsRelative = false)
185 {
186 return ToJson(
187 FromTilesheet(
188 tilesheet,
189 baseDirectory,
190 makePathsRelative));
191 }
192
193 private static TilesheetDefinition FromJson(
194 string json,
195 string? sourceDescription)
196 {
197 if (string.IsNullOrWhiteSpace(json))
198 throw new ArgumentException("GTS JSON content must be a non-empty string.", nameof(json));
199
200 try
201 {
202 var definition = JsonConvert.DeserializeObject<TilesheetDefinition>(json, Settings);
203 if (definition is null)
204 {
205 var source = string.IsNullOrWhiteSpace(sourceDescription)
206 ? "GTS JSON content"
207 : sourceDescription;
208
209 throw new InvalidDataException(
210 $"Failed to deserialize {source}. Result was null.");
211 }
212
213 // Maintain compatibility with files written before frame-level metadata existed,
214 // and tolerate explicit null collection values from hand-authored JSON.
215 definition.Regions ??= [];
216 foreach (var region in definition.Regions)
217 {
218 if (region is null)
219 throw new InvalidDataException("GTS Regions cannot contain null entries.");
220 region.Frames ??= [];
221 }
222
223 return definition;
224 }
225 catch (JsonException ex)
226 {
227 var source = string.IsNullOrWhiteSpace(sourceDescription)
228 ? "GTS JSON content"
229 : sourceDescription;
230
231 throw new InvalidDataException($"Failed to deserialize {source}.", ex);
232 }
233 }
234
235 private static TilesheetImageDefinition CreateImageDefinition(
236 Tilesheet tilesheet,
237 string? baseDirectory,
238 bool makePathsRelative)
239 {
240 if (!string.IsNullOrWhiteSpace(tilesheet.ImageFilePath))
241 {
242 return new TilesheetImageDefinition
243 {
244 FilePath = NormalizePath(
245 tilesheet.ImageFilePath,
246 baseDirectory,
247 makePathsRelative)
248 };
249 }
250
251 if (tilesheet.AssetIdentifier is not null)
252 {
253 var assetIdentifier = tilesheet.AssetIdentifier;
254 if (assetIdentifier.AssetType != AssetTypes.Image)
255 {
256 throw new InvalidOperationException(
257 $"Tilesheet '{tilesheet.Name}' references an asset of type '{assetIdentifier.AssetType}', but expected '{AssetTypes.Image}'.");
258 }
259
260 if (assetIdentifier.AssetsFile is null ||
261 string.IsNullOrWhiteSpace(assetIdentifier.AssetsFile.SourcePath))
262 {
263 throw new InvalidOperationException(
264 $"Tilesheet '{tilesheet.Name}' has an asset identifier, but the assets file path is missing.");
265 }
266
267 return new TilesheetImageDefinition
268 {
269 AssetsFilePath = NormalizePath(
270 assetIdentifier.AssetsFile.SourcePath,
271 baseDirectory,
272 makePathsRelative),
273 AssetEntryName = assetIdentifier.AssetName
274 };
275 }
276
277 throw new InvalidOperationException(
278 $"Tilesheet '{tilesheet.Name}' cannot be converted to a TilesheetDefinition because it has no ImageFilePath or AssetIdentifier.");
279 }
280
281 private static TilesheetRegionDefinition CreateRegionDefinition(
282 TilesheetRegion region)
283 {
284 var definition = new TilesheetRegionDefinition
285 {
286 Name = region.Name,
287 Area = region.Area,
288 TileSize = region.TileSize,
289 TilePadding = region.TilePadding,
290 RegionMargin = region.RegionMargin,
291 Overhang = region.Overhang,
292 CollisionAdjust = region.CollisionAdjust,
293 CollisionType = region.CollisionType
294 };
295
296 // Persist every frame coordinate while retaining the distinction between an
297 // inherited region default (null) and an explicit frame-level override.
298 for (int y = 0; y < region.Rows; y++)
299 {
300 for (int x = 0; x < region.Columns; x++)
301 {
302 CollisionAdjust? collisionAdjust =
303 region.TryGetFrameCollisionAdjustOverride(
304 x,
305 y,
306 out var frameCollisionAdjust)
307 ? frameCollisionAdjust
308 : null;
309
310 TileCollisionType? collisionType =
311 region.TryGetFrameCollisionTypeOverride(
312 x,
313 y,
314 out var frameCollisionType)
315 ? frameCollisionType
316 : null;
317
318 definition.Frames.Add(new TilesheetFrameDefinition
319 {
320 XTile = x,
321 YTile = y,
322 CollisionAdjust = collisionAdjust,
323 CollisionType = collisionType
324 });
325 }
326 }
327
328 return definition;
329 }
330
331 private static TilesheetMaskDefinition? CreateMaskDefinition(Tilesheet tilesheet)
332 {
333 if (tilesheet.MaskColor is null)
334 return null;
335
336 var color = tilesheet.MaskColor.Value;
337 return new TilesheetMaskDefinition
338 {
339 Red = color.Red,
340 Green = color.Green,
341 Blue = color.Blue,
342 Alpha = color.Alpha,
343 Tolerance = tilesheet.MaskTolerance
344 };
345 }
346
347 private static void ApplyDefaultSource(
348 TilesheetDefinition definition,
349 TilesheetDefinitionSource source)
350 {
351 if (definition.Source.Kind == TilesheetDefinitionSourceKind.None)
352 definition.Source = source;
353 }
354
355 private static TilesheetDefinition CreateDefinitionForSaving(
356 TilesheetDefinition definition,
357 string fullPath)
358 {
359 if (definition.Source.Kind != TilesheetDefinitionSourceKind.None)
360 return definition;
361
362 var clone = FromJson(ToJson(definition));
363 clone.Source = TilesheetDefinitionSource.LooseDefinitionFile(fullPath);
364 return clone;
365 }
366
367 private static string NormalizePath(
368 string path,
369 string? baseDirectory,
370 bool makeRelative)
371 {
372 if (string.IsNullOrWhiteSpace(path))
373 return path;
374
375 if (!makeRelative || string.IsNullOrWhiteSpace(baseDirectory))
376 return path.Replace('\\', '/');
377
378 if (!Path.IsPathRooted(path))
379 return path.Replace('\\', '/');
380
381 var fullPath = Path.GetFullPath(path);
382 var fullBaseDirectory = Path.GetFullPath(baseDirectory);
383
384 return Path.GetRelativePath(fullBaseDirectory, fullPath).Replace('\\', '/');
385 }
386}
AssetTypes
Defines the types of assets that can be stored and managed by the engine.
Definition AssetTypes.cs:8
@ TilesheetDefinition
Represents a tilesheet definition file (.gts) for Gondwana.Drawing.Tilesheets.GTS....
Definition AssetTypes.cs:47
@ Image
Represents an image file type.
Definition AssetTypes.cs:12
TilesheetDefinitionSourceKind
Indicates where a tilesheet definition came from.
TileCollisionType
Defines the default collision behavior associated with a tilesheet region, frame, or runtime tile.