public static int SplitDLLFile(string datafilename, string inifilename, string projectFolderName) { try { byte[] datafile = File.ReadAllBytes(datafilename); IniData inifile = IniSerializer.Deserialize <IniData>(inifilename); uint imageBase = HelperFunctions.SetupEXE(ref datafile).Value; Dictionary <string, int> exports; { int ptr = BitConverter.ToInt32(datafile, BitConverter.ToInt32(datafile, 0x3c) + 4 + 20 + 96); GCHandle handle = GCHandle.Alloc(datafile, GCHandleType.Pinned); IMAGE_EXPORT_DIRECTORY dir = (IMAGE_EXPORT_DIRECTORY)Marshal.PtrToStructure( Marshal.UnsafeAddrOfPinnedArrayElement(datafile, ptr), typeof(IMAGE_EXPORT_DIRECTORY)); handle.Free(); exports = new Dictionary <string, int>(dir.NumberOfFunctions); int nameaddr = dir.AddressOfNames; int ordaddr = dir.AddressOfNameOrdinals; for (int i = 0; i < dir.NumberOfNames; i++) { string name = datafile.GetCString(BitConverter.ToInt32(datafile, nameaddr), System.Text.Encoding.ASCII); int addr = BitConverter.ToInt32(datafile, dir.AddressOfFunctions + (BitConverter.ToInt16(datafile, ordaddr) * 4)); exports.Add(name, addr); nameaddr += 4; ordaddr += 2; } } ModelFormat modelfmt = 0; LandTableFormat landfmt = 0; string modelext = null; string landext = null; switch (inifile.Game) { case Game.SADX: modelfmt = ModelFormat.BasicDX; landfmt = LandTableFormat.SADX; modelext = ".sa1mdl"; landext = ".sa1lvl"; break; case Game.SA2B: modelfmt = ModelFormat.Chunk; landfmt = LandTableFormat.SA2; modelext = ".sa2mdl"; landext = ".sa2lvl"; break; } int itemcount = 0; List <string> labels = new List <string>(); ModelAnimationsDictionary models = new ModelAnimationsDictionary(); DllIniData output = new DllIniData() { Name = inifile.ModuleName, Game = inifile.Game }; Stopwatch timer = new Stopwatch(); timer.Start(); foreach (KeyValuePair <string, FileInfo> item in inifile.Files) { if (string.IsNullOrEmpty(item.Key)) { continue; } FileInfo data = item.Value; string type = data.Type; string name = item.Key; output.Exports[name] = type; int address = exports[name]; string fileOutputPath = ""; if (data.Filename != null) { fileOutputPath = string.Concat(projectFolderName, data.Filename); Console.WriteLine(name + " -> " + fileOutputPath); Directory.CreateDirectory(Path.GetDirectoryName(fileOutputPath)); } else { Console.WriteLine(name); } switch (type) { case "landtable": { LandTable land = new LandTable(datafile, address, imageBase, landfmt) { Description = name }; DllItemInfo info = new DllItemInfo() { Export = name, Label = land.Name }; output.Items.Add(info); if (!labels.Contains(land.Name)) { land.SaveToFile(fileOutputPath, landfmt); output.Files[data.Filename] = new FileTypeHash("landtable", HelperFunctions.FileHash(fileOutputPath)); labels.AddRange(land.GetLabels()); } } break; case "landtablearray": for (int i = 0; i < data.Length; i++) { int ptr = BitConverter.ToInt32(datafile, address); if (ptr != 0) { ptr = (int)(ptr - imageBase); string idx = name + "[" + i.ToString(NumberFormatInfo.InvariantInfo) + "]"; LandTable land = new LandTable(datafile, ptr, imageBase, landfmt) { Description = idx }; DllItemInfo info = new DllItemInfo() { Export = name, Index = i, Label = land.Name }; output.Items.Add(info); if (!labels.Contains(land.Name)) { string outputFN = Path.Combine(fileOutputPath, i.ToString(NumberFormatInfo.InvariantInfo) + landext); string fileName = Path.Combine(data.Filename, i.ToString(NumberFormatInfo.InvariantInfo) + landext); land.SaveToFile(outputFN, landfmt); output.Files[fileName] = new FileTypeHash("landtable", HelperFunctions.FileHash(outputFN)); labels.AddRange(land.GetLabels()); } } address += 4; } break; case "model": { NJS_OBJECT mdl = new NJS_OBJECT(datafile, address, imageBase, modelfmt); DllItemInfo info = new DllItemInfo() { Export = name, Label = mdl.Name }; output.Items.Add(info); if (!labels.Contains(mdl.Name)) { models.Add(new ModelAnimations(data.Filename, name, mdl, modelfmt)); labels.AddRange(mdl.GetLabels()); } } break; case "morph": { BasicAttach dummy = new BasicAttach(datafile, address, imageBase, modelfmt == ModelFormat.BasicDX); NJS_OBJECT mdl = new NJS_OBJECT() { Attach = dummy }; DllItemInfo info = new DllItemInfo() { Export = name, Label = dummy.Name }; output.Items.Add(info); if (!labels.Contains(dummy.Name)) { models.Add(new ModelAnimations(data.Filename, name, mdl, modelfmt)); labels.AddRange(mdl.GetLabels()); } } break; case "modelarray": for (int i = 0; i < data.Length; i++) { int ptr = BitConverter.ToInt32(datafile, address); if (ptr != 0) { ptr = (int)(ptr - imageBase); NJS_OBJECT mdl = new NJS_OBJECT(datafile, ptr, imageBase, modelfmt); string idx = name + "[" + i.ToString(NumberFormatInfo.InvariantInfo) + "]"; DllItemInfo info = new DllItemInfo() { Export = name, Index = i, Label = mdl.Name }; output.Items.Add(info); if (!labels.Contains(mdl.Name)) { string fn = Path.Combine(data.Filename, i.ToString(NumberFormatInfo.InvariantInfo) + modelext); models.Add(new ModelAnimations(fn, idx, mdl, modelfmt)); labels.AddRange(mdl.GetLabels()); } } address += 4; } break; case "modelsarray": for (int i = 0; i < data.Length; i++) { int ptr = BitConverter.ToInt32(datafile, address); if (ptr != 0) { ptr = (int)(ptr - imageBase); BasicAttach dummy = new BasicAttach(datafile, ptr, imageBase, modelfmt == ModelFormat.BasicDX); NJS_OBJECT mdl = new NJS_OBJECT() { Attach = dummy }; string idx = name + "[" + i.ToString(NumberFormatInfo.InvariantInfo) + "]"; DllItemInfo info = new DllItemInfo() { Export = name, Index = i, Label = dummy.Name }; output.Items.Add(info); if (!labels.Contains(dummy.Name)) { string fn = Path.Combine(data.Filename, i.ToString(NumberFormatInfo.InvariantInfo) + modelext); models.Add(new ModelAnimations(fn, idx, mdl, ModelFormat.BasicDX)); labels.AddRange(mdl.GetLabels()); } } address += 4; } break; case "basicmodel": { NJS_OBJECT mdl = new NJS_OBJECT(datafile, address, imageBase, ModelFormat.Basic); DllItemInfo info = new DllItemInfo() { Export = name, Label = mdl.Name }; output.Items.Add(info); if (!labels.Contains(mdl.Name)) { models.Add(new ModelAnimations(data.Filename, name, mdl, ModelFormat.Basic)); labels.AddRange(mdl.GetLabels()); } } break; case "basicmodelarray": for (int i = 0; i < data.Length; i++) { int ptr = BitConverter.ToInt32(datafile, address); if (ptr != 0) { ptr = (int)(ptr - imageBase); NJS_OBJECT mdl = new NJS_OBJECT(datafile, ptr, imageBase, ModelFormat.Basic); string idx = name + "[" + i.ToString(NumberFormatInfo.InvariantInfo) + "]"; DllItemInfo info = new DllItemInfo() { Export = name, Index = i, Label = mdl.Name }; output.Items.Add(info); if (!labels.Contains(mdl.Name)) { string fn = Path.Combine(data.Filename, i.ToString(NumberFormatInfo.InvariantInfo) + ".sa1mdl"); models.Add(new ModelAnimations(fn, idx, mdl, ModelFormat.Basic)); labels.AddRange(mdl.GetLabels()); } } address += 4; } break; case "basicdxmodel": { NJS_OBJECT mdl = new NJS_OBJECT(datafile, address, imageBase, ModelFormat.BasicDX); DllItemInfo info = new DllItemInfo() { Export = name, Label = mdl.Name }; output.Items.Add(info); if (!labels.Contains(mdl.Name)) { models.Add(new ModelAnimations(data.Filename, name, mdl, ModelFormat.BasicDX)); labels.AddRange(mdl.GetLabels()); } } break; case "basicdxmodelarray": for (int i = 0; i < data.Length; i++) { int ptr = BitConverter.ToInt32(datafile, address); if (ptr != 0) { ptr = (int)(ptr - imageBase); NJS_OBJECT mdl = new NJS_OBJECT(datafile, ptr, imageBase, ModelFormat.BasicDX); string idx = name + "[" + i.ToString(NumberFormatInfo.InvariantInfo) + "]"; DllItemInfo info = new DllItemInfo() { Export = name, Index = i, Label = mdl.Name }; output.Items.Add(info); if (!labels.Contains(mdl.Name)) { string fn = Path.Combine(data.Filename, i.ToString(NumberFormatInfo.InvariantInfo) + ".sa1mdl"); models.Add(new ModelAnimations(fn, idx, mdl, ModelFormat.BasicDX)); labels.AddRange(mdl.GetLabels()); } } address += 4; } break; case "chunkmodel": { NJS_OBJECT mdl = new NJS_OBJECT(datafile, address, imageBase, ModelFormat.Chunk); DllItemInfo info = new DllItemInfo() { Export = name, Label = mdl.Name }; output.Items.Add(info); if (!labels.Contains(mdl.Name)) { models.Add(new ModelAnimations(data.Filename, name, mdl, ModelFormat.Chunk)); labels.AddRange(mdl.GetLabels()); } } break; case "chunkmodelarray": for (int i = 0; i < data.Length; i++) { int ptr = BitConverter.ToInt32(datafile, address); if (ptr != 0) { ptr = (int)(ptr - imageBase); NJS_OBJECT mdl = new NJS_OBJECT(datafile, ptr, imageBase, ModelFormat.Chunk); string idx = name + "[" + i.ToString(NumberFormatInfo.InvariantInfo) + "]"; DllItemInfo info = new DllItemInfo() { Export = name, Index = i, Label = mdl.Name }; output.Items.Add(info); if (!labels.Contains(mdl.Name)) { string fn = Path.Combine(data.Filename, i.ToString(NumberFormatInfo.InvariantInfo) + ".sa2mdl"); models.Add(new ModelAnimations(fn, idx, mdl, ModelFormat.Chunk)); labels.AddRange(mdl.GetLabels()); } } address += 4; } break; case "actionarray": for (int i = 0; i < data.Length; i++) { int ptr = BitConverter.ToInt32(datafile, address); if (ptr != 0) { ptr = (int)(ptr - imageBase); NJS_ACTION ani = new NJS_ACTION(datafile, ptr, imageBase, modelfmt); string idx = name + "[" + i.ToString(NumberFormatInfo.InvariantInfo) + "]"; ani.Animation.Name = item.Key + "_" + i; DllItemInfo info = new DllItemInfo() { Export = name, Index = i, Label = ani.Animation.Name, Field = "motion" }; output.Items.Add(info); info = new DllItemInfo() { Export = name, Index = i, Label = ani.Model.Name, Field = "object" }; output.Items.Add(info); string outputFN = Path.Combine(fileOutputPath, i.ToString(NumberFormatInfo.InvariantInfo) + ".saanim"); string fn = Path.Combine(data.Filename, i.ToString(NumberFormatInfo.InvariantInfo) + ".saanim"); ani.Animation.Save(outputFN); output.Files[fn] = new FileTypeHash("animation", HelperFunctions.FileHash(outputFN)); if (models.Contains(ani.Model.Name)) { ModelAnimations mdl = models[ani.Model.Name]; System.Text.StringBuilder sb = new System.Text.StringBuilder(260); PathRelativePathTo(sb, Path.GetFullPath(Path.Combine(projectFolderName, mdl.Filename)), 0, Path.GetFullPath(outputFN), 0); mdl.Animations.Add(sb.ToString()); // this is where the problem is } else { string mfn = Path.ChangeExtension(fn, modelext); string outputmfn = Path.Combine(projectFolderName, mfn); string animationName = Path.GetFileName(outputFN); ModelFile.CreateFile(outputmfn, ani.Model, new[] { animationName }, null, null, idx + "->object", null, modelfmt); output.Files[mfn] = new FileTypeHash("model", HelperFunctions.FileHash(outputmfn)); } } address += 4; } break; case "texlist": if (output.TexLists == null) { output.TexLists = new TexListContainer(); } output.TexLists.Add((uint)(address + imageBase), new DllTexListInfo(name, null)); break; case "texlistarray": if (output.TexLists == null) { output.TexLists = new TexListContainer(); } for (int i = 0; i < data.Length; i++) { uint ptr = BitConverter.ToUInt32(datafile, address); if (ptr != 0 && !output.TexLists.ContainsKey(ptr)) { output.TexLists.Add(ptr, new DllTexListInfo(name, i)); } address += 4; } break; case "animindexlist": { int c = 0; int i = ByteConverter.ToInt16(datafile, address); while (i != -1) { string outputFN = Path.Combine(fileOutputPath, i.ToString(NumberFormatInfo.InvariantInfo) + ".saanim"); string fileName = Path.Combine(data.Filename, i.ToString(NumberFormatInfo.InvariantInfo) + ".saanim"); NJS_MOTION anim = new NJS_MOTION(datafile, datafile.GetPointer(address + 4, imageBase), imageBase, ByteConverter.ToInt16(datafile, address + 2)); DllItemInfo info = new DllItemInfo() { Export = name, Index = c, Field = "Animation", Label = anim.Name }; anim.Save(outputFN); output.Files[fileName] = new FileTypeHash("animindex", HelperFunctions.FileHash(outputFN)); address += 8; i = ByteConverter.ToInt16(datafile, address); ++c; } } break; } itemcount++; } foreach (ModelAnimations item in models) { string modelOutputPath = string.Concat(projectFolderName, item.Filename); //string modelOutputPath = item.Filename; ModelFile.CreateFile(modelOutputPath, item.Model, item.Animations.ToArray(), null, null, item.Name, null, item.Format); string type = "model"; switch (item.Format) { case ModelFormat.Basic: type = "basicmodel"; break; case ModelFormat.BasicDX: type = "basicdxmodel"; break; case ModelFormat.Chunk: type = "chunkmodel"; break; } output.Files[item.Filename] = new FileTypeHash(type, HelperFunctions.FileHash(modelOutputPath)); } IniSerializer.Serialize(output, Path.Combine(projectFolderName, Path.GetFileNameWithoutExtension(datafilename)) + "_data.ini"); timer.Stop(); Console.WriteLine("Split " + itemcount + " items in " + timer.Elapsed.TotalSeconds + " seconds."); Console.WriteLine(); } catch (Exception e) { Console.WriteLine(e.Message); Console.WriteLine(e.StackTrace); Console.WriteLine("Press any key to exit."); Console.ReadLine(); return((int)ERRORVALUE.UnhandledException); } return((int)ERRORVALUE.Success); }
public static int SplitDLLFile(string datafilename, string inifilename, string projectFolderName) { #if !DEBUG try #endif { byte[] datafile = File.ReadAllBytes(datafilename); IniData inifile = IniSerializer.Deserialize <IniData>(inifilename); uint imageBase = HelperFunctions.SetupEXE(ref datafile).Value; Dictionary <string, int> exports; { int ptr = BitConverter.ToInt32(datafile, BitConverter.ToInt32(datafile, 0x3c) + 4 + 20 + 96); GCHandle handle = GCHandle.Alloc(datafile, GCHandleType.Pinned); IMAGE_EXPORT_DIRECTORY dir = (IMAGE_EXPORT_DIRECTORY)Marshal.PtrToStructure( Marshal.UnsafeAddrOfPinnedArrayElement(datafile, ptr), typeof(IMAGE_EXPORT_DIRECTORY)); handle.Free(); exports = new Dictionary <string, int>(dir.NumberOfFunctions); int nameaddr = dir.AddressOfNames; int ordaddr = dir.AddressOfNameOrdinals; for (int i = 0; i < dir.NumberOfNames; i++) { string name = datafile.GetCString(BitConverter.ToInt32(datafile, nameaddr), System.Text.Encoding.ASCII); int addr = BitConverter.ToInt32(datafile, dir.AddressOfFunctions + (BitConverter.ToInt16(datafile, ordaddr) * 4)); exports.Add(name, addr); nameaddr += 4; ordaddr += 2; } } ModelFormat modelfmt = 0; LandTableFormat landfmt = 0; string modelext = null; string landext = null; switch (inifile.Game) { case Game.SADX: modelfmt = ModelFormat.BasicDX; landfmt = LandTableFormat.SADX; modelext = ".sa1mdl"; landext = ".sa1lvl"; break; case Game.SA2B: modelfmt = ModelFormat.Chunk; landfmt = LandTableFormat.SA2; modelext = ".sa2mdl"; landext = ".sa2lvl"; break; } int itemcount = 0; List <string> labels = new List <string>(); Dictionary <string, string> anilabels = new Dictionary <string, string>(); ModelAnimationsDictionary models = new ModelAnimationsDictionary(); DllIniData output = new DllIniData() { Name = inifile.ModuleName, Game = inifile.Game }; Stopwatch timer = new Stopwatch(); timer.Start(); foreach (KeyValuePair <string, FileInfo> item in inifile.Files) { if (string.IsNullOrEmpty(item.Key)) { continue; } FileInfo data = item.Value; string type = data.Type; string name = item.Key; output.Exports[name] = type; int address = exports[name]; string fileOutputPath = ""; if (data.Filename != null) { fileOutputPath = string.Concat(projectFolderName, data.Filename); Console.WriteLine(name + " -> " + fileOutputPath); Directory.CreateDirectory(Path.GetDirectoryName(fileOutputPath)); } else { Console.WriteLine(name); } switch (type) { case "landtable": { LandTable land = new LandTable(datafile, address, imageBase, landfmt) { Description = name }; DllItemInfo info = new DllItemInfo() { Export = name, Label = land.Name }; output.Items.Add(info); if (!labels.Contains(land.Name)) { land.SaveToFile(fileOutputPath, landfmt); output.Files[data.Filename] = new FileTypeHash("landtable", HelperFunctions.FileHash(fileOutputPath)); labels.AddRange(land.GetLabels()); } } break; case "battlelandtable": { LandTable land = new LandTable(datafile, address, imageBase, LandTableFormat.SA2B) { Description = name }; DllItemInfo info = new DllItemInfo() { Export = name, Label = land.Name }; output.Items.Add(info); if (!labels.Contains(land.Name)) { land.SaveToFile(fileOutputPath, LandTableFormat.SA2B); output.Files[data.Filename] = new FileTypeHash("landtable", HelperFunctions.FileHash(fileOutputPath)); labels.AddRange(land.GetLabels()); } } break; case "landtablearray": for (int i = 0; i < data.Length; i++) { int ptr = BitConverter.ToInt32(datafile, address); if (ptr != 0) { ptr = (int)(ptr - imageBase); string idx = name + "[" + i.ToString(NumberFormatInfo.InvariantInfo) + "]"; LandTable land = new LandTable(datafile, ptr, imageBase, landfmt) { Description = idx }; DllItemInfo info = new DllItemInfo() { Export = name, Index = i, Label = land.Name }; output.Items.Add(info); if (!labels.Contains(land.Name)) { string outputFN = Path.Combine(fileOutputPath, i.ToString(NumberFormatInfo.InvariantInfo) + landext); string fileName = Path.Combine(data.Filename, i.ToString(NumberFormatInfo.InvariantInfo) + landext); land.SaveToFile(outputFN, landfmt); output.Files[fileName] = new FileTypeHash("landtable", HelperFunctions.FileHash(outputFN)); labels.AddRange(land.GetLabels()); } } address += 4; } break; case "model": { NJS_OBJECT mdl = new NJS_OBJECT(datafile, address, imageBase, modelfmt, new Dictionary <int, Attach>()); DllItemInfo info = new DllItemInfo() { Export = name, Label = mdl.Name }; output.Items.Add(info); if (!labels.Contains(mdl.Name)) { models.Add(new ModelAnimations(data.Filename, name, mdl, modelfmt)); labels.AddRange(mdl.GetLabels()); } } break; case "morph": { BasicAttach dummy = new BasicAttach(datafile, address, imageBase, modelfmt == ModelFormat.BasicDX); NJS_OBJECT mdl = new NJS_OBJECT() { Attach = dummy }; DllItemInfo info = new DllItemInfo() { Export = name, Label = dummy.Name }; output.Items.Add(info); if (!labels.Contains(dummy.Name)) { models.Add(new ModelAnimations(data.Filename, name, mdl, modelfmt)); labels.AddRange(mdl.GetLabels()); } } break; case "modelarray": for (int i = 0; i < data.Length; i++) { int ptr = BitConverter.ToInt32(datafile, address); if (ptr != 0) { ptr = (int)(ptr - imageBase); NJS_OBJECT mdl = new NJS_OBJECT(datafile, ptr, imageBase, modelfmt, new Dictionary <int, Attach>()); string idx = name + "[" + i.ToString(NumberFormatInfo.InvariantInfo) + "]"; DllItemInfo info = new DllItemInfo() { Export = name, Index = i, Label = mdl.Name }; output.Items.Add(info); if (!labels.Contains(mdl.Name)) { string fn = Path.Combine(data.Filename, i.ToString(NumberFormatInfo.InvariantInfo) + modelext); models.Add(new ModelAnimations(fn, idx, mdl, modelfmt)); labels.AddRange(mdl.GetLabels()); } } address += 4; } break; case "modelsarray": for (int i = 0; i < data.Length; i++) { int ptr = BitConverter.ToInt32(datafile, address); if (ptr != 0) { ptr = (int)(ptr - imageBase); BasicAttach dummy = new BasicAttach(datafile, ptr, imageBase, modelfmt == ModelFormat.BasicDX); NJS_OBJECT mdl = new NJS_OBJECT() { Attach = dummy }; string idx = name + "[" + i.ToString(NumberFormatInfo.InvariantInfo) + "]"; DllItemInfo info = new DllItemInfo() { Export = name, Index = i, Label = dummy.Name }; output.Items.Add(info); if (!labels.Contains(dummy.Name)) { string fn = Path.Combine(data.Filename, i.ToString(NumberFormatInfo.InvariantInfo) + modelext); models.Add(new ModelAnimations(fn, idx, mdl, ModelFormat.BasicDX)); labels.AddRange(mdl.GetLabels()); } } address += 4; } break; case "basicmodel": { NJS_OBJECT mdl = new NJS_OBJECT(datafile, address, imageBase, ModelFormat.Basic, new Dictionary <int, Attach>()); DllItemInfo info = new DllItemInfo() { Export = name, Label = mdl.Name }; output.Items.Add(info); if (!labels.Contains(mdl.Name)) { models.Add(new ModelAnimations(data.Filename, name, mdl, ModelFormat.Basic)); labels.AddRange(mdl.GetLabels()); } } break; case "basicmodelarray": for (int i = 0; i < data.Length; i++) { int ptr = BitConverter.ToInt32(datafile, address); if (ptr != 0) { ptr = (int)(ptr - imageBase); NJS_OBJECT mdl = new NJS_OBJECT(datafile, ptr, imageBase, ModelFormat.Basic, new Dictionary <int, Attach>()); string idx = name + "[" + i.ToString(NumberFormatInfo.InvariantInfo) + "]"; DllItemInfo info = new DllItemInfo() { Export = name, Index = i, Label = mdl.Name }; output.Items.Add(info); if (!labels.Contains(mdl.Name)) { string fn = Path.Combine(data.Filename, i.ToString(NumberFormatInfo.InvariantInfo) + ".sa1mdl"); models.Add(new ModelAnimations(fn, idx, mdl, ModelFormat.Basic)); labels.AddRange(mdl.GetLabels()); } } address += 4; } break; case "basicdxmodel": { NJS_OBJECT mdl = new NJS_OBJECT(datafile, address, imageBase, ModelFormat.BasicDX, new Dictionary <int, Attach>()); DllItemInfo info = new DllItemInfo() { Export = name, Label = mdl.Name }; output.Items.Add(info); if (!labels.Contains(mdl.Name)) { models.Add(new ModelAnimations(data.Filename, name, mdl, ModelFormat.BasicDX)); labels.AddRange(mdl.GetLabels()); } } break; case "basicdxmodelarray": for (int i = 0; i < data.Length; i++) { int ptr = BitConverter.ToInt32(datafile, address); if (ptr != 0) { ptr = (int)(ptr - imageBase); NJS_OBJECT mdl = new NJS_OBJECT(datafile, ptr, imageBase, ModelFormat.BasicDX, new Dictionary <int, Attach>()); string idx = name + "[" + i.ToString(NumberFormatInfo.InvariantInfo) + "]"; DllItemInfo info = new DllItemInfo() { Export = name, Index = i, Label = mdl.Name }; output.Items.Add(info); if (!labels.Contains(mdl.Name)) { string fn = Path.Combine(data.Filename, i.ToString(NumberFormatInfo.InvariantInfo) + ".sa1mdl"); models.Add(new ModelAnimations(fn, idx, mdl, ModelFormat.BasicDX)); labels.AddRange(mdl.GetLabels()); } } address += 4; } break; case "chunkmodel": { NJS_OBJECT mdl = new NJS_OBJECT(datafile, address, imageBase, ModelFormat.Chunk, new Dictionary <int, Attach>()); DllItemInfo info = new DllItemInfo() { Export = name, Label = mdl.Name }; output.Items.Add(info); if (!labels.Contains(mdl.Name)) { models.Add(new ModelAnimations(data.Filename, name, mdl, ModelFormat.Chunk)); labels.AddRange(mdl.GetLabels()); } } break; case "chunkmodelarray": for (int i = 0; i < data.Length; i++) { int ptr = BitConverter.ToInt32(datafile, address); if (ptr != 0) { ptr = (int)(ptr - imageBase); NJS_OBJECT mdl = new NJS_OBJECT(datafile, ptr, imageBase, ModelFormat.Chunk, new Dictionary <int, Attach>()); string idx = name + "[" + i.ToString(NumberFormatInfo.InvariantInfo) + "]"; DllItemInfo info = new DllItemInfo() { Export = name, Index = i, Label = mdl.Name }; output.Items.Add(info); if (!labels.Contains(mdl.Name)) { string fn = Path.Combine(data.Filename, i.ToString(NumberFormatInfo.InvariantInfo) + ".sa2mdl"); models.Add(new ModelAnimations(fn, idx, mdl, ModelFormat.Chunk)); labels.AddRange(mdl.GetLabels()); } } address += 4; } break; case "actionarray": for (int i = 0; i < data.Length; i++) { int ptr = BitConverter.ToInt32(datafile, address); if (ptr != 0) { ptr = (int)(ptr - imageBase); NJS_ACTION ani = new NJS_ACTION(datafile, ptr, imageBase, modelfmt, new Dictionary <int, Attach>()); string nm = item.Key + "_" + i; bool saveani = false; if (!anilabels.ContainsKey(ani.Animation.Name)) { anilabels.Add(ani.Animation.Name, nm); ani.Animation.Name = nm; saveani = true; } else { nm = anilabels[ani.Animation.Name]; } DllItemInfo info = new DllItemInfo() { Export = name, Index = i, Label = nm, Field = "motion" }; output.Items.Add(info); info = new DllItemInfo() { Export = name, Index = i, Label = ani.Model.Name, Field = "object" }; output.Items.Add(info); string outputFN = Path.Combine(fileOutputPath, i.ToString(NumberFormatInfo.InvariantInfo) + ".saanim"); string fn = Path.Combine(data.Filename, i.ToString(NumberFormatInfo.InvariantInfo) + ".saanim"); if (saveani) { ani.Animation.Save(outputFN); output.Files[fn] = new FileTypeHash("animation", HelperFunctions.FileHash(outputFN)); } if (models.Contains(ani.Model.Name)) { ModelAnimations mdl = models[ani.Model.Name]; System.Text.StringBuilder sb = new System.Text.StringBuilder(260); PathRelativePathTo(sb, Path.GetFullPath(Path.Combine(projectFolderName, mdl.Filename)), 0, Path.GetFullPath(outputFN), 0); mdl.Animations.Add(sb.ToString()); // this is where the problem is } else { string mfn = Path.ChangeExtension(fn, modelext); string outputmfn = Path.Combine(projectFolderName, mfn); string animationName = Path.GetFileName(outputFN); ModelFile.CreateFile(outputmfn, ani.Model, new[] { animationName }, null, $"{name}[{i}]->object", null, modelfmt); output.Files[mfn] = new FileTypeHash("model", HelperFunctions.FileHash(outputmfn)); } } address += 4; } break; case "motionarray": { int[] nodecounts = data.CustomProperties["nodecounts"].Split(',').Select(a => int.Parse(a)).ToArray(); for (int i = 0; i < data.Length; i++) { int ptr = BitConverter.ToInt32(datafile, address); if (ptr != 0) { ptr = (int)(ptr - imageBase); NJS_MOTION ani = new NJS_MOTION(datafile, ptr, imageBase, nodecounts[i]); string nm = item.Key + "_" + i; bool saveani = false; if (!anilabels.ContainsKey(ani.Name)) { anilabels.Add(ani.Name, nm); ani.Name = nm; saveani = true; } else { nm = anilabels[ani.Name]; } DllItemInfo info = new DllItemInfo() { Export = name, Index = i, Label = nm }; output.Items.Add(info); if (saveani) { string outputFN = Path.Combine(fileOutputPath, i.ToString(NumberFormatInfo.InvariantInfo) + ".saanim"); string fn = Path.Combine(data.Filename, i.ToString(NumberFormatInfo.InvariantInfo) + ".saanim"); ani.Save(outputFN); output.Files[fn] = new FileTypeHash("animation", HelperFunctions.FileHash(outputFN)); } } address += 4; } } break; case "texlist": if (output.TexLists == null) { output.TexLists = new TexListContainer(); } output.TexLists.Add((uint)(address + imageBase), new DllTexListInfo(name, null)); break; case "texlistarray": if (output.TexLists == null) { output.TexLists = new TexListContainer(); } for (int i = 0; i < data.Length; i++) { uint ptr = BitConverter.ToUInt32(datafile, address); if (ptr != 0 && !output.TexLists.ContainsKey(ptr)) { output.TexLists.Add(ptr, new DllTexListInfo(name, i)); } address += 4; } break; case "animindexlist": { Directory.CreateDirectory(fileOutputPath); List <string> hashes = new List <string>(); int i = ByteConverter.ToInt16(datafile, address); while (i != -1) { new NJS_MOTION(datafile, datafile.GetPointer(address + 4, imageBase), imageBase, ByteConverter.ToInt16(datafile, address + 2)) .Save(fileOutputPath + "/" + i.ToString(NumberFormatInfo.InvariantInfo) + ".saanim"); hashes.Add(i.ToString(NumberFormatInfo.InvariantInfo) + ":" + HelperFunctions.FileHash(fileOutputPath + "/" + i.ToString(NumberFormatInfo.InvariantInfo) + ".saanim")); address += 8; i = ByteConverter.ToInt16(datafile, address); } output.DataItems.Add(new DllDataItemInfo() { Type = type, Export = name, Filename = data.Filename, MD5Hash = string.Join("|", hashes.ToArray()) }); } break; case "charaobjectdatalist": { Directory.CreateDirectory(fileOutputPath); List <CharaObjectData> result = new List <CharaObjectData>(); List <string> hashes = new List <string>(); for (int i = 0; i < data.Length; i++) { string chnm = charaobjectnames[i]; CharaObjectData chara = new CharaObjectData(); NJS_OBJECT model = new NJS_OBJECT(datafile, (int)(BitConverter.ToInt32(datafile, address) - imageBase), imageBase, ModelFormat.Chunk, new Dictionary <int, Attach>()); chara.MainModel = model.Name; NJS_MOTION anim = new NJS_MOTION(datafile, (int)(BitConverter.ToInt32(datafile, address + 4) - imageBase), imageBase, model.CountAnimated()); chara.Animation1 = anim.Name; anim.Save(Path.Combine(fileOutputPath, $"{chnm} Anim 1.saanim")); hashes.Add($"{chnm} Anim 1.saanim:" + HelperFunctions.FileHash(Path.Combine(fileOutputPath, $"{chnm} Anim 1.saanim"))); anim = new NJS_MOTION(datafile, (int)(BitConverter.ToInt32(datafile, address + 8) - imageBase), imageBase, model.CountAnimated()); chara.Animation2 = anim.Name; anim.Save(Path.Combine(fileOutputPath, $"{chnm} Anim 2.saanim")); hashes.Add($"{chnm} Anim 2.saanim:" + HelperFunctions.FileHash(Path.Combine(fileOutputPath, $"{chnm} Anim 2.saanim"))); anim = new NJS_MOTION(datafile, (int)(BitConverter.ToInt32(datafile, address + 12) - imageBase), imageBase, model.CountAnimated()); chara.Animation3 = anim.Name; anim.Save(Path.Combine(fileOutputPath, $"{chnm} Anim 3.saanim")); hashes.Add($"{chnm} Anim 3.saanim:" + HelperFunctions.FileHash(Path.Combine(fileOutputPath, $"{chnm} Anim 3.saanim"))); ModelFile.CreateFile(Path.Combine(fileOutputPath, $"{chnm}.sa2mdl"), model, new[] { $"{chnm} Anim 1.saanim", $"{chnm} Anim 2.saanim", $"{chnm} Anim 3.saanim" }, null, null, null, ModelFormat.Chunk); hashes.Add($"{chnm}.sa2mdl:" + HelperFunctions.FileHash(Path.Combine(fileOutputPath, $"{chnm}.sa2mdl"))); int ptr = BitConverter.ToInt32(datafile, address + 16); if (ptr != 0) { model = new NJS_OBJECT(datafile, (int)(ptr - imageBase), imageBase, ModelFormat.Chunk, new Dictionary <int, Attach>()); chara.AccessoryModel = model.Name; chara.AccessoryAttachNode = "object_" + (BitConverter.ToInt32(datafile, address + 20) - imageBase).ToString("X8"); ModelFile.CreateFile(Path.Combine(fileOutputPath, $"{chnm} Accessory.sa2mdl"), model, null, null, null, null, ModelFormat.Chunk); hashes.Add($"{chnm} Accessory.sa2mdl:" + HelperFunctions.FileHash(Path.Combine(fileOutputPath, $"{chnm} Accessory.sa2mdl"))); } ptr = BitConverter.ToInt32(datafile, address + 24); if (ptr != 0) { model = new NJS_OBJECT(datafile, (int)(ptr - imageBase), imageBase, ModelFormat.Chunk, new Dictionary <int, Attach>()); chara.SuperModel = model.Name; anim = new NJS_MOTION(datafile, (int)(BitConverter.ToInt32(datafile, address + 28) - imageBase), imageBase, model.CountAnimated()); chara.SuperAnimation1 = anim.Name; anim.Save(Path.Combine(fileOutputPath, $"Super {chnm} Anim 1.saanim")); hashes.Add($"Super {chnm} Anim 1.saanim:" + HelperFunctions.FileHash(Path.Combine(fileOutputPath, $"Super {chnm} Anim 1.saanim"))); anim = new NJS_MOTION(datafile, (int)(BitConverter.ToInt32(datafile, address + 32) - imageBase), imageBase, model.CountAnimated()); chara.SuperAnimation2 = anim.Name; anim.Save(Path.Combine(fileOutputPath, $"Super {chnm} Anim 2.saanim")); hashes.Add($"Super {chnm} Anim 2.saanim:" + HelperFunctions.FileHash(Path.Combine(fileOutputPath, $"Super {chnm} Anim 2.saanim"))); anim = new NJS_MOTION(datafile, (int)(BitConverter.ToInt32(datafile, address + 36) - imageBase), imageBase, model.CountAnimated()); chara.SuperAnimation3 = anim.Name; anim.Save(Path.Combine(fileOutputPath, $"Super {chnm} Anim 3.saanim")); hashes.Add($"Super {chnm} Anim 3.saanim:" + HelperFunctions.FileHash(Path.Combine(fileOutputPath, $"Super {chnm} Anim 3.saanim"))); ModelFile.CreateFile(Path.Combine(fileOutputPath, $"Super {chnm}.sa2mdl"), model, new[] { $"Super {chnm} Anim 1.saanim", $"Super {chnm} Anim 2.saanim", $"Super {chnm} Anim 3.saanim" }, null, null, null, ModelFormat.Chunk); hashes.Add($"Super {chnm}.sa2mdl:" + HelperFunctions.FileHash(Path.Combine(fileOutputPath, $"Super {chnm}.sa2mdl"))); } chara.Unknown1 = BitConverter.ToInt32(datafile, address + 40); chara.Rating = BitConverter.ToInt32(datafile, address + 44); chara.DescriptionID = BitConverter.ToInt32(datafile, address + 48); chara.TextBackTexture = BitConverter.ToInt32(datafile, address + 52); chara.Unknown5 = BitConverter.ToSingle(datafile, address + 56); result.Add(chara); address += 60; } IniSerializer.Serialize(result, Path.Combine(fileOutputPath, "info.ini")); hashes.Add("info.ini:" + HelperFunctions.FileHash(Path.Combine(fileOutputPath, "info.ini"))); output.DataItems.Add(new DllDataItemInfo() { Type = type, Export = name, Filename = data.Filename, MD5Hash = string.Join("|", hashes.ToArray()) }); } break; case "kartspecialinfolist": { Directory.CreateDirectory(fileOutputPath); List <KartSpecialInfo> result = new List <KartSpecialInfo>(); List <string> hashes = new List <string>(); for (int i = 0; i < data.Length; i++) { KartSpecialInfo kart = new KartSpecialInfo { ID = ByteConverter.ToInt32(datafile, address) }; NJS_OBJECT model = new NJS_OBJECT(datafile, (int)(BitConverter.ToInt32(datafile, address + 4) - imageBase), imageBase, ModelFormat.Chunk, new Dictionary <int, Attach>()); kart.Model = model.Name; ModelFile.CreateFile(Path.Combine(fileOutputPath, $"{i}.sa2mdl"), model, null, null, null, null, ModelFormat.Chunk); hashes.Add($"{i}.sa2mdl:" + HelperFunctions.FileHash(Path.Combine(fileOutputPath, $"{i}.sa2mdl"))); int ptr = BitConverter.ToInt32(datafile, address + 8); if (ptr != 0) { model = new NJS_OBJECT(datafile, (int)(ptr - imageBase), imageBase, ModelFormat.Chunk, new Dictionary <int, Attach>()); kart.LowModel = model.Name; ModelFile.CreateFile(Path.Combine(fileOutputPath, $"{i} Low.sa2mdl"), model, null, null, null, null, ModelFormat.Chunk); hashes.Add($"{i} Low.sa2mdl:" + HelperFunctions.FileHash(Path.Combine(fileOutputPath, $"{i} Low.sa2mdl"))); } kart.TexList = ByteConverter.ToUInt32(datafile, address + 12); kart.Unknown1 = ByteConverter.ToInt32(datafile, address + 16); kart.Unknown2 = ByteConverter.ToInt32(datafile, address + 20); kart.Unknown3 = ByteConverter.ToInt32(datafile, address + 24); result.Add(kart); address += 0x1C; } IniSerializer.Serialize(result, Path.Combine(fileOutputPath, "info.ini")); hashes.Add("info.ini:" + HelperFunctions.FileHash(Path.Combine(fileOutputPath, "info.ini"))); output.DataItems.Add(new DllDataItemInfo() { Type = type, Export = name, Filename = data.Filename, MD5Hash = string.Join("|", hashes.ToArray()) }); } break; case "chaomotiontable": { Directory.CreateDirectory(fileOutputPath); List <ChaoMotionTableEntry> result = new List <ChaoMotionTableEntry>(); List <string> hashes = new List <string>(); int nodeCount = int.Parse(data.CustomProperties["nodecount"]); Dictionary <int, string> mtns = new Dictionary <int, string>(); for (int i = 0; i < data.Length; i++) { ChaoMotionTableEntry cmte = new ChaoMotionTableEntry(); int mtnaddr = (int)(ByteConverter.ToInt32(datafile, address) - imageBase); if (!mtns.ContainsKey(mtnaddr)) { NJS_MOTION motion = new NJS_MOTION(datafile, mtnaddr, imageBase, nodeCount, shortrot: true); cmte.Motion = motion.Name; mtns.Add(mtnaddr, motion.Name); motion.Save(Path.Combine(fileOutputPath, $"{i}.saanim")); hashes.Add($"{i}.saanim:" + HelperFunctions.FileHash(Path.Combine(fileOutputPath, $"{i}.saanim"))); } else { cmte.Motion = mtns[mtnaddr]; } cmte.Flag1 = ByteConverter.ToUInt16(datafile, address + 4); cmte.Pose = ByteConverter.ToUInt16(datafile, address + 6); cmte.TransitionID = ByteConverter.ToInt32(datafile, address + 8); cmte.Flag2 = ByteConverter.ToUInt32(datafile, address + 12); cmte.StartFrame = ByteConverter.ToSingle(datafile, address + 16); cmte.EndFrame = ByteConverter.ToSingle(datafile, address + 20); cmte.PlaySpeed = ByteConverter.ToSingle(datafile, address + 24); result.Add(cmte); address += 0x1C; } IniSerializer.Serialize(result, Path.Combine(fileOutputPath, "info.ini")); hashes.Add("info.ini:" + HelperFunctions.FileHash(Path.Combine(fileOutputPath, "info.ini"))); output.DataItems.Add(new DllDataItemInfo() { Type = type, Export = name, Filename = data.Filename, MD5Hash = string.Join("|", hashes.ToArray()) }); } break; } itemcount++; } foreach (ModelAnimations item in models) { string modelOutputPath = string.Concat(projectFolderName, item.Filename); //string modelOutputPath = item.Filename; ModelFile.CreateFile(modelOutputPath, item.Model, item.Animations.ToArray(), null, item.Name, null, item.Format); string type = "model"; switch (item.Format) { case ModelFormat.Basic: type = "basicmodel"; break; case ModelFormat.BasicDX: type = "basicdxmodel"; break; case ModelFormat.Chunk: type = "chunkmodel"; break; } output.Files[item.Filename] = new FileTypeHash(type, HelperFunctions.FileHash(modelOutputPath)); } IniSerializer.Serialize(output, Path.Combine(projectFolderName, Path.GetFileNameWithoutExtension(datafilename)) + "_data.ini"); timer.Stop(); Console.WriteLine("Split " + itemcount + " items in " + timer.Elapsed.TotalSeconds + " seconds."); Console.WriteLine(); } #if !DEBUG catch (Exception e) { Console.WriteLine(e.Message); Console.WriteLine(e.StackTrace); Console.WriteLine("Press any key to exit."); Console.ReadLine(); return((int)SA_Tools.Split.SplitERRORVALUE.UnhandledException); } #endif return((int)SA_Tools.Split.SplitERRORVALUE.Success); }
private void button1_Click(object sender, EventArgs e) { bool success = false; uint address = (uint)numericUpDownBinaryAddress.Value; if (checkBoxBinaryMemory.Checked) { address -= (uint)numericUpDownBinaryKey.Value; } LandTableFormat format = (LandTableFormat)comboBoxBinaryFormat.SelectedIndex; LandTableFormat outfmt = format; if (format == LandTableFormat.SADX) { outfmt = LandTableFormat.SA1; } ByteConverter.BigEndian = checkBoxBinaryBigEndian.Checked; Settings.Author = textBoxBinaryAuthor.Text; Settings.Save(); SaveFileDialog sd = new SaveFileDialog(); switch (comboBoxBinaryItemType.SelectedIndex) { //Level case 0: sd = new SaveFileDialog() { DefaultExt = outfmt.ToString().ToLowerInvariant() + "lvl", Filter = outfmt.ToString().ToUpperInvariant() + "LVL Files|*." + outfmt.ToString().ToLowerInvariant() + "lvl|All Files|*.*" }; if (sd.ShowDialog() == System.Windows.Forms.DialogResult.OK) { new LandTable(file, (int)numericUpDownBinaryAddress.Value, (uint)numericUpDownBinaryKey.Value, format) { Author = textBoxBinaryAuthor.Text, Description = textBoxBinaryDescription.Text }.SaveToFile(sd.FileName, outfmt); if (checkBoxBinaryStructs.Checked) { ConvertToText(sd.FileName); } if (!checkBoxBinarySAModel.Checked) { File.Delete(sd.FileName); } success = true; } break; //Model case 1: sd = new SaveFileDialog() { DefaultExt = outfmt.ToString().ToLowerInvariant() + "mdl", Filter = outfmt.ToString().ToUpperInvariant() + "MDL Files|*." + outfmt.ToString().ToLowerInvariant() + "mdl|All Files|*.*" }; if (sd.ShowDialog() == System.Windows.Forms.DialogResult.OK) { NJS_OBJECT tempmodel = new NJS_OBJECT(file, (int)address, (uint)numericUpDownBinaryKey.Value, (ModelFormat)comboBoxBinaryFormat.SelectedIndex, null); ModelFile.CreateFile(sd.FileName, tempmodel, null, textBoxBinaryAuthor.Text, textBoxBinaryDescription.Text, null, (ModelFormat)comboBoxBinaryFormat.SelectedIndex); ConvertToText(sd.FileName, checkBoxBinaryStructs.Checked, checkBoxBinaryNJA.Checked, false); if (!checkBoxBinarySAModel.Checked) { File.Delete(sd.FileName); } success = true; } break; //Action case 2: sd = new SaveFileDialog() { DefaultExt = outfmt.ToString().ToLowerInvariant() + "mdl", Filter = outfmt.ToString().ToUpperInvariant() + "MDL Files|*." + outfmt.ToString().ToLowerInvariant() + "mdl|All Files|*.*" }; if (sd.ShowDialog() == System.Windows.Forms.DialogResult.OK) { //Model NJS_ACTION tempaction = new NJS_ACTION(file, (int)address, (uint)numericUpDownBinaryKey.Value, (ModelFormat)comboBoxBinaryFormat.SelectedIndex, null); NJS_OBJECT tempmodel = tempaction.Model; ModelFile.CreateFile(sd.FileName, tempmodel, null, textBoxBinaryAuthor.Text, textBoxBinaryDescription.Text, null, (ModelFormat)comboBoxBinaryFormat.SelectedIndex); ConvertToText(sd.FileName, checkBoxBinaryStructs.Checked, checkBoxBinaryNJA.Checked, false); if (!checkBoxBinarySAModel.Checked) { File.Delete(sd.FileName); } //Action string saanimPath = Path.Combine(Path.GetDirectoryName(sd.FileName), Path.GetFileNameWithoutExtension(sd.FileName) + ".saanim"); tempaction.Animation.Save(saanimPath); ConvertToText(saanimPath, checkBoxBinaryStructs.Checked, false, checkBoxBinaryJSON.Checked); if (checkBoxBinarySAModel.Checked) { using (TextWriter twmain = File.CreateText(Path.Combine(Path.GetDirectoryName(sd.FileName), Path.GetFileNameWithoutExtension(sd.FileName) + ".action"))) { twmain.WriteLine(Path.GetFileName(saanimPath)); twmain.Flush(); twmain.Close(); } } else { File.Delete(saanimPath); } success = true; } break; } if (success) { MessageBox.Show("Data extracted!", "Binary Data Extractor", MessageBoxButtons.OK, MessageBoxIcon.Information); } }
public static int SplitFile(string datafilename, string inifilename, string projectFolderName) { try { byte[] datafile = File.ReadAllBytes(datafilename); IniData inifile = IniSerializer.Deserialize <IniData>(inifilename); if (inifile.MD5 != null && inifile.MD5.Count > 0) { string datahash = HelperFunctions.FileHash(datafile); if (!inifile.MD5.Any(h => h.Equals(datahash, StringComparison.OrdinalIgnoreCase))) { Console.WriteLine("The file {0} is not valid for use with the INI {1}.", datafilename, inifilename); return((int)SplitERRORVALUE.InvalidDataMapping); } } ByteConverter.BigEndian = SonicRetro.SAModel.ByteConverter.BigEndian = inifile.BigEndian; Environment.CurrentDirectory = Path.Combine(Environment.CurrentDirectory, Path.GetDirectoryName(datafilename)); if (inifile.Compressed) { datafile = FraGag.Compression.Prs.Decompress(datafile); } uint imageBase = HelperFunctions.SetupEXE(ref datafile) ?? inifile.ImageBase.Value; if (Path.GetExtension(datafilename).Equals(".rel", StringComparison.OrdinalIgnoreCase)) { HelperFunctions.FixRELPointers(datafile); } bool SA2 = inifile.Game == Game.SA2 | inifile.Game == Game.SA2B; ModelFormat modelfmt = 0; LandTableFormat landfmt = 0; switch (inifile.Game) { case Game.SA1: modelfmt = ModelFormat.Basic; landfmt = LandTableFormat.SA1; break; case Game.SADX: modelfmt = ModelFormat.BasicDX; landfmt = LandTableFormat.SADX; break; case Game.SA2: case Game.SA2B: modelfmt = ModelFormat.Chunk; landfmt = LandTableFormat.SA2; break; } int itemcount = 0; Dictionary <string, MasterObjectListEntry> masterobjlist = new Dictionary <string, MasterObjectListEntry>(); Dictionary <string, Dictionary <string, int> > objnamecounts = new Dictionary <string, Dictionary <string, int> >(); Stopwatch timer = new Stopwatch(); timer.Start(); foreach (KeyValuePair <string, SA_Tools.FileInfo> item in inifile.Files) { if (string.IsNullOrEmpty(item.Key)) { continue; } string filedesc = item.Key; SA_Tools.FileInfo data = item.Value; Dictionary <string, string> customProperties = data.CustomProperties; string type = data.Type; int address = data.Address; bool nohash = false; string fileOutputPath = string.Concat(projectFolderName, data.Filename); Console.WriteLine(item.Key + ": " + data.Address.ToString("X") + " → " + fileOutputPath); Directory.CreateDirectory(Path.GetDirectoryName(fileOutputPath)); switch (type) { case "landtable": new LandTable(datafile, address, imageBase, landfmt) { Description = item.Key }.SaveToFile(fileOutputPath, landfmt); break; case "model": { NJS_OBJECT mdl = new NJS_OBJECT(datafile, address, imageBase, modelfmt, new Dictionary <int, Attach>()); string[] mdlanis = new string[0]; if (customProperties.ContainsKey("animations")) { mdlanis = customProperties["animations"].Split(','); } string[] mdlmorphs = new string[0]; if (customProperties.ContainsKey("morphs")) { mdlmorphs = customProperties["morphs"].Split(','); } ModelFile.CreateFile(fileOutputPath, mdl, mdlanis, null, item.Key, null, modelfmt); } break; case "basicmodel": { NJS_OBJECT mdl = new NJS_OBJECT(datafile, address, imageBase, ModelFormat.Basic, new Dictionary <int, Attach>()); string[] mdlanis = new string[0]; if (customProperties.ContainsKey("animations")) { mdlanis = customProperties["animations"].Split(','); } string[] mdlmorphs = new string[0]; if (customProperties.ContainsKey("morphs")) { mdlmorphs = customProperties["morphs"].Split(','); } ModelFile.CreateFile(fileOutputPath, mdl, mdlanis, null, item.Key, null, ModelFormat.Basic); } break; case "basicdxmodel": { NJS_OBJECT mdl = new NJS_OBJECT(datafile, address, imageBase, ModelFormat.BasicDX, new Dictionary <int, Attach>()); string[] mdlanis = new string[0]; if (customProperties.ContainsKey("animations")) { mdlanis = customProperties["animations"].Split(','); } string[] mdlmorphs = new string[0]; if (customProperties.ContainsKey("morphs")) { mdlmorphs = customProperties["morphs"].Split(','); } ModelFile.CreateFile(fileOutputPath, mdl, mdlanis, null, item.Key, null, ModelFormat.BasicDX); } break; case "chunkmodel": { NJS_OBJECT mdl = new NJS_OBJECT(datafile, address, imageBase, ModelFormat.Chunk, new Dictionary <int, Attach>()); string[] mdlanis = new string[0]; if (customProperties.ContainsKey("animations")) { mdlanis = customProperties["animations"].Split(','); } string[] mdlmorphs = new string[0]; if (customProperties.ContainsKey("morphs")) { mdlmorphs = customProperties["morphs"].Split(','); } ModelFile.CreateFile(fileOutputPath, mdl, mdlanis, null, item.Key, null, ModelFormat.Chunk); } break; case "gcmodel": { NJS_OBJECT mdl = new NJS_OBJECT(datafile, address, imageBase, ModelFormat.GC, new Dictionary <int, Attach>()); string[] mdlanis = new string[0]; if (customProperties.ContainsKey("animations")) { mdlanis = customProperties["animations"].Split(','); } string[] mdlmorphs = new string[0]; if (customProperties.ContainsKey("morphs")) { mdlmorphs = customProperties["morphs"].Split(','); } ModelFile.CreateFile(fileOutputPath, mdl, mdlanis, null, item.Key, null, ModelFormat.GC); } break; case "action": { NJS_ACTION ani = new NJS_ACTION(datafile, address, imageBase, modelfmt, new Dictionary <int, Attach>()); ani.Animation.Name = filedesc; ani.Animation.Save(fileOutputPath); } break; case "animation": new NJS_MOTION(datafile, address, imageBase, int.Parse(customProperties["numparts"], NumberStyles.AllowLeadingWhite | NumberStyles.AllowTrailingWhite, NumberFormatInfo.InvariantInfo)) { Name = filedesc } .Save(fileOutputPath); break; case "objlist": { ObjectListEntry[] objs = ObjectList.Load(datafile, address, imageBase, SA2); if (inifile.MasterObjectList != null) { foreach (ObjectListEntry obj in objs) { if (!masterobjlist.ContainsKey(obj.CodeString)) { masterobjlist.Add(obj.CodeString, new MasterObjectListEntry(obj)); } if (!objnamecounts.ContainsKey(obj.CodeString)) { objnamecounts.Add(obj.CodeString, new Dictionary <string, int>() { { obj.Name, 1 } }); } else if (!objnamecounts[obj.CodeString].ContainsKey(obj.Name)) { objnamecounts[obj.CodeString].Add(obj.Name, 1); } else { objnamecounts[obj.CodeString][obj.Name]++; } } } objs.Save(fileOutputPath); } break; case "startpos": if (SA2) { SA2StartPosList.Load(datafile, address).Save(fileOutputPath); } else { SA1StartPosList.Load(datafile, address).Save(fileOutputPath); } break; case "texlist": TextureList.Load(datafile, address, imageBase).Save(fileOutputPath); break; case "leveltexlist": new LevelTextureList(datafile, address, imageBase).Save(fileOutputPath); break; case "triallevellist": TrialLevelList.Save(TrialLevelList.Load(datafile, address, imageBase), fileOutputPath); break; case "bosslevellist": BossLevelList.Save(BossLevelList.Load(datafile, address), fileOutputPath); break; case "fieldstartpos": FieldStartPosList.Load(datafile, address).Save(fileOutputPath); break; case "soundtestlist": SoundTestList.Load(datafile, address, imageBase).Save(fileOutputPath); break; case "musiclist": { int muscnt = int.Parse(customProperties["length"], NumberStyles.Integer, NumberFormatInfo.InvariantInfo); MusicList.Load(datafile, address, imageBase, muscnt).Save(fileOutputPath); } break; case "soundlist": SoundList.Load(datafile, address, imageBase).Save(fileOutputPath); break; case "stringarray": { int cnt = int.Parse(customProperties["length"], NumberStyles.Integer, NumberFormatInfo.InvariantInfo); Languages lang = Languages.Japanese; if (data.CustomProperties.ContainsKey("language")) { lang = (Languages)Enum.Parse(typeof(Languages), data.CustomProperties["language"], true); } StringArray.Load(datafile, address, imageBase, cnt, lang).Save(fileOutputPath); } break; case "nextlevellist": NextLevelList.Load(datafile, address).Save(fileOutputPath); break; case "cutscenetext": { int cnt = int.Parse(customProperties["length"], NumberStyles.Integer, NumberFormatInfo.InvariantInfo); new CutsceneText(datafile, address, imageBase, cnt).Save(fileOutputPath, out string[] hashes); data.MD5Hash = string.Join(",", hashes); nohash = true; } break; case "recapscreen": { int cnt = int.Parse(customProperties["length"], NumberStyles.Integer, NumberFormatInfo.InvariantInfo); RecapScreenList.Load(datafile, address, imageBase, cnt).Save(fileOutputPath, out string[][] hashes); string[] hash2 = new string[hashes.Length]; for (int i = 0; i < hashes.Length; i++) { hash2[i] = string.Join(",", hashes[i]); } data.MD5Hash = string.Join(":", hash2); nohash = true; } break; case "npctext": { int cnt = int.Parse(customProperties["length"], NumberStyles.Integer, NumberFormatInfo.InvariantInfo); NPCTextList.Load(datafile, address, imageBase, cnt).Save(fileOutputPath, out string[][] hashes); string[] hash2 = new string[hashes.Length]; for (int i = 0; i < hashes.Length; i++) { hash2[i] = string.Join(",", hashes[i]); } data.MD5Hash = string.Join(":", hash2); nohash = true; } break; case "levelclearflags": LevelClearFlagList.Save(LevelClearFlagList.Load(datafile, address), fileOutputPath); break; case "deathzone": { List <DeathZoneFlags> flags = new List <DeathZoneFlags>(); string path = Path.GetDirectoryName(fileOutputPath); List <string> hashes = new List <string>(); int num = 0; while (ByteConverter.ToUInt32(datafile, address + 4) != 0) { flags.Add(new DeathZoneFlags(datafile, address)); string file = Path.Combine(path, num++.ToString(NumberFormatInfo.InvariantInfo) + (modelfmt == ModelFormat.Chunk ? ".sa2mdl" : ".sa1mdl")); ModelFile.CreateFile(file, new NJS_OBJECT(datafile, datafile.GetPointer(address + 4, imageBase), imageBase, modelfmt, new Dictionary <int, Attach>()), null, null, null, null, modelfmt); hashes.Add(HelperFunctions.FileHash(file)); address += 8; } flags.ToArray().Save(fileOutputPath); hashes.Insert(0, HelperFunctions.FileHash(fileOutputPath)); data.MD5Hash = string.Join(",", hashes.ToArray()); nohash = true; } break; case "skyboxscale": { int cnt = int.Parse(customProperties["count"], NumberStyles.Integer, NumberFormatInfo.InvariantInfo); SkyboxScaleList.Load(datafile, address, imageBase, cnt).Save(fileOutputPath); } break; case "stageselectlist": { int cnt = int.Parse(customProperties["count"], NumberStyles.Integer, NumberFormatInfo.InvariantInfo); StageSelectLevelList.Load(datafile, address, cnt).Save(fileOutputPath); } break; case "levelrankscores": LevelRankScoresList.Load(datafile, address).Save(fileOutputPath); break; case "levelranktimes": LevelRankTimesList.Load(datafile, address).Save(fileOutputPath); break; case "endpos": SA2EndPosList.Load(datafile, address).Save(fileOutputPath); break; case "animationlist": { int cnt = int.Parse(customProperties["count"], NumberStyles.Integer, NumberFormatInfo.InvariantInfo); SA2AnimationInfoList.Load(datafile, address, cnt).Save(fileOutputPath); } break; case "levelpathlist": { List <string> hashes = new List <string>(); ushort lvlnum = (ushort)ByteConverter.ToUInt32(datafile, address); while (lvlnum != 0xFFFF) { int ptr = ByteConverter.ToInt32(datafile, address + 4); if (ptr != 0) { ptr = (int)((uint)ptr - imageBase); SA1LevelAct level = new SA1LevelAct(lvlnum); string lvldir = Path.Combine(fileOutputPath, level.ToString()); PathList.Load(datafile, ptr, imageBase).Save(lvldir, out string[] lvlhashes); hashes.Add(level.ToString() + ":" + string.Join(",", lvlhashes)); } address += 8; lvlnum = (ushort)ByteConverter.ToUInt32(datafile, address); } data.MD5Hash = string.Join("|", hashes.ToArray()); nohash = true; } break; case "pathlist": { PathList.Load(datafile, address, imageBase).Save(fileOutputPath, out string[] hashes); data.MD5Hash = string.Join(",", hashes.ToArray()); nohash = true; } break; case "stagelightdatalist": SA1StageLightDataList.Load(datafile, address).Save(fileOutputPath); break; case "weldlist": WeldList.Load(datafile, address, imageBase).Save(fileOutputPath); break; case "bmitemattrlist": BlackMarketItemAttributesList.Load(datafile, address, imageBase).Save(fileOutputPath); break; case "creditstextlist": CreditsTextList.Load(datafile, address, imageBase).Save(fileOutputPath); break; case "animindexlist": { Directory.CreateDirectory(fileOutputPath); List <string> hashes = new List <string>(); int i = ByteConverter.ToInt16(datafile, address); while (i != -1) { new NJS_MOTION(datafile, datafile.GetPointer(address + 4, imageBase), imageBase, ByteConverter.ToInt16(datafile, address + 2)) .Save(fileOutputPath + "/" + i.ToString(NumberFormatInfo.InvariantInfo) + ".saanim"); hashes.Add(i.ToString(NumberFormatInfo.InvariantInfo) + ":" + HelperFunctions.FileHash(fileOutputPath + "/" + i.ToString(NumberFormatInfo.InvariantInfo) + ".saanim")); address += 8; i = ByteConverter.ToInt16(datafile, address); } data.MD5Hash = string.Join("|", hashes.ToArray()); nohash = true; } break; case "storysequence": SA2StoryList.Load(datafile, address).Save(fileOutputPath); break; default: // raw binary { byte[] bin = new byte[int.Parse(customProperties["size"], NumberStyles.HexNumber)]; Array.Copy(datafile, address, bin, 0, bin.Length); File.WriteAllBytes(fileOutputPath, bin); } break; } if (!nohash) { data.MD5Hash = HelperFunctions.FileHash(fileOutputPath); } itemcount++; } if (inifile.MasterObjectList != null) { foreach (KeyValuePair <string, MasterObjectListEntry> obj in masterobjlist) { KeyValuePair <string, int> name = new KeyValuePair <string, int>(); foreach (KeyValuePair <string, int> it in objnamecounts[obj.Key]) { if (it.Value > name.Value) { name = it; } } obj.Value.Name = name.Key; obj.Value.Names = objnamecounts[obj.Key].Select((it) => it.Key).ToArray(); } string masterObjectListOutputPath = string.Concat(projectFolderName, inifile.MasterObjectList); IniSerializer.Serialize(masterobjlist, masterObjectListOutputPath); } IniSerializer.Serialize(inifile, Path.Combine(projectFolderName, Path.GetFileNameWithoutExtension(datafilename) + "_data.ini")); timer.Stop(); Console.WriteLine("Split " + itemcount + " items in " + timer.Elapsed.TotalSeconds + " seconds."); Console.WriteLine(); } catch (Exception e) { Console.WriteLine(e.Message); Console.WriteLine(e.StackTrace); Console.WriteLine("Press any key to exit."); Console.ReadLine(); return((int)SplitERRORVALUE.UnhandledException); } return((int)SplitERRORVALUE.Success); }
public GeoAnimData(NJS_ACTION action) { Model = action.Model; Animation = action.Animation; }