10public static class TilesheetDefinitionSerializer
12 private static readonly JsonSerializerSettings Settings =
new()
14 Formatting = Formatting.Indented,
15 NullValueHandling = NullValueHandling.Ignore,
16 DefaultValueHandling = DefaultValueHandling.Include,
17 MissingMemberHandling = MissingMemberHandling.Ignore
27 if (
string.IsNullOrWhiteSpace(filePath))
28 throw new ArgumentException(
"GTS file path must be a non-empty string.", nameof(filePath));
30 var fullPath = Path.GetFullPath(filePath);
31 if (!File.Exists(fullPath))
32 throw new FileNotFoundException($
"GTS file not found: {fullPath}", fullPath);
36 var definition = FromJson(File.ReadAllText(fullPath), fullPath);
39 TilesheetDefinitionSource.LooseDefinitionFile(fullPath));
40 definition.LoadStamp =
new(definition, filePath: fullPath);
43 catch (JsonException ex)
45 throw new InvalidDataException($
"Failed to deserialize GTS file: {fullPath}", ex);
56 ArgumentNullException.ThrowIfNull(stream);
59 throw new ArgumentException(
"Stream must be readable.", nameof(stream));
61 using var reader =
new StreamReader(stream, leaveOpen:
true);
62 return FromJson(reader.ReadToEnd());
72 if (
string.IsNullOrWhiteSpace(filePath))
73 throw new ArgumentException(
"GTS file path must be a non-empty string.", nameof(filePath));
75 ArgumentNullException.ThrowIfNull(definition);
77 var fullPath = Path.GetFullPath(filePath);
78 var directory = Path.GetDirectoryName(fullPath);
79 if (!
string.IsNullOrWhiteSpace(directory))
80 Directory.CreateDirectory(directory);
82 var definitionToSave = CreateDefinitionForSaving(definition, fullPath);
83 File.WriteAllText(fullPath, ToJson(definitionToSave));
92 FromJson(json, sourceDescription:
null);
101 ArgumentNullException.ThrowIfNull(definition);
102 return JsonConvert.SerializeObject(definition, Settings);
114 string? baseDirectory =
null,
115 bool makePathsRelative =
false)
117 ArgumentNullException.ThrowIfNull(tilesheet);
121 Name = tilesheet.Name,
122 Image = CreateImageDefinition(
126 Regions = tilesheet.Regions
127 .Select(CreateRegionDefinition)
129 Mask = CreateMaskDefinition(tilesheet),
130 PremultiplyAlpha = tilesheet.Premultiplied && tilesheet.MaskColor is
null,
131 Source = TilesheetDefinitionSource.Generated()
148 public static void Save(
151 bool makePathsRelative =
true)
153 if (
string.IsNullOrWhiteSpace(filePath))
154 throw new ArgumentException(
"GTS file path must be a non-empty string.", nameof(filePath));
156 ArgumentNullException.ThrowIfNull(tilesheet);
158 var fullPath = Path.GetFullPath(filePath);
159 if (
string.IsNullOrWhiteSpace(tilesheet.ImageFilePath) &&
160 string.IsNullOrWhiteSpace(tilesheet.AssetIdentifier?.AssetsFile.SourcePath))
162 tilesheet.PersistImageToFile(
163 Path.ChangeExtension(fullPath,
".png"));
166 var definition = FromTilesheet(
168 Path.GetDirectoryName(fullPath),
171 Save(fullPath, definition);
181 public static string ToJson(
183 string? baseDirectory =
null,
184 bool makePathsRelative =
false)
195 string? sourceDescription)
197 if (
string.IsNullOrWhiteSpace(json))
198 throw new ArgumentException(
"GTS JSON content must be a non-empty string.", nameof(json));
203 if (definition is
null)
205 var source =
string.IsNullOrWhiteSpace(sourceDescription)
209 throw new InvalidDataException(
210 $
"Failed to deserialize {source}. Result was null.");
215 definition.Regions ??= [];
216 foreach (var region
in definition.Regions)
219 throw new InvalidDataException(
"GTS Regions cannot contain null entries.");
220 region.Frames ??= [];
225 catch (JsonException ex)
227 var source =
string.IsNullOrWhiteSpace(sourceDescription)
231 throw new InvalidDataException($
"Failed to deserialize {source}.", ex);
235 private static TilesheetImageDefinition CreateImageDefinition(
237 string? baseDirectory,
238 bool makePathsRelative)
240 if (!
string.IsNullOrWhiteSpace(tilesheet.ImageFilePath))
242 return new TilesheetImageDefinition
244 FilePath = NormalizePath(
245 tilesheet.ImageFilePath,
251 if (tilesheet.AssetIdentifier is not
null)
253 var assetIdentifier = tilesheet.AssetIdentifier;
254 if (assetIdentifier.AssetType !=
AssetTypes.Image)
256 throw new InvalidOperationException(
257 $
"Tilesheet '{tilesheet.Name}' references an asset of type '{assetIdentifier.AssetType}', but expected '{AssetTypes.Image}'.");
260 if (assetIdentifier.AssetsFile is
null ||
261 string.IsNullOrWhiteSpace(assetIdentifier.AssetsFile.SourcePath))
263 throw new InvalidOperationException(
264 $
"Tilesheet '{tilesheet.Name}' has an asset identifier, but the assets file path is missing.");
267 return new TilesheetImageDefinition
269 AssetsFilePath = NormalizePath(
270 assetIdentifier.AssetsFile.SourcePath,
273 AssetEntryName = assetIdentifier.AssetName
277 throw new InvalidOperationException(
278 $
"Tilesheet '{tilesheet.Name}' cannot be converted to a TilesheetDefinition because it has no ImageFilePath or AssetIdentifier.");
281 private static TilesheetRegionDefinition CreateRegionDefinition(
282 TilesheetRegion region)
284 var definition =
new TilesheetRegionDefinition
288 TileSize = region.TileSize,
289 TilePadding = region.TilePadding,
290 RegionMargin = region.RegionMargin,
291 Overhang = region.Overhang,
292 CollisionAdjust = region.CollisionAdjust,
293 CollisionType = region.CollisionType
298 for (
int y = 0; y < region.Rows; y++)
300 for (
int x = 0; x < region.Columns; x++)
302 CollisionAdjust? collisionAdjust =
303 region.TryGetFrameCollisionAdjustOverride(
306 out var frameCollisionAdjust)
307 ? frameCollisionAdjust
311 region.TryGetFrameCollisionTypeOverride(
314 out var frameCollisionType)
318 definition.Frames.Add(
new TilesheetFrameDefinition
322 CollisionAdjust = collisionAdjust,
323 CollisionType = collisionType
331 private static TilesheetMaskDefinition? CreateMaskDefinition(Tilesheet tilesheet)
333 if (tilesheet.MaskColor is
null)
336 var color = tilesheet.MaskColor.Value;
337 return new TilesheetMaskDefinition
343 Tolerance = tilesheet.MaskTolerance
347 private static void ApplyDefaultSource(
349 TilesheetDefinitionSource source)
352 definition.Source = source;
362 var clone = FromJson(ToJson(definition));
363 clone.Source = TilesheetDefinitionSource.LooseDefinitionFile(fullPath);
367 private static string NormalizePath(
369 string? baseDirectory,
372 if (
string.IsNullOrWhiteSpace(path))
375 if (!makeRelative ||
string.IsNullOrWhiteSpace(baseDirectory))
376 return path.Replace(
'\\',
'/');
378 if (!Path.IsPathRooted(path))
379 return path.Replace(
'\\',
'/');
381 var fullPath = Path.GetFullPath(path);
382 var fullBaseDirectory = Path.GetFullPath(baseDirectory);
384 return Path.GetRelativePath(fullBaseDirectory, fullPath).Replace(
'\\',
'/');
AssetTypes
Defines the types of assets that can be stored and managed by the engine.
@ TilesheetDefinition
Represents a tilesheet definition file (.gts) for Gondwana.Drawing.Tilesheets.GTS....
@ Image
Represents an image file type.
TilesheetDefinitionSourceKind
Indicates where a tilesheet definition came from.
TileCollisionType
Defines the default collision behavior associated with a tilesheet region, frame, or runtime tile.