using System; using System.Collections.Generic; using System.IO; using System.IO.Compression; using System.Linq; namespace DotTiled.Serialization; internal static partial class Helpers { internal static Func CreateMapper(Func noMatch, params (string, T)[] mappings) { return s => { foreach (var (key, value) in mappings) { if (key == s) return value; } throw noMatch(s); }; } internal static uint[] ReadMemoryStreamAsInt32Array(Stream stream) { var finalValues = new List(); var int32Bytes = new byte[4]; while (stream.Read(int32Bytes, 0, 4) == 4) { var value = BitConverter.ToUInt32(int32Bytes, 0); finalValues.Add(value); } return [.. finalValues]; } internal static uint[] DecompressGZip(MemoryStream stream) { using var decompressedStream = new GZipStream(stream, CompressionMode.Decompress); return ReadMemoryStreamAsInt32Array(decompressedStream); } internal static uint[] DecompressZLib(MemoryStream stream) { using var decompressedStream = new ZLibStream(stream, CompressionMode.Decompress); return ReadMemoryStreamAsInt32Array(decompressedStream); } internal static uint[] ReadBytesAsInt32Array(byte[] bytes) { var intArray = new uint[bytes.Length / 4]; for (var i = 0; i < intArray.Length; i++) { intArray[i] = BitConverter.ToUInt32(bytes, i * 4); } return intArray; } internal static (uint[] GlobalTileIDs, FlippingFlags[] FlippingFlags) ReadAndClearFlippingFlagsFromGIDs(uint[] globalTileIDs) { var clearedGlobalTileIDs = new uint[globalTileIDs.Length]; var flippingFlags = new FlippingFlags[globalTileIDs.Length]; for (var i = 0; i < globalTileIDs.Length; i++) { var gid = globalTileIDs[i]; var flags = gid & 0xF0000000u; flippingFlags[i] = (FlippingFlags)flags; clearedGlobalTileIDs[i] = gid & 0x0FFFFFFFu; } return (clearedGlobalTileIDs, flippingFlags); } internal static ImageFormat ParseImageFormatFromSource(string source) { var extension = Path.GetExtension(source).ToLowerInvariant(); return extension switch { ".png" => ImageFormat.Png, ".gif" => ImageFormat.Gif, ".jpg" => ImageFormat.Jpg, ".jpeg" => ImageFormat.Jpg, ".bmp" => ImageFormat.Bmp, _ => throw new NotSupportedException($"Unsupported image format '{extension}'") }; } internal static List ResolveClassProperties(string className, Func> customTypeResolver) { if (string.IsNullOrWhiteSpace(className)) return null; var customType = customTypeResolver(className) ?? throw new InvalidOperationException($"Could not resolve custom type '{className}'."); if (!customType.HasValue) return null; if (customType.Value is not CustomClassDefinition ccd) throw new InvalidOperationException($"Custom type '{className}' is not a class."); return CreateInstanceOfCustomClass(ccd, customTypeResolver); } internal static List CreateInstanceOfCustomClass( CustomClassDefinition customClassDefinition, Func> customTypeResolver) { return customClassDefinition.Members.Select(x => { if (x is ClassProperty cp) { var resolvedType = customTypeResolver(cp.PropertyType); if (!resolvedType.HasValue) { return new ClassProperty { Name = cp.Name, PropertyType = cp.PropertyType, Value = [] }; } if (resolvedType.Value is not CustomClassDefinition ccd) throw new InvalidOperationException($"Custom type '{cp.PropertyType}' is not a class."); return new ClassProperty { Name = cp.Name, PropertyType = cp.PropertyType, Value = CreateInstanceOfCustomClass(ccd, customTypeResolver) }; } return x.Clone(); }).ToList(); } internal static IList MergeProperties(IList baseProperties, IList overrideProperties) { if (baseProperties is null) return overrideProperties ?? []; if (overrideProperties is null) return baseProperties; var result = baseProperties.Select(x => x.Clone()).ToList(); foreach (var overrideProp in overrideProperties) { if (!result.Any(x => x.Name == overrideProp.Name)) { result.Add(overrideProp); continue; } else { var existingProp = result.First(x => x.Name == overrideProp.Name); if (existingProp is ClassProperty classProp) { classProp.Value = MergeProperties(classProp.Value, ((ClassProperty)overrideProp).Value); } else { ReplacePropertyInList(result, overrideProp); } } } return result; } internal static void ReplacePropertyInList(List properties, IProperty property) { var index = properties.FindIndex(p => p.Name == property.Name); if (index == -1) properties.Add(property); else properties[index] = property; } internal static void SetAtMostOnce(ref T field, T value, string fieldName) { if (field is not null) throw new InvalidOperationException($"{fieldName} already set"); field = value; } internal static void SetAtMostOnceUsingCounter(ref T field, T value, string fieldName, ref int counter) { if (counter > 0) throw new InvalidOperationException($"{fieldName} already set"); field = value; counter++; } internal static bool StringIsXml(string s) => s.StartsWith("