From fb0ff502f0e722b65d3de98956b1f50797440a0f Mon Sep 17 00:00:00 2001 From: REDCODE Date: Sat, 13 Jun 2026 17:17:58 +0200 Subject: [PATCH] Phase 0.1: Resource extraction pipeline - Add OrderedDictionary JSON converter for round-trip support - Add ObjLoadRaw() for GDI+-free binary resource loading - Simplify Ser.cs JSON methods (consistent settings, JsonConvert API) - Create SlaveMatrix.Extract CLI tool for resource extraction - Manual export walks Obj/Difs/Dif/Pars/Par hierarchy - Exports all 13 Obj resources to structured JSON with shape data - Update .gitignore for build artifacts and extraction output --- .gitignore | 7 +- .../_2DGAMELIB/OrderedDictionaryConverter.cs | 47 ++++ 2DGAMELIB/_2DGAMELIB/Oth.cs | 16 +- 2DGAMELIB/_2DGAMELIB/Ser.cs | 194 +++++++---------- SlaveMatrix.Extract/Program.cs | 201 ++++++++++++++++++ .../SlaveMatrix.Extract.csproj | 19 ++ Solution.sln | 25 +++ 7 files changed, 382 insertions(+), 127 deletions(-) create mode 100644 2DGAMELIB/_2DGAMELIB/OrderedDictionaryConverter.cs create mode 100644 SlaveMatrix.Extract/Program.cs create mode 100644 SlaveMatrix.Extract/SlaveMatrix.Extract.csproj diff --git a/.gitignore b/.gitignore index 47e73b3..ded2c79 100644 --- a/.gitignore +++ b/.gitignore @@ -1,9 +1,8 @@ -2DGAMELIB/obj/ -SlaveMatrix/obj/ -2DGAMELIB/bin/ -SlaveMatrix/bin/ +**/bin/ +**/obj/ .vs/ .idea/ Config.ini game_folder/save/* /game_folder/save +extracted/ diff --git a/2DGAMELIB/_2DGAMELIB/OrderedDictionaryConverter.cs b/2DGAMELIB/_2DGAMELIB/OrderedDictionaryConverter.cs new file mode 100644 index 0000000..879d1ff --- /dev/null +++ b/2DGAMELIB/_2DGAMELIB/OrderedDictionaryConverter.cs @@ -0,0 +1,47 @@ +using System; +using System.Collections.Generic; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; + +namespace _2DGAMELIB +{ + public class OrderedDictionaryConverter : JsonConverter> + where T1 : notnull + { + public override OrderedDictionary ReadJson(JsonReader reader, Type objectType, OrderedDictionary existingValue, bool hasExistingValue, JsonSerializer serializer) + { + var dict = new OrderedDictionary(); + + if (reader.TokenType == JsonToken.Null) + return dict; + + var array = JArray.Load(reader); + + foreach (var item in array) + { + var key = item["Key"].ToObject(serializer); + var value = item["Value"].ToObject(serializer); + dict.Add(key, value); + } + + return dict; + } + + public override void WriteJson(JsonWriter writer, OrderedDictionary value, JsonSerializer serializer) + { + writer.WriteStartArray(); + + foreach (var key in value.Keys) + { + writer.WriteStartObject(); + writer.WritePropertyName("Key"); + serializer.Serialize(writer, key); + writer.WritePropertyName("Value"); + serializer.Serialize(writer, value[key]); + writer.WriteEndObject(); + } + + writer.WriteEndArray(); + } + } +} \ No newline at end of file diff --git a/2DGAMELIB/_2DGAMELIB/Oth.cs b/2DGAMELIB/_2DGAMELIB/Oth.cs index 107a664..047df84 100644 --- a/2DGAMELIB/_2DGAMELIB/Oth.cs +++ b/2DGAMELIB/_2DGAMELIB/Oth.cs @@ -156,11 +156,23 @@ namespace _2DGAMELIB GetMinMaxY(Par, ref MM[1].Y, ref MM[0].Y); } - public static Obj ObjLoad(this byte[] bd) - { +public static Obj ObjLoad(this byte[] bd) + { return bd.Load().ToDeserialObject().SetDefaultR(); } + public static Obj? ObjLoadRaw(this byte[] bd) + { + try + { + return bd.Load().ToDeserialObject(); + } + catch + { + return null; + } + } + public static bool Lot(this double p) { diff --git a/2DGAMELIB/_2DGAMELIB/Ser.cs b/2DGAMELIB/_2DGAMELIB/Ser.cs index f447412..39cf227 100644 --- a/2DGAMELIB/_2DGAMELIB/Ser.cs +++ b/2DGAMELIB/_2DGAMELIB/Ser.cs @@ -1,155 +1,107 @@ using System; +using System.Collections.Generic; using System.IO; +using System.Linq; +using System.Reflection; using System.Runtime.Serialization.Formatters.Binary; -using System.Security.Cryptography; using System.Text; using Newtonsoft.Json; +using Newtonsoft.Json.Serialization; namespace _2DGAMELIB { - - //serialization stuff public static class Ser { - private static SerializableAttribute s = new SerializableAttribute(); + private static SerializableAttribute s = new SerializableAttribute(); - public static T DeepCopy(this T Object) - { - BinaryFormatter binaryFormatter = new BinaryFormatter(); - using MemoryStream memoryStream = new MemoryStream(); - binaryFormatter.Serialize(memoryStream, Object); - memoryStream.Position = 0L; - return (T)binaryFormatter.Deserialize(memoryStream); - } - - public static byte[] ToSerialBytes(this T Object) - { - using MemoryStream memoryStream = new MemoryStream(); - new BinaryFormatter().Serialize(memoryStream, Object); - return memoryStream.ToArray(); - } - - public static T ToDeserialObject(this byte[] Bytes) - { - using MemoryStream serializationStream = new MemoryStream(Bytes); - return (T)new BinaryFormatter().Deserialize(serializationStream); - } - - public static void Save(this T Object, string Path) - { - using FileStream serializationStream = new FileStream(Path, FileMode.Create, FileAccess.Write); - new BinaryFormatter().Serialize(serializationStream, Object); - } - - public static T Load(this string Path) - { - using FileStream serializationStream = new FileStream(Path, FileMode.Open, FileAccess.Read); - return (T)new BinaryFormatter().Deserialize(serializationStream); - } - - public static T Load(this byte[] bd) - { - using MemoryStream serializationStream = new MemoryStream(bd); - return (T)new BinaryFormatter().Deserialize(serializationStream); - } - /* - public static void ToXml(this T Object, string Path) - { - using FileStream output = new FileStream(Path, FileMode.Create, FileAccess.Write); - using XmlWriter writer = XmlWriter.Create(output, new XmlWriterSettings - { - Indent = true - }); - new DataContractSerializer(typeof(T)).WriteObject(writer, Object); - } - - public static T FromXml(this string Path) - { - using FileStream input = new FileStream(Path, FileMode.Open, FileAccess.Read); - using XmlReader reader = XmlReader.Create(input); - return (T)new DataContractSerializer(typeof(T)).ReadObject(reader); - } - */ - static Ser(){} - - public static T JsonDeepCopy(this T Object) + private static JsonSerializerSettings CreateSettings() { - using MemoryStream memoryStream = new MemoryStream(); - - - JsonSerializer jsonSerializer = new JsonSerializer + var settings = new JsonSerializerSettings { PreserveReferencesHandling = PreserveReferencesHandling.All, TypeNameHandling = TypeNameHandling.All, Formatting = Newtonsoft.Json.Formatting.Indented, NullValueHandling = NullValueHandling.Ignore }; + settings.Converters.Add(new OrderedDictionaryConverter()); + settings.Converters.Add(new OrderedDictionaryConverter()); + settings.Converters.Add(new OrderedDictionaryConverter()); + settings.Converters.Add(new OrderedDictionaryConverter()); + return settings; + } - jsonSerializer.Serialize(new StreamWriter(memoryStream, Encoding.UTF8), Object); + public static T DeepCopy(this T Object) + { + BinaryFormatter binaryFormatter = new BinaryFormatter(); + using MemoryStream memoryStream = new MemoryStream(); + binaryFormatter.Serialize(memoryStream, Object); + memoryStream.Position = 0L; + return (T)binaryFormatter.Deserialize(memoryStream); + } + public static byte[] ToSerialBytes(this T Object) + { + using MemoryStream memoryStream = new MemoryStream(); + new BinaryFormatter().Serialize(memoryStream, Object); + return memoryStream.ToArray(); + } - return (T)new JsonSerializer - { - PreserveReferencesHandling = PreserveReferencesHandling.All, - TypeNameHandling = TypeNameHandling.All, - Formatting = Newtonsoft.Json.Formatting.Indented, - NullValueHandling = NullValueHandling.Ignore - }.Deserialize(new StreamReader(memoryStream), typeof(T)); + public static T ToDeserialObject(this byte[] Bytes) + { + using MemoryStream serializationStream = new MemoryStream(Bytes); + return (T)new BinaryFormatter().Deserialize(serializationStream); + } + + public static void Save(this T Object, string Path) + { + using FileStream serializationStream = new FileStream(Path, FileMode.Create, FileAccess.Write); + new BinaryFormatter().Serialize(serializationStream, Object); + } + + public static T Load(this string Path) + { + using FileStream serializationStream = new FileStream(Path, FileMode.Open, FileAccess.Read); + return (T)new BinaryFormatter().Deserialize(serializationStream); + } + + public static T Load(this byte[] bd) + { + using MemoryStream serializationStream = new MemoryStream(bd); + return (T)new BinaryFormatter().Deserialize(serializationStream); + } + + static Ser() { } + + public static T JsonDeepCopy(this T Object) + { + var json = JsonConvert.SerializeObject(Object, CreateSettings()); + return JsonConvert.DeserializeObject(json, CreateSettings()); } public static byte[] ToJsonBytes(this T Object) { - MemoryStream textWriter = new MemoryStream(); - - JsonSerializer jsonSerializer = new JsonSerializer - { - PreserveReferencesHandling = PreserveReferencesHandling.Objects, - TypeNameHandling = TypeNameHandling.All, - Formatting = Newtonsoft.Json.Formatting.Indented, - NullValueHandling = NullValueHandling.Ignore - }; - - jsonSerializer.Serialize(new StreamWriter(textWriter, Encoding.UTF8), Object); - - return textWriter.ToArray(); + var json = JsonConvert.SerializeObject(Object, CreateSettings()); + return Encoding.UTF8.GetBytes(json); } public static T ToUnJsonObject(this byte[] Bytes) { - using StreamReader reader = new StreamReader(new MemoryStream(Bytes), Encoding.UTF8); - return (T)new JsonSerializer - { - PreserveReferencesHandling = PreserveReferencesHandling.All, - TypeNameHandling = TypeNameHandling.All, - Formatting = Newtonsoft.Json.Formatting.Indented, - NullValueHandling = NullValueHandling.Ignore - }.Deserialize(reader, typeof(T)); + var json = Encoding.UTF8.GetString(Bytes); + return JsonConvert.DeserializeObject(json, CreateSettings()); } - public static void ToJson(this T Object, string Path) - { - using StreamWriter textWriter = File.CreateText(Path); - JsonSerializer jsonSerializer = new JsonSerializer - { - PreserveReferencesHandling = PreserveReferencesHandling.All, - TypeNameHandling = TypeNameHandling.All, - Formatting = Newtonsoft.Json.Formatting.Indented, - NullValueHandling = NullValueHandling.Ignore - }; - jsonSerializer.Serialize(textWriter, Object); - } + { + var json = JsonConvert.SerializeObject(Object, CreateSettings()); + File.WriteAllText(Path, json); + } - public static T UnJson(string Path) - { - using StreamReader reader = File.OpenText(Path); - return (T)new JsonSerializer - { - PreserveReferencesHandling = PreserveReferencesHandling.All, - TypeNameHandling = TypeNameHandling.All, - Formatting = Newtonsoft.Json.Formatting.Indented, - NullValueHandling = NullValueHandling.Ignore - }.Deserialize(reader, typeof(T)); - } + public static T UnJson(string Path) + { + var json = File.ReadAllText(Path); + return JsonConvert.DeserializeObject(json, CreateSettings()); + } + + public static JsonSerializerSettings GetSettings() => CreateSettings(); } -} +} \ No newline at end of file diff --git a/SlaveMatrix.Extract/Program.cs b/SlaveMatrix.Extract/Program.cs new file mode 100644 index 0000000..cd61325 --- /dev/null +++ b/SlaveMatrix.Extract/Program.cs @@ -0,0 +1,201 @@ +using _2DGAMELIB; +using SlaveMatrix.Properties; +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; + +namespace SlaveMatrix.Extract; + +class Program +{ + static readonly string OutputDir = Path.Combine(Directory.GetCurrentDirectory(), "extracted"); + + static void Main(string[] args) + { + Console.WriteLine("SlaveMatrix Resource Extractor"); + Console.WriteLine("=================================\n"); + + Directory.CreateDirectory(OutputDir); + + var resources = new (string Name, byte[] Data)[] + { + ("胴体", Resources.胴体), + ("肩左", Resources.肩左), + ("腕左", Resources.腕左), + ("脚左", Resources.脚左), + ("尻尾", Resources.尻尾), + ("半身", Resources.半身), + ("肢左", Resources.肢左), + ("肢中", Resources.肢中), + ("性器", Resources.性器), + ("性器付", Resources.性器付), + ("スタンプ", Resources.スタンプ), + ("カーソル", Resources.カーソル), + ("その他", Resources.その他), + }; + + foreach (var (name, data) in resources) + { + Console.Write($"Loading {name}... "); + Obj? obj; + try + { + obj = data.ObjLoadRaw(); + if (obj == null) { Console.WriteLine("FAILED: null"); continue; } + Console.WriteLine($"OK ({obj.Difss.Count} keys)"); + } + catch (Exception ex) { Console.WriteLine($"FAILED: {ex.Message}"); continue; } + + Console.Write(" Applying MigrateKeys... "); + obj.MigrateKeys(); + Console.WriteLine("OK"); + + var objDir = Path.Combine(OutputDir, name); + Directory.CreateDirectory(objDir); + + Console.Write(" Exporting to JSON... "); + try + { + var jobj = ExportObj(obj); + var json = JsonConvert.SerializeObject(jobj, Formatting.Indented); + File.WriteAllText(Path.Combine(objDir, $"{name}.json"), json); + Console.WriteLine($"OK ({json.Length / 1024}KB)"); + } + catch (Exception ex) { Console.WriteLine($"FAILED: {ex.Message}"); } + + Console.Write(" Validating... "); + var keyCount = obj.Difss.Keys.Cast().Count(); + File.WriteAllLines(Path.Combine(objDir, $"{name}_keys.txt"), obj.Difss.Keys.Cast()); + Console.WriteLine($"OK ({keyCount} keys)"); + } + + Console.WriteLine("\nExtraction complete."); + } + + static JObject ExportObj(Obj obj) + { + var keys = obj.Difss.Keys.Cast().ToList(); + var result = new JObject + { + ["Tag"] = obj.Tag ?? "", + ["KeyCount"] = keys.Count, + ["Keys"] = JArray.FromObject(keys), + ["Difss"] = new JObject() + }; + + foreach (var key in keys) + { + result["Difss"][key] = ExportDifs(obj.Difss[key]); + } + + return result; + } + + static JObject ExportDifs(Difs difs) + { + int xCount = difs.CountX; + int yCount = difs.CountY; + + var result = new JObject + { + ["Tag"] = difs.Tag ?? "", + ["ValueX"] = difs.ValueX, + ["ValueY"] = difs.ValueY, + ["CountX"] = xCount, + ["CountY"] = yCount, + ["Difs"] = new JArray() + }; + + for (int x = 0; x < xCount; x++) + { + var dif = difs[x]; + var difArr = new JArray(); + + for (int y = 0; y < dif.Count; y++) + { + difArr.Add(ExportPars(dif[y])); + } + + ((JArray)result["Difs"]).Add(difArr); + } + + return result; + } + + static JObject ExportPars(Pars pars) + { + var result = new JObject + { + ["Tag"] = pars.Tag ?? "", + ["Children"] = new JObject() + }; + + foreach (string key in pars.Keys) + { + var val = pars[key]; + if (val is Pars childPars) + result["Children"][key] = ExportPars(childPars); + else if (val is ParT parT) + result["Children"][key] = ExportParT(parT); + else if (val is Par par) + result["Children"][key] = ExportPar(par); + else + result["Children"][key] = val?.ToString() ?? "null"; + } + + return result; + } + + static JObject ExportPar(Par par) + { + var result = new JObject + { + ["Tag"] = par.Tag ?? "", + ["Dra"] = par.Dra, + ["PenWidth"] = par.PenWidth, + ["Closed"] = par.Closed, + ["BasePoint"] = ExportVec(par.BasePoint), + ["Position"] = ExportVec(par.Position), + ["Angle"] = par.Angle, + ["Size"] = par.Size, + ["SizeX"] = par.SizeX, + ["SizeY"] = par.SizeY, + ["Out"] = new JArray() + }; + + foreach (var outObj in par.OP) + { + var outJ = new JObject + { + ["Tension"] = outObj.Tension, + ["Outline"] = outObj.Outline, + ["Points"] = new JArray() + }; + foreach (var pt in outObj.ps) + ((JArray)outJ["Points"]).Add(ExportVec(pt)); + ((JArray)result["Out"]).Add(outJ); + } + + if (par.JP.Count > 0) + { + result["Joints"] = new JArray(); + foreach (var joi in par.JP) + ((JArray)result["Joints"]).Add(ExportVec(joi.Joint)); + } + + return result; + } + + static JObject ExportParT(ParT parT) + { + var result = ExportPar(parT); + result["Text"] = parT.Text ?? ""; + result["FontSize"] = parT.FontSize; + return result; + } + + static JObject ExportVec(Vector2D v) => new JObject { ["X"] = v.X, ["Y"] = v.Y }; +} \ No newline at end of file diff --git a/SlaveMatrix.Extract/SlaveMatrix.Extract.csproj b/SlaveMatrix.Extract/SlaveMatrix.Extract.csproj new file mode 100644 index 0000000..e835999 --- /dev/null +++ b/SlaveMatrix.Extract/SlaveMatrix.Extract.csproj @@ -0,0 +1,19 @@ + + + + Exe + net8.0 + enable + enable + True + true + true + false + SYSLIB0011;CA1416 + + + + + + + \ No newline at end of file diff --git a/Solution.sln b/Solution.sln index bc0e129..e298a31 100644 --- a/Solution.sln +++ b/Solution.sln @@ -1,3 +1,4 @@ + Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio Version 17 VisualStudioVersion = 17.11.35222.181 @@ -6,30 +7,54 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SlaveMatrix", "SlaveMatrix\ EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "2DGAMELIB", "2DGAMELIB\2DGAMELIB.csproj", "{A12B2957-8EC4-4DA7-AD38-4FE141C532E1}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SlaveMatrix.Extract", "SlaveMatrix.Extract\SlaveMatrix.Extract.csproj", "{DAEAB23A-E099-432E-9C85-39D0853BD78A}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|x64 = Debug|x64 Debug|x86 = Debug|x86 + Debug|Any CPU = Debug|Any CPU Release|x64 = Release|x64 Release|x86 = Release|x86 + Release|Any CPU = Release|Any CPU EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {627ABCB0-698F-4FF9-A85D-1E99EBCACF46}.Debug|x64.ActiveCfg = Debug|Any CPU {627ABCB0-698F-4FF9-A85D-1E99EBCACF46}.Debug|x64.Build.0 = Debug|Any CPU {627ABCB0-698F-4FF9-A85D-1E99EBCACF46}.Debug|x86.ActiveCfg = Debug|Any CPU {627ABCB0-698F-4FF9-A85D-1E99EBCACF46}.Debug|x86.Build.0 = Debug|Any CPU + {627ABCB0-698F-4FF9-A85D-1E99EBCACF46}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {627ABCB0-698F-4FF9-A85D-1E99EBCACF46}.Debug|Any CPU.Build.0 = Debug|Any CPU {627ABCB0-698F-4FF9-A85D-1E99EBCACF46}.Release|x64.ActiveCfg = Release|Any CPU {627ABCB0-698F-4FF9-A85D-1E99EBCACF46}.Release|x64.Build.0 = Release|Any CPU {627ABCB0-698F-4FF9-A85D-1E99EBCACF46}.Release|x86.ActiveCfg = Release|Any CPU {627ABCB0-698F-4FF9-A85D-1E99EBCACF46}.Release|x86.Build.0 = Release|Any CPU + {627ABCB0-698F-4FF9-A85D-1E99EBCACF46}.Release|Any CPU.ActiveCfg = Release|Any CPU + {627ABCB0-698F-4FF9-A85D-1E99EBCACF46}.Release|Any CPU.Build.0 = Release|Any CPU {A12B2957-8EC4-4DA7-AD38-4FE141C532E1}.Debug|x64.ActiveCfg = Debug|Any CPU {A12B2957-8EC4-4DA7-AD38-4FE141C532E1}.Debug|x64.Build.0 = Debug|Any CPU {A12B2957-8EC4-4DA7-AD38-4FE141C532E1}.Debug|x86.ActiveCfg = Debug|Any CPU {A12B2957-8EC4-4DA7-AD38-4FE141C532E1}.Debug|x86.Build.0 = Debug|Any CPU + {A12B2957-8EC4-4DA7-AD38-4FE141C532E1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {A12B2957-8EC4-4DA7-AD38-4FE141C532E1}.Debug|Any CPU.Build.0 = Debug|Any CPU {A12B2957-8EC4-4DA7-AD38-4FE141C532E1}.Release|x64.ActiveCfg = Release|Any CPU {A12B2957-8EC4-4DA7-AD38-4FE141C532E1}.Release|x64.Build.0 = Release|Any CPU {A12B2957-8EC4-4DA7-AD38-4FE141C532E1}.Release|x86.ActiveCfg = Release|Any CPU {A12B2957-8EC4-4DA7-AD38-4FE141C532E1}.Release|x86.Build.0 = Release|Any CPU + {A12B2957-8EC4-4DA7-AD38-4FE141C532E1}.Release|Any CPU.ActiveCfg = Release|Any CPU + {A12B2957-8EC4-4DA7-AD38-4FE141C532E1}.Release|Any CPU.Build.0 = Release|Any CPU + {DAEAB23A-E099-432E-9C85-39D0853BD78A}.Debug|x64.ActiveCfg = Debug|Any CPU + {DAEAB23A-E099-432E-9C85-39D0853BD78A}.Debug|x64.Build.0 = Debug|Any CPU + {DAEAB23A-E099-432E-9C85-39D0853BD78A}.Debug|x86.ActiveCfg = Debug|Any CPU + {DAEAB23A-E099-432E-9C85-39D0853BD78A}.Debug|x86.Build.0 = Debug|Any CPU + {DAEAB23A-E099-432E-9C85-39D0853BD78A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {DAEAB23A-E099-432E-9C85-39D0853BD78A}.Debug|Any CPU.Build.0 = Debug|Any CPU + {DAEAB23A-E099-432E-9C85-39D0853BD78A}.Release|x64.ActiveCfg = Release|Any CPU + {DAEAB23A-E099-432E-9C85-39D0853BD78A}.Release|x64.Build.0 = Release|Any CPU + {DAEAB23A-E099-432E-9C85-39D0853BD78A}.Release|x86.ActiveCfg = Release|Any CPU + {DAEAB23A-E099-432E-9C85-39D0853BD78A}.Release|x86.Build.0 = Release|Any CPU + {DAEAB23A-E099-432E-9C85-39D0853BD78A}.Release|Any CPU.ActiveCfg = Release|Any CPU + {DAEAB23A-E099-432E-9C85-39D0853BD78A}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE