private static string GetMotion(byte[] fc, int address, uint key, string fn, List <NJS_MOTION> motions, int cnt) { NJS_MOTION mtn = null; if (motions != null) { mtn = motions[ByteConverter.ToInt32(fc, address)]; } else { int ptr3 = fc.GetPointer(address, key); if (ptr3 != 0) { mtn = new NJS_MOTION(fc, ptr3, key, cnt); } } if (mtn == null) { return(null); } if (!motionfiles.ContainsKey(mtn.Name) || motionfiles[mtn.Name].Filename == null) { motionfiles[mtn.Name] = new MotionInfo(fn, mtn); } return(mtn.Name); }
public EventEntity(byte[] file, int address, uint imageBase, bool battle, Dictionary <string, NJS_OBJECT> models, List <NJS_MOTION> motions) { Model = Event.GetModel(file, address, imageBase, models); if (battle) { Motion = motions[ByteConverter.ToInt32(file, address + 4)]; ShapeMotion = motions[ByteConverter.ToInt32(file, address + 8)]; ShadowModel = Event.GetModel(file, address + 16, imageBase, models); Position = new Vertex(file, address + 24); Flags = ByteConverter.ToUInt32(file, address + 40); } else { int ptr = file.GetPointer(address + 4, imageBase); if (ptr != 0) { Motion = new NJS_MOTION(file, ptr, imageBase, Model.CountAnimated()); } ptr = file.GetPointer(address + 8, imageBase); if (ptr != 0) { ShapeMotion = new NJS_MOTION(file, ptr, imageBase, Model.CountMorph()); } Position = new Vertex(file, address + 16); Flags = ByteConverter.ToUInt32(file, address + 28); } }
private static void Convert(string[] args) { foreach (string filename in args) { Console.WriteLine(filename); removedFrames = 0; NJS_MOTION mtn = NJS_MOTION.Load(filename); foreach (var node in mtn.Models) { CheckValues(node.Value.Position); CheckValues(node.Value.Rotation); CheckValues(node.Value.Scale); CheckValues(node.Value.Vector); CheckValues(node.Value.Vertex); CheckValues(node.Value.Normal); CheckValues(node.Value.Target); CheckValues(node.Value.Roll); CheckValues(node.Value.Angle); CheckValues(node.Value.Color); CheckValues(node.Value.Intensity); CheckValues(node.Value.Spot); CheckValues(node.Value.Point); CheckValues(node.Value.Quaternion); } if (removedFrames > 0) { Console.WriteLine("Removed {0} frame{1}.", removedFrames, removedFrames == 1 ? "" : "s"); mtn.Save(filename); } } }
public GeoAnimData(byte[] file, int address, uint imageBase, LandTableFormat format, Dictionary <int, string> labels, Dictionary <int, Attach> attaches) { ModelFormat mfmt = 0; switch (format) { case LandTableFormat.SA1: mfmt = ModelFormat.Basic; break; case LandTableFormat.SADX: mfmt = ModelFormat.BasicDX; break; case LandTableFormat.SA2: mfmt = ModelFormat.Chunk; break; } Unknown1 = ByteConverter.ToInt32(file, address); Unknown2 = ByteConverter.ToSingle(file, address + 4); Unknown3 = ByteConverter.ToSingle(file, address + 8); Model = new NJS_OBJECT(file, (int)(ByteConverter.ToUInt32(file, address + 0xC) - imageBase), imageBase, mfmt, labels, attaches); Animation = NJS_MOTION.ReadHeader(file, (int)(ByteConverter.ToUInt32(file, address + 0x10) - imageBase), imageBase, mfmt, labels, attaches); Unknown4 = ByteConverter.ToInt32(file, address + 0x14); }
private static void Convert(string[] args) { foreach (string filename in args) { Console.WriteLine(filename); JsonSerializer js = new JsonSerializer() { Culture = System.Globalization.CultureInfo.InvariantCulture }; switch (Path.GetExtension(filename).ToLowerInvariant()) { case ".saanim": using (TextWriter tw = File.CreateText(Path.ChangeExtension(filename, ".json"))) using (JsonTextWriter jtw = new JsonTextWriter(tw) { Formatting = Formatting.Indented }) js.Serialize(jtw, NJS_MOTION.Load(filename)); break; case ".json": using (TextReader tr = File.OpenText(filename)) using (JsonTextReader jtr = new JsonTextReader(tr)) js.Deserialize <NJS_MOTION>(jtr).Save(Path.ChangeExtension(filename, ".saanim")); break; default: Console.WriteLine("Unknown file type."); return; } } }
static void Main(string[] args) { if (args.Length == 0) { Console.Write("Filename: "); args = new string[] { Console.ReadLine().Trim('"') }; } foreach (string filename in args) { byte[] fc; if (Path.GetExtension(filename).Equals(".prs", StringComparison.OrdinalIgnoreCase)) { fc = Prs.Decompress(filename); } else { fc = File.ReadAllBytes(filename); } string path = Path.Combine(Path.GetDirectoryName(Path.GetFullPath(filename)), Path.GetFileNameWithoutExtension(filename)); JsonSerializer js = new JsonSerializer(); EventIniData ini; using (TextReader tr = File.OpenText(Path.Combine(path, Path.ChangeExtension(Path.GetFileName(filename), ".json")))) using (JsonTextReader jtr = new JsonTextReader(tr)) ini = js.Deserialize <EventIniData>(jtr); uint key; if (fc[0] == 0x81) { ByteConverter.BigEndian = true; key = 0x8125FE60; } else { ByteConverter.BigEndian = false; key = 0xC600000; } bool battle = ini.Game == Game.SA2B; List <byte> modelbytes = new List <byte>(fc); Dictionary <string, uint> labels = new Dictionary <string, uint>(); foreach (string file in ini.Files.Where(a => a.Key.EndsWith(".sa2mdl", StringComparison.OrdinalIgnoreCase) && HelperFunctions.FileHash(Path.Combine(path, a.Key)) != a.Value).Select(a => a.Key)) { modelbytes.AddRange(new ModelFile(Path.Combine(path, file)).Model.GetBytes((uint)(key + modelbytes.Count), false, labels, out uint _)); } if (battle) { List <byte> motionbytes = new List <byte>(new byte[(ini.Motions.Count + 1) * 8]); Dictionary <string, int> partcounts = new Dictionary <string, int>(ini.Motions.Count); foreach (string file in ini.Files.Where(a => a.Key.EndsWith(".saanim", StringComparison.OrdinalIgnoreCase)).Select(a => a.Key)) { NJS_MOTION motion = NJS_MOTION.Load(Path.Combine(path, file)); motionbytes.AddRange(motion.GetBytes((uint)(key + motionbytes.Count), labels, out uint _)); partcounts.Add(motion.Name, motion.ModelParts); } byte[] mfc = motionbytes.ToArray(); for (int i = 0; i < ini.Motions.Count; i++) { if (ini.Motions[i] == null) { new byte[] { 0xFF, 0xFF, 0xFF, 0xFF } }
public GeoAnimData(NJS_OBJECT model, NJS_MOTION animation) { Model = model; Animation = animation; MaxFrame = animation.Frames; AnimationSpeed = 1.0f; Animation.ActionName = "action_" + Extensions.GenerateIdentifier(); Animation.ObjectName = model.Name; }
// Scan for Actions in a specific range static int ScanActions(uint addr, uint nummdl, ModelFormat modelfmt) { int count = 0; ByteConverter.BigEndian = BigEndian; if (nummdl == 0) { return(0); } for (uint address = addr; address < datafile.Length - 8; address += 1) { CurrentAddress = address; if (CancelScan) { break; } if (ByteConverter.ToUInt32(datafile, (int)address) != addr + ImageBase) { continue; } uint motaddr = ByteConverter.ToUInt32(datafile, (int)address + 4); if (motaddr < ImageBase) { continue; } try { NJS_MOTION mot = new NJS_MOTION(datafile, (int)(motaddr - ImageBase), ImageBase, (int)nummdl, null, false); if (mot.Models.Count == 0) { continue; } addresslist.Add(motaddr - ImageBase, "NJS_MOTION"); Console.WriteLine("\rMotion found for model {0} at address {1}", addr.ToString("X8"), (motaddr - ImageBase).ToString("X")); string fileOutputPath = Path.Combine(OutputFolder, "actions", (motaddr - ImageBase).ToString("X8")); if (SingleOutputFolder) { fileOutputPath = Path.Combine(OutputFolder, (motaddr - ImageBase).ToString("X8")); } mot.Save(fileOutputPath + ".saanim", NoMeta); uint[] arr = new uint[2]; arr[0] = addr; arr[1] = nummdl; actionlist.Add(motaddr - ImageBase, arr); AddAction(addr, motaddr - ImageBase); address += 7; count++; } catch (Exception) { continue; } } return(count); }
public void ProcessShapeMotionVertexData(NJS_MOTION motion, int frame) { int animindex = -1; NJS_OBJECT obj = this; do { obj.ProcessShapeMotionVertexData(motion, frame, ref animindex); obj = obj.Sibling; } while (obj != null); }
public static void BuildNBFile(string filename, string dest, int verbose = 0) { // Needs update to use filenames from an INI file Dictionary <int, string> sectionlist = IniSerializer.Deserialize <Dictionary <int, string> >(filename); List <byte> result = new List <byte>(); result.AddRange(BitConverter.GetBytes(0x04424A4E)); int numfiles = sectionlist.Count; result.AddRange(BitConverter.GetBytes(numfiles)); foreach (var item in sectionlist) { string insidepath = Path.Combine(Environment.CurrentDirectory, Path.GetFileNameWithoutExtension(filename), item.Key.ToString("D2")); byte[] writeout; // Convert models and motions to BIN if (item.Value != "NULL") { switch (Path.GetExtension(item.Value).ToLowerInvariant()) { case ".sa1mdl": ModelFile mdlfile = new ModelFile(insidepath + ".sa1mdl"); writeout = GetSections(mdlfile.Model); result.AddRange(BitConverter.GetBytes((ushort)1)); result.AddRange(BitConverter.GetBytes((ushort)0xCDCD)); result.AddRange(BitConverter.GetBytes((uint)writeout.Length)); result.AddRange(writeout); File.WriteAllBytes(insidepath + ".bin", writeout); break; case ".saanim": NJS_MOTION mot = NJS_MOTION.Load(insidepath + ".saanim"); if (verbose > 1) { Console.WriteLine("Section {0}", item.Key); } writeout = GetSections(mot, verbose); result.AddRange(BitConverter.GetBytes((ushort)3)); result.AddRange(BitConverter.GetBytes((ushort)0xCDCD)); result.AddRange(BitConverter.GetBytes((uint)writeout.Length)); result.AddRange(writeout); File.WriteAllBytes(insidepath + ".bin", writeout); break; } } else { result.AddRange(BitConverter.GetBytes((ushort)0)); result.AddRange(BitConverter.GetBytes((ushort)0xCDCD)); result.AddRange(BitConverter.GetBytes((uint)0)); } //Build the NB file File.WriteAllBytes(dest, result.ToArray()); } }
public override void ProcessShapeMotionVertexData(NJS_MOTION motion, float frame, int animindex) { if (!motion.Models.ContainsKey(animindex)) { ProcessVertexData(); return; } #if modellog Extensions.Log("Processing Chunk Attach " + Name + Environment.NewLine); #endif if (Vertex != null) { foreach (VertexChunk chunk in Vertex) { #if modellog Extensions.Log("Vertex Declaration: " + chunk.IndexOffset + "-" + (chunk.IndexOffset + chunk.VertexCount - 1) + Environment.NewLine); #endif if (VertexBuffer.Length < chunk.IndexOffset + chunk.VertexCount) { Array.Resize(ref VertexBuffer, chunk.IndexOffset + chunk.VertexCount); } Vertex[] vertdata = chunk.Vertices.ToArray(); Vertex[] normdata = chunk.Normals.ToArray(); AnimModelData data = motion.Models[animindex]; if (data.Vertex.Count > 0) { vertdata = data.GetVertex(frame); } if (data.Normal.Count > 0) { normdata = data.GetNormal(frame); } for (int i = 0; i < chunk.VertexCount; i++) { VertexBuffer[i + chunk.IndexOffset] = new VertexData(vertdata[i]); if (normdata.Length > 0) { VertexBuffer[i + chunk.IndexOffset].Normal = normdata[i]; } if (chunk.Diffuse.Count > 0) { VertexBuffer[i + chunk.IndexOffset].Color = chunk.Diffuse[i]; } } } } List <MeshInfo> result = new List <MeshInfo>(); if (Poly != null) { result = ProcessPolyList(PolyName, Poly, 0); } MeshInfo = result.ToArray(); }
public static void ExportINI(DllIniData IniData, Dictionary <string, bool> itemsToExport, string fileName) { string dstfol = Path.GetDirectoryName(fileName); DllIniData output = new DllIniData() { Name = IniData.Name, Game = IniData.Game, Exports = IniData.Exports, TexLists = IniData.TexLists }; List <string> labels = new List <string>(); foreach (KeyValuePair <string, FileTypeHash> item in IniData.Files.Where(i => itemsToExport[i.Key])) { Directory.CreateDirectory(Path.GetDirectoryName(Path.Combine(dstfol, item.Key))); File.Copy(item.Key, Path.Combine(dstfol, item.Key), true); switch (item.Value.Type) { case "landtable": LandTable tbl = LandTable.LoadFromFile(item.Key); labels.AddRange(tbl.GetLabels()); break; case "model": case "basicmodel": case "chunkmodel": case "gcmodel": case "basicdxmodel": NJS_OBJECT mdl = new ModelFile(item.Key).Model; labels.AddRange(mdl.GetLabels()); break; case "animation": NJS_MOTION ani = NJS_MOTION.Load(item.Key); labels.Add(ani.Name); break; } output.Files.Add(item.Key, new FileTypeHash(item.Value.Type, null)); } output.Items = new List <DllItemInfo>(IniData.Items.Where(a => labels.Contains(a.Label))); foreach (var item in IniData.DataItems.Where(i => itemsToExport[i.Filename])) { Directory.CreateDirectory(Path.Combine(dstfol, item.Filename)); CopyDirectory(new DirectoryInfo(item.Filename), Path.Combine(dstfol, item.Filename)); output.DataItems.Add(item); } IniSerializer.Serialize(output, fileName); }
private void ProcessShapeMotionVertexData(NJS_MOTION motion, int frame, ref int animindex) { if (Morph) { animindex++; } if (Attach != null) { Attach.ProcessShapeMotionVertexData(motion, frame, animindex); } foreach (NJS_OBJECT item in Children) { item.ProcessShapeMotionVertexData(motion, frame, ref animindex); } }
private static void ProcessMTN(string fn, Dictionary <int, NJS_MOTION> mtns) { byte[] file = File.ReadAllBytes(fn); if (Path.GetExtension(fn).Equals(".prs", StringComparison.OrdinalIgnoreCase)) { file = FraGag.Compression.Prs.Decompress(file); } ByteConverter.BigEndian = file[2] == 0; int address = 0; int i = ByteConverter.ToInt16(file, address); while (i != -1) { int aniaddr = ByteConverter.ToInt32(file, address + 4); mtns[i] = new NJS_MOTION(file, aniaddr, 0, ByteConverter.ToInt16(file, address + 2)); address += 8; i = ByteConverter.ToInt16(file, address); } }
static void Main(string[] args) { string filename; Console.Write("File: "); if (args.Length > 0) { Console.WriteLine(filename = args[0]); } else { filename = Console.ReadLine().Trim('"'); } JsonSerializer js = new JsonSerializer() { Culture = System.Globalization.CultureInfo.InvariantCulture }; switch (Path.GetExtension(filename).ToLowerInvariant()) { case ".saanim": using (TextWriter tw = File.CreateText(Path.ChangeExtension(filename, ".json"))) using (JsonTextWriter jtw = new JsonTextWriter(tw) { Formatting = Formatting.Indented }) js.Serialize(jtw, NJS_MOTION.Load(filename)); break; case ".json": using (TextReader tr = File.OpenText(filename)) using (JsonTextReader jtr = new JsonTextReader(tr)) js.Deserialize <NJS_MOTION>(jtr).Save(Path.ChangeExtension(filename, ".saanim")); break; default: Console.WriteLine("Unknown file type."); return; } }
public GeoAnimData(byte[] file, int address, uint imageBase, LandTableFormat format, Dictionary <int, string> labels, Dictionary <int, Attach> attaches) { ModelFormat mfmt = 0; switch (format) { case LandTableFormat.SA1: mfmt = ModelFormat.Basic; break; case LandTableFormat.SADX: mfmt = ModelFormat.BasicDX; break; case LandTableFormat.SA2: mfmt = ModelFormat.Chunk; break; } Unknown1 = ByteConverter.ToInt32(file, address); Unknown2 = ByteConverter.ToSingle(file, address + 4); Unknown3 = ByteConverter.ToSingle(file, address + 8); Model = new NJS_OBJECT(file, (int)(ByteConverter.ToUInt32(file, address + 0xC) - imageBase), imageBase, mfmt, labels, attaches); int actionaddr = (int)(ByteConverter.ToUInt32(file, address + 0x10) - imageBase); int motionaddr = (int)(ByteConverter.ToUInt32(file, actionaddr + 4) - imageBase); Animation = NJS_MOTION.ReadDirect(file, Model.CountAnimated(), motionaddr, imageBase, labels, attaches); Unknown4 = ByteConverter.ToInt32(file, address + 0x14); if (labels.ContainsKey(actionaddr)) { ActionName = labels[actionaddr]; } else { NJS_ACTION action = new NJS_ACTION(file, actionaddr, imageBase, mfmt, labels, attaches); ActionName = action.Name; labels.Add(actionaddr + (int)imageBase, ActionName); } }
static NJS_MOTION ProcessMotion(byte[] file, int verbose = 0, int startaddress = 0) { int frames; InterpolationMode intmode; AnimFlags animtype; int curaddr = 0; do { AnimSections section_type = (AnimSections)BitConverter.ToInt16(file, curaddr); int section_size = BitConverter.ToInt32(file, curaddr + 4); int section_addr = curaddr + 8; if (verbose > 1) { Console.Write("Subsection type {0}, size {1}, data begins at {2}\n", section_type.ToString(), section_size, section_addr.ToString("X")); } switch (section_type) { case AnimSections.MKEY_F: if (verbose > 1) { Console.Write(", calculated item count: {0}\n", (float)section_size / 16.0f); } break; case AnimSections.MKEY_A: if (verbose > 1) { Console.Write(", calculated item count: {0}\n", (float)section_size / 16.0f); } break; case AnimSections.UNKNOWN: if (verbose > 1) { Console.Write(", calculated item count: {0}\n", (float)section_size / 16.0f); } break; case AnimSections.MDATA2_HEADER: if (verbose > 1) { Console.Write(", number of MDATA entries: {0}\n", BitConverter.ToInt32(file, section_addr) >> BitConverter.ToInt32(file, section_addr + 4)); } break; case AnimSections.MOTION: if (verbose > 0) { Console.WriteLine("Motion at {0}", (startaddress + section_addr).ToString("X")); } frames = BitConverter.ToInt32(file, section_addr + 4); intmode = (InterpolationMode)BitConverter.ToInt16(file, section_addr + 10); animtype = (AnimFlags)BitConverter.ToInt16(file, section_addr + 8); int mdataaddr = BitConverter.ToInt32(file, section_addr); if (verbose > 1) { Console.Write("\nMDATA header at: {0}, frames: {1}, flags: {2}, interpolation: {3}", mdataaddr.ToString("X"), frames, animtype.ToString(), intmode.ToString()); } // Create motion stub NJS_MOTION mot = new NJS_MOTION(); mot.Name = "animation_" + section_addr.ToString("X8"); mot.MdataName = mot.Name + "_mdat"; mot.Frames = frames; mot.InterpolationMode = intmode; // Read the MDATA header and get the number of MDATA entries mot.ModelParts = BitConverter.ToInt32(file, mdataaddr) >> BitConverter.ToInt32(file, mdataaddr + 4); if (verbose > 1) { Console.WriteLine(", model parts: {0}", mot.ModelParts); } int tmpaddr = mdataaddr + 8; //Start of actual MDATA array for (int u = 0; u < mot.ModelParts; u++) { //Console.Write("\nMotion data {0}", u.ToString()); AnimModelData data = new AnimModelData(); if (animtype.HasFlag(AnimFlags.Position)) { uint posoff = ByteConverter.ToUInt32(file, tmpaddr + u * 16); data.PositionName = mot.Name + "_mkey_" + u.ToString() + "_pos_" + posoff.ToString("X8"); int pos_count = ByteConverter.ToInt32(file, tmpaddr + u * 16 + 8); //Console.Write(", position at: {0} ({1} entries)", posoff.ToString("X"), pos_count); for (int p = 0; p < pos_count; p++) { int index = ByteConverter.ToInt32(file, (int)posoff + p * 16); float pos_x = ByteConverter.ToSingle(file, (int)posoff + p * 16 + 4); float pos_y = ByteConverter.ToSingle(file, (int)posoff + p * 16 + 8); float pos_z = ByteConverter.ToSingle(file, (int)posoff + p * 16 + 12); //Console.WriteLine("\nAdded position index {3}: X: {0} Y: {1} Z: {2}", pos_x, pos_y, pos_z, index); data.Position.Add(index, new Vertex(pos_x, pos_y, pos_z)); } } if (animtype.HasFlag(AnimFlags.Rotation)) { uint rotoff = ByteConverter.ToUInt32(file, tmpaddr + u * 16 + 4); data.RotationName = mot.Name + "_mkey_" + u.ToString() + "_rot_" + rotoff.ToString("X8"); int rot_count = ByteConverter.ToInt32(file, tmpaddr + u * 16 + 12); //Console.Write(", rotation at: {0} ({1} entries)", rotoff.ToString("X"), rot_count); for (int p = 0; p < rot_count; p++) { int index = ByteConverter.ToInt32(file, (int)rotoff + p * 16); int rot_x = ByteConverter.ToInt32(file, (int)rotoff + p * 16 + 4); int rot_y = ByteConverter.ToInt32(file, (int)rotoff + p * 16 + 8); int rot_z = ByteConverter.ToInt32(file, (int)rotoff + p * 16 + 12); //Console.WriteLine("\nAdded rotation index {3}: X: {0} Y: {1} Z: {2}", rot_x, rot_y, rot_z, index); data.Rotation.Add(index, new Rotation(rot_x, rot_y, rot_z)); } } mot.Models.Add(u, data); } return(mot); } curaddr += 8 + section_size; }while (curaddr < file.Length); return(null); }
static byte[] GetSections(NJS_MOTION motion, int verbose = 0) { List <byte> result = new List <byte>(); int[] mkey_f_addr = new int[motion.ModelParts]; int[] mkey_a_addr = new int[motion.ModelParts]; int mkey_f_section = 0; int mkey_f_count = 0; // Get MKEY_F foreach (var anim in motion.Models) { AnimModelData model = anim.Value; if (model.Position != null && model.Position.Count > 0) { mkey_f_addr[anim.Key] = result.Count; if (verbose > 1) { Console.WriteLine("MKEY_F: {0} at {1}", anim.Key, result.Count.ToString("X")); } foreach (var item in model.Position) { result.AddRange(BitConverter.GetBytes(item.Key)); result.AddRange(BitConverter.GetBytes(item.Value.X)); result.AddRange(BitConverter.GetBytes(item.Value.Y)); result.AddRange(BitConverter.GetBytes(item.Value.Z)); mkey_f_count++; } } } if (mkey_f_count > 0) { result.InsertRange(mkey_f_section, BitConverter.GetBytes((uint)0)); result.InsertRange(mkey_f_section + 4, BitConverter.GetBytes((uint)mkey_f_count * 16)); } // Get MKEY_A int mkey_a_section = result.Count; int mkey_a_count = 0; foreach (var anim in motion.Models) { AnimModelData model = anim.Value; if (model.Rotation != null && model.Rotation.Count > 0) { mkey_a_addr[anim.Key] = result.Count; if (verbose > 1) { Console.WriteLine("MKEY_A: {0} at {1}", anim.Key, result.Count.ToString("X")); } foreach (var item in model.Rotation) { result.AddRange(BitConverter.GetBytes(item.Key)); result.AddRange(BitConverter.GetBytes(item.Value.X)); result.AddRange(BitConverter.GetBytes(item.Value.Y)); result.AddRange(BitConverter.GetBytes(item.Value.Z)); mkey_a_count++; } } } if (mkey_a_count > 0) { result.InsertRange(mkey_a_section, BitConverter.GetBytes((uint)1)); result.InsertRange(mkey_a_section + 4, BitConverter.GetBytes((uint)mkey_a_count * 16)); } // Get MDATA2 header int mdata_section = result.Count; for (int m = 0; m < motion.ModelParts; m++) { if (motion.Models.ContainsKey(m)) { AnimModelData model = motion.Models[m]; if (model.Position != null && model.Position.Count > 0) { result.AddRange(BitConverter.GetBytes((uint)(mkey_f_addr[m] + 8))); } else { result.AddRange(BitConverter.GetBytes((uint)0)); } if (model.Rotation != null && model.Rotation.Count > 0) { result.AddRange(BitConverter.GetBytes((uint)(mkey_a_addr[m] + 8))); } else { result.AddRange(BitConverter.GetBytes((uint)0)); } if (model.Position != null) { result.AddRange(BitConverter.GetBytes((uint)(model.Position.Count))); } else { result.AddRange(BitConverter.GetBytes((uint)0)); } if (model.Rotation != null) { result.AddRange(BitConverter.GetBytes((uint)(model.Rotation.Count))); } else { result.AddRange(BitConverter.GetBytes((uint)0)); } } else { result.AddRange(BitConverter.GetBytes((uint)0)); result.AddRange(BitConverter.GetBytes((uint)0)); result.AddRange(BitConverter.GetBytes((uint)0)); result.AddRange(BitConverter.GetBytes((uint)0)); } } result.InsertRange(mdata_section, BitConverter.GetBytes((uint)3)); result.InsertRange(mdata_section + 4, BitConverter.GetBytes((uint)(16 * motion.ModelParts + 8))); result.InsertRange(mdata_section + 8, BitConverter.GetBytes((uint)motion.ModelParts << 2)); result.InsertRange(mdata_section + 12, BitConverter.GetBytes((uint)2)); // Write motion header result.AddRange(BitConverter.GetBytes((uint)4)); result.AddRange(BitConverter.GetBytes((uint)12)); result.AddRange(BitConverter.GetBytes(mdata_section + 8)); result.AddRange(BitConverter.GetBytes(motion.Frames)); // It's MDATA2 so flags are hardcoded AnimFlags flags = 0; flags |= AnimFlags.Position; flags |= AnimFlags.Rotation; result.AddRange(BitConverter.GetBytes((short)flags)); result.AddRange(BitConverter.GetBytes((short)motion.InterpolationMode)); return(result.ToArray()); }
/// <summary> /// Exports a single level, model or animation file as text. /// </summary> /// <param name="source">Source pathname.</param> /// <param name="type">Type of text conversion.</param> /// <param name="destination">Destination pathname. Leave blank to export in the same folder with a swapped extension.</param> /// <param name="basicDX">Use the SADX2004 format for Basic models.</param> public static void ConvertFileToText(string source, TextType type, string destination = "", bool basicDX = true, bool overwrite = true) { string outext = ".c"; string extension = Path.GetExtension(source); switch (extension.ToLowerInvariant()) { case ".sa2lvl": case ".sa1lvl": if (type == TextType.CStructs) { if (destination == "") { destination = Path.Combine(Path.GetDirectoryName(source), Path.GetFileNameWithoutExtension(source) + outext); } if (!overwrite && File.Exists(destination)) { while (File.Exists(destination)) { destination = destination = Path.Combine(Path.GetDirectoryName(destination), Path.GetFileNameWithoutExtension(destination) + "_" + outext); } } LandTable land = LandTable.LoadFromFile(source); List <string> labels = new List <string>() { land.Name }; using (StreamWriter sw = File.CreateText(destination)) { sw.Write("/* Sonic Adventure "); LandTableFormat fmt = land.Format; switch (land.Format) { case LandTableFormat.SA1: case LandTableFormat.SADX: if (basicDX) { sw.Write("DX"); fmt = LandTableFormat.SADX; } else { sw.Write("1"); fmt = LandTableFormat.SA1; } break; case LandTableFormat.SA2: sw.Write("2"); fmt = LandTableFormat.SA2; break; case LandTableFormat.SA2B: sw.Write("2 Battle"); fmt = LandTableFormat.SA2B; break; } sw.WriteLine(" LandTable"); sw.WriteLine(" * "); sw.WriteLine(" * Generated by DataToolbox"); sw.WriteLine(" * "); if (!string.IsNullOrEmpty(land.Description)) { sw.Write(" * Description: "); sw.WriteLine(land.Description); sw.WriteLine(" * "); } if (!string.IsNullOrEmpty(land.Author)) { sw.Write(" * Author: "); sw.WriteLine(land.Author); sw.WriteLine(" * "); } sw.WriteLine(" */"); sw.WriteLine(); land.ToStructVariables(sw, fmt, labels, null); } } break; case ".sa1mdl": case ".sa2mdl": ModelFile modelFile = new ModelFile(source); NJS_OBJECT model = modelFile.Model; List <NJS_MOTION> animations = new List <NJS_MOTION>(modelFile.Animations); if (type == TextType.CStructs) { outext = ".c"; if (destination == "") { destination = Path.Combine(Path.GetDirectoryName(source), Path.GetFileNameWithoutExtension(source) + outext); } if (!overwrite && File.Exists(destination)) { while (File.Exists(destination)) { destination = destination = Path.Combine(Path.GetDirectoryName(destination), Path.GetFileNameWithoutExtension(destination) + "_" + outext); } } using (StreamWriter sw = File.CreateText(destination)) { sw.Write("/* NINJA "); switch (modelFile.Format) { case ModelFormat.Basic: case ModelFormat.BasicDX: if (basicDX) { sw.Write("Basic (with Sonic Adventure DX additions)"); } else { sw.Write("Basic"); } break; case ModelFormat.Chunk: sw.Write("Chunk"); break; case ModelFormat.GC: sw.Write("GC"); break; } sw.WriteLine(" model"); sw.WriteLine(" * "); sw.WriteLine(" * Generated by DataToolbox"); sw.WriteLine(" * "); if (modelFile != null) { if (!string.IsNullOrEmpty(modelFile.Description)) { sw.Write(" * Description: "); sw.WriteLine(modelFile.Description); sw.WriteLine(" * "); } if (!string.IsNullOrEmpty(modelFile.Author)) { sw.Write(" * Author: "); sw.WriteLine(modelFile.Author); sw.WriteLine(" * "); } } sw.WriteLine(" */"); sw.WriteLine(); List <string> labels_m = new List <string>() { model.Name }; model.ToStructVariables(sw, basicDX, labels_m, null); foreach (NJS_MOTION anim in animations) { anim.ToStructVariables(sw); } } } else if (type == TextType.NJA) { outext = ".nja"; if (destination == "") { destination = Path.Combine(Path.GetDirectoryName(source), Path.GetFileNameWithoutExtension(source) + outext); } if (!overwrite && File.Exists(destination)) { while (File.Exists(destination)) { destination = destination = Path.Combine(Path.GetDirectoryName(destination), Path.GetFileNameWithoutExtension(destination) + "_" + outext); } } using (StreamWriter sw2 = File.CreateText(destination)) { List <string> labels_nj = new List <string>() { model.Name }; model.ToNJA(sw2, basicDX, labels_nj, null); } } break; case ".saanim": NJS_MOTION animation = NJS_MOTION.Load(source); if (type == TextType.CStructs) { outext = ".c"; if (destination == "") { destination = Path.Combine(Path.GetDirectoryName(source), Path.GetFileNameWithoutExtension(source) + outext); } if (!overwrite && File.Exists(destination)) { while (File.Exists(destination)) { destination = destination = Path.Combine(Path.GetDirectoryName(destination), Path.GetFileNameWithoutExtension(destination) + "_" + outext); } } using (StreamWriter sw = File.CreateText(destination)) { sw.WriteLine("/* NINJA Motion"); sw.WriteLine(" * "); sw.WriteLine(" * Generated by DataToolbox"); sw.WriteLine(" * "); sw.WriteLine(" */"); sw.WriteLine(); animation.ToStructVariables(sw); } } else if (type == TextType.JSON) { outext = ".json"; if (destination == "") { destination = Path.Combine(Path.GetDirectoryName(source), Path.GetFileNameWithoutExtension(source) + outext); } if (!overwrite && File.Exists(destination)) { while (File.Exists(destination)) { destination = destination = Path.Combine(Path.GetDirectoryName(destination), Path.GetFileNameWithoutExtension(destination) + "_" + outext); } } JsonSerializer js = new JsonSerializer() { Culture = System.Globalization.CultureInfo.InvariantCulture }; using (TextWriter tw = File.CreateText(destination)) using (JsonTextWriter jtw = new JsonTextWriter(tw) { Formatting = Formatting.Indented }) js.Serialize(jtw, animation); } break; } }
static void Main(string[] args) { string[] arguments = Environment.GetCommandLineArgs(); string filename; bool dx = true; string outputfile; string extension; string dir = Environment.CurrentDirectory; if (args.Length == 0) { Console.WriteLine("Struct Exporter is a tool that lets you convert level, model and animation files to C structs."); Console.WriteLine("Usage: StructExporter <filename> [output path] [-nodx]\n"); Console.WriteLine("Arguments: -nodx to output Basic models without SADX additions\n"); Console.WriteLine("Supported file types: sa1lvl, sa2lvl, sa1mdl, sa2mdl, saanim\n"); Console.WriteLine("Examples:"); Console.WriteLine("StructExporter mylevel.sa1lvl"); Console.WriteLine("StructExporter mylevel.sa1lvl D:\\mylevel.c"); Console.WriteLine("StructExporter mymodel.sa1mdl D:\\mymodel.c -nodx\n"); Console.WriteLine("You can also drag your file onto StructExporter.exe to get it converted."); Console.WriteLine("Press ENTER to exit"); Console.ReadLine(); return; } //Args list: game, filename, key, type, address, [address2/count], [language], [name] filename = args[0]; outputfile = Path.GetFileNameWithoutExtension(filename) + ".c"; if (args.Length > 1) { if (args[args.Length - 1] == "-nodx") { dx = false; } if (args[1] != "-nodx") { outputfile = args[1]; } } byte[] file = File.ReadAllBytes(filename); extension = Path.GetExtension(filename); switch (extension) { case ".sa2lvl": case ".sa1lvl": LandTable land = LandTable.LoadFromFile(filename); List <string> labels = new List <string>() { land.Name }; using (StreamWriter sw = File.CreateText(outputfile)) { sw.Write("/* Sonic Adventure "); LandTableFormat fmt = land.Format; switch (land.Format) { case LandTableFormat.SA1: case LandTableFormat.SADX: if (dx) { sw.Write("DX"); fmt = LandTableFormat.SADX; } else { sw.Write("1"); fmt = LandTableFormat.SA1; } break; case LandTableFormat.SA2: sw.Write("2"); fmt = LandTableFormat.SA2; break; case LandTableFormat.SA2B: sw.Write("2 Battle"); fmt = LandTableFormat.SA2B; break; } sw.WriteLine(" LandTable"); sw.WriteLine(" * "); sw.WriteLine(" * Generated by StructExporter"); sw.WriteLine(" * "); if (!string.IsNullOrEmpty(land.Description)) { sw.Write(" * Description: "); sw.WriteLine(land.Description); sw.WriteLine(" * "); } if (!string.IsNullOrEmpty(land.Author)) { sw.Write(" * Author: "); sw.WriteLine(land.Author); sw.WriteLine(" * "); } sw.WriteLine(" */"); sw.WriteLine(); land.ToStructVariables(sw, fmt, labels, null); } break; case ".sa1mdl": case ".sa2mdl": ModelFile modelFile = new ModelFile(filename); NJS_OBJECT model = modelFile.Model; List <NJS_MOTION> animations = new List <NJS_MOTION>(modelFile.Animations); using (StreamWriter sw = File.CreateText(outputfile)) { sw.Write("/* NINJA "); switch (modelFile.Format) { case ModelFormat.Basic: case ModelFormat.BasicDX: if (dx) { sw.Write("Basic (with Sonic Adventure DX additions)"); } else { sw.Write("Basic"); } break; case ModelFormat.Chunk: sw.Write("Chunk"); break; case ModelFormat.GC: sw.Write("GC"); break; } sw.WriteLine(" model"); sw.WriteLine(" * "); sw.WriteLine(" * Generated by StructExporter"); sw.WriteLine(" * "); if (modelFile != null) { if (!string.IsNullOrEmpty(modelFile.Description)) { sw.Write(" * Description: "); sw.WriteLine(modelFile.Description); sw.WriteLine(" * "); } if (!string.IsNullOrEmpty(modelFile.Author)) { sw.Write(" * Author: "); sw.WriteLine(modelFile.Author); sw.WriteLine(" * "); } } sw.WriteLine(" */"); sw.WriteLine(); List <string> labels_m = new List <string>() { model.Name }; model.ToStructVariables(sw, dx, labels_m, null); foreach (NJS_MOTION anim in animations) { anim.ToStructVariables(sw); } } break; case ".saanim": NJS_MOTION animation = NJS_MOTION.Load(filename); using (StreamWriter sw = File.CreateText(outputfile)) { sw.WriteLine("/* NINJA Motion"); sw.WriteLine(" * "); sw.WriteLine(" * Generated by StructExporter"); sw.WriteLine(" * "); sw.WriteLine(" */"); sw.WriteLine(); animation.ToStructVariables(sw); break; } } }
public static LevelAnim ImportLevelAnimation(NJS_OBJECT objm, NJS_MOTION mot, EditorItemSelection selectionManager) { objm.ProcessVertexData(); StateChanged(); return(new LevelAnim(new GeoAnimData(objm, mot), levelAnims.Count, selectionManager)); }
static void Main(string[] args) { bool nometa = false; bool nolabel = false; string mode; string fullpath_out; bool bigendian = false; List <string> mdlanimfiles; if (args.Length == 0) { Console.WriteLine("Split any binary files supported by SA Tools.\n"); Console.WriteLine("Usage:\n"); Console.WriteLine("-Splitting binary files with INI data-"); Console.WriteLine("split binary <file> <inifile> [output path] [-nometa] [-nolabel]\n"); Console.WriteLine("-Splitting SA1/SADX NB files-"); Console.WriteLine("split nb <file> [output path] -ini [split INI file]\n"); Console.WriteLine("-Splitting SA2 MDL files-"); Console.WriteLine("split mdl <file> [output path] -anim [animation files]\n"); Console.WriteLine("-Splitting SA2B MDL files-"); Console.WriteLine("split mdl_b <file> [output path] -anim [animation files]\n"); Console.WriteLine("-Splitting dllexport entries from DLL files-"); Console.WriteLine("split dllexport <file> <type> <name> [output path] [-p numparts]\n"); Console.WriteLine("Press ENTER to exit."); Console.ReadLine(); return; } #if DEBUG if (SplitExtensions(args) == true) { return; } #endif for (int u = 2; u < args.Length; u++) { if (args[u] == "-nometa") { nometa = true; } if (args[u] == "-nolabel") { nolabel = true; } } mode = args[0]; switch (mode.ToLowerInvariant()) { case "binary": string fullpath_bin = Path.GetFullPath(args[1]); if (!File.Exists(fullpath_bin)) { Console.WriteLine("File {0} doesn't exist.", fullpath_bin); return; } Console.WriteLine("File: {0}", fullpath_bin); string fullpath_ini = Path.GetFullPath(args[2]); if (!File.Exists(fullpath_ini)) { Console.WriteLine("File {0} doesn't exist.", fullpath_ini); return; } Console.WriteLine("Data mapping: {0}", fullpath_ini); fullpath_out = Path.GetDirectoryName(fullpath_bin); if (args.Length > 3) { fullpath_out = args[3]; if (fullpath_out[fullpath_out.Length - 1] != '/') { fullpath_out = string.Concat(fullpath_out, '/'); } fullpath_out = Path.GetFullPath(fullpath_out); } Console.WriteLine("Output folder: {0}", fullpath_out); if (nometa) { Console.WriteLine("Labels are disabled"); } if (Path.GetExtension(args[1]).ToLowerInvariant() == ".dll") { SA_Tools.SplitDLL.SplitDLL.SplitDLLFile(fullpath_bin, fullpath_ini, fullpath_out, nometa, nolabel); } else { SA_Tools.Split.Split.SplitFile(fullpath_bin, fullpath_ini, fullpath_out, nometa, nolabel); } break; case "nb": case "nb_b": string fullpath_nb = Path.GetFullPath(args[1]); string path_ini = null; if (args[args.Length - 2].ToLowerInvariant() == "-ini") { path_ini = Path.GetFullPath(args[args.Length - 1]); } if (!File.Exists(fullpath_nb)) { Console.WriteLine("File {0} doesn't exist.", fullpath_nb); return; } Console.WriteLine("File: {0}", fullpath_nb); fullpath_out = Path.GetDirectoryName(fullpath_nb); if (args.Length > 2) { fullpath_out = args[2]; if (fullpath_out[fullpath_out.Length - 1] != '/') { fullpath_out = string.Concat(fullpath_out, '/'); } fullpath_out = Path.GetFullPath(fullpath_out); } Console.WriteLine("Output folder: {0}", fullpath_out); SA_Tools.Split.SplitNB.SplitNBFile(fullpath_nb, false, fullpath_out, 1, path_ini); break; case "mdl": case "mdl_b": string fullpath_mdl = Path.GetFullPath(args[1]); if (!File.Exists(fullpath_mdl)) { Console.WriteLine("File {0} doesn't exist.", fullpath_mdl); return; } Console.Write("File: {0}", fullpath_mdl); if (mode == "mdl_b") { bigendian = true; Console.Write(" (Big Endian)\n"); } else { Console.Write(System.Environment.NewLine); } fullpath_out = Path.GetDirectoryName(fullpath_mdl); if (args.Length > 2) { fullpath_out = args[2]; if (fullpath_out[fullpath_out.Length - 1] != '/') { fullpath_out = string.Concat(fullpath_out, '/'); } fullpath_out = Path.GetFullPath(fullpath_out); } Console.WriteLine("Output path: {0}", fullpath_out); if (args.Length > 3) { mdlanimfiles = new List <string>(); Console.WriteLine("Animation files:"); for (int u = 3; u < args.Length; u++) { string animpath = Path.GetFullPath(args[u]); if (File.Exists(animpath)) { mdlanimfiles.Add(animpath); Console.WriteLine(animpath); } else { Console.WriteLine("File {0} doesn't exist.", animpath); } } SA_Tools.SAArc.sa2MDL.Split(bigendian, fullpath_mdl, fullpath_out, mdlanimfiles.ToArray()); } else { SA_Tools.SAArc.sa2MDL.Split(bigendian, fullpath_mdl, fullpath_out, null); } break; case "dllexport": string fullpath_dllex = Path.GetFullPath(args[1]); string type = args[2]; string name = args[3]; string fileOutputPath = ""; if (args.Length > 4) { for (int i = 4; i < args.Length; i++) { if (args[i].Substring(0, 1) != "-" && args[i - 1].Substring(0, 1) != "-") { fileOutputPath = args[i]; } } } if (!File.Exists(fullpath_dllex)) { Console.WriteLine("File {0} doesn't exist.", fullpath_dllex); return; } Console.Write("File: {0}", fullpath_dllex); byte[] datafile = File.ReadAllBytes(fullpath_dllex); uint imageBase = SA_Tools.HelperFunctions.SetupEXE(ref datafile).Value; Dictionary <string, int> exports; Dictionary <int, string> labels = new Dictionary <int, string>(); { 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 namex = datafile.GetCString(BitConverter.ToInt32(datafile, nameaddr), System.Text.Encoding.ASCII); int addr = BitConverter.ToInt32(datafile, dir.AddressOfFunctions + (BitConverter.ToInt16(datafile, ordaddr) * 4)); exports.Add(namex, addr); labels.Add(addr, namex); nameaddr += 4; ordaddr += 2; } Console.Write(" ({0} exports)\n", exports.Count); } if (!exports.ContainsKey(name)) { Console.WriteLine("The export table has no item named {0}", name); return; } int address = exports[name]; Console.WriteLine("{0} {1}:{2}", type, name, address.ToString("X8")); switch (type) { // Landtables case "landtable": case "sa1landtable": case "sadxlandtable": case "sa2landtable": case "sa2blandtable": case "battlelandtable": LandTableFormat landfmt_cur; string landext; switch (type) { case "sa1landtable": landfmt_cur = LandTableFormat.SA1; landext = ".sa1lvl"; break; case "sadxlandtable": landfmt_cur = LandTableFormat.SADX; landext = ".sa1lvl"; break; case "sa2landtable": landfmt_cur = LandTableFormat.SA2; landext = ".sa2lvl"; break; case "sa2blandtable": case "battlelandtable": landfmt_cur = LandTableFormat.SA2B; landext = ".sa2blvl"; break; case "landtable": default: landfmt_cur = LandTableFormat.SADX; landext = ".sa1lvl"; break; } LandTable land = new LandTable(datafile, address, imageBase, landfmt_cur, labels); if (fileOutputPath == "") { fileOutputPath = land.Name + landext; } if (!Directory.Exists(Path.GetDirectoryName(fileOutputPath))) { Directory.CreateDirectory(Path.GetDirectoryName(fileOutputPath)); } land.SaveToFile(fileOutputPath, landfmt_cur, nometa); break; // NJS_OBJECT case "model": case "object": case "basicmodel": case "basicdxmodel": case "chunkmodel": case "gcmodel": { ModelFormat modelfmt_obj; string modelext; switch (type) { case "basicmodel": modelfmt_obj = ModelFormat.Basic; modelext = ".sa1mdl"; break; case "basicdxmodel": modelfmt_obj = ModelFormat.BasicDX; modelext = ".sa1mdl"; break; case "chunkmodel": modelfmt_obj = ModelFormat.Chunk; modelext = ".sa2mdl"; break; case "gcmodel": modelfmt_obj = ModelFormat.GC; modelext = ".sa2bmdl"; break; default: modelfmt_obj = ModelFormat.BasicDX; modelext = ".sa1mdl"; break; } NJS_OBJECT mdl = new NJS_OBJECT(datafile, address, imageBase, modelfmt_obj, labels, new Dictionary <int, Attach>()); if (fileOutputPath == "") { fileOutputPath = mdl.Name + modelext; } if (!Directory.Exists(Path.GetDirectoryName(fileOutputPath))) { Directory.CreateDirectory(Path.GetDirectoryName(fileOutputPath)); } ModelFile.CreateFile(fileOutputPath, mdl, null, null, null, null, modelfmt_obj, nometa); } break; // NJS_MOTION case "animation": case "motion": int numparts = 0; for (int a = 3; a < args.Length; a++) { if (args[a] == "-p") { numparts = int.Parse(args[a + 1], System.Globalization.NumberStyles.Integer); } } NJS_MOTION ani = new NJS_MOTION(datafile, address, imageBase, numparts, labels); if (fileOutputPath == "") { fileOutputPath = ani.Name + ".saanim"; } string outpath = Path.GetDirectoryName(Path.GetFullPath(fileOutputPath)); Console.WriteLine("Output file: {0}", Path.GetFullPath(fileOutputPath)); if (!Directory.Exists(outpath)) { Directory.CreateDirectory(outpath); } ani.Save(fileOutputPath, nometa); break; default: Console.WriteLine("Unrecognized export type {0}", type); break; } break; default: Console.WriteLine("Incorrect mode specified. Press ENTER to exit."); Console.ReadLine(); return; } }
public override void ProcessShapeMotionVertexData(NJS_MOTION motion, int frame, int animindex) { if (!motion.Models.ContainsKey(animindex)) { ProcessVertexData(); return; } Vertex[] vertdata = Vertex; Vertex[] normdata = Normal; AnimModelData data = motion.Models[animindex]; if (data.Vertex.Count > 0) { vertdata = data.GetVertex(frame); } if (data.Normal.Count > 0) { normdata = data.GetNormal(frame); } List <MeshInfo> result = new List <MeshInfo>(); foreach (NJS_MESHSET mesh in Mesh) { bool hasVColor = mesh.VColor != null; bool hasUV = mesh.UV != null; List <Poly> polys = new List <Poly>(); List <VertexData> verts = new List <VertexData>(); int currentstriptotal = 0; foreach (Poly poly in mesh.Poly) { Poly newpoly = null; switch (mesh.PolyType) { case Basic_PolyType.Triangles: newpoly = new Triangle(); break; case Basic_PolyType.Quads: newpoly = new Quad(); break; case Basic_PolyType.NPoly: case Basic_PolyType.Strips: newpoly = new Strip(poly.Indexes.Length, ((Strip)poly).Reversed); break; } for (int i = 0; i < poly.Indexes.Length; i++) { newpoly.Indexes[i] = (ushort)verts.Count; verts.Add(new VertexData( vertdata[poly.Indexes[i]], normdata[poly.Indexes[i]], hasVColor ? (Color?)mesh.VColor[currentstriptotal] : null, hasUV ? mesh.UV[currentstriptotal++] : null)); } polys.Add(newpoly); } NJS_MATERIAL mat = null; if (Material != null && mesh.MaterialID < Material.Count) { mat = Material[mesh.MaterialID]; } result.Add(new MeshInfo(mat, polys.ToArray(), verts.ToArray(), hasUV, hasVColor)); } MeshInfo = result.ToArray(); }
public MotionInfo(string fn, NJS_MOTION mtn) { Filename = fn; Motion = mtn; }
public static void SplitNBFile(string filename, bool extractchunks, string outdir, int verbose = 0, string inifilename = null, bool overwrite = true) { Dictionary <int, string> sectionlist = new Dictionary <int, string>(); Dictionary <int, NJS_OBJECT> modellist = new Dictionary <int, NJS_OBJECT>(); Dictionary <int, NJS_MOTION> animlist = new Dictionary <int, NJS_MOTION>(); byte[] file = File.ReadAllBytes(filename); if (file.Length == 180920) // Patch in unused rotation for E101R, required for rebuilding with correct pointers { file[129896] = 0xE0; file[129897] = 0x0A; file[129904] = 0x02; } if (BitConverter.ToInt32(file, 0) != 0x04424A4E) { Console.WriteLine("Invalid NB file."); return; } if (!Directory.Exists(outdir)) { Directory.CreateDirectory(outdir); } Environment.CurrentDirectory = outdir; int numfiles = BitConverter.ToInt16(file, 4); Dictionary <int, string> splitfilenames = new Dictionary <int, string>(); if (inifilename != null) { inifilename = Path.GetFullPath(inifilename); } if (File.Exists(inifilename)) { splitfilenames = IniSerializer.Deserialize <Dictionary <int, string> >(inifilename); if (verbose > 0) { Console.WriteLine("Split INI: {0}", inifilename); } } else { if (verbose > 0) { Console.WriteLine("Split INI {0} not found!", inifilename); } for (int i = 0; i < numfiles; i++) { splitfilenames[i] = i.ToString("D2"); } } int curaddr = 8; for (int i = 0; i < numfiles; i++) { ushort type = BitConverter.ToUInt16(file, curaddr); byte[] chunk = new byte[BitConverter.ToInt32(file, curaddr + 4)]; Array.Copy(file, curaddr + 8, chunk, 0, chunk.Length); switch (type) { case 0: if (verbose > 0) { Console.WriteLine("\nSection {0} at {1} is empty", i.ToString("D2", NumberFormatInfo.InvariantInfo), curaddr.ToString("X")); } sectionlist.Add(i, "NULL"); break; case 1: if (verbose > 0) { Console.WriteLine("\nSection {0} at {1} is a model", i.ToString("D2", NumberFormatInfo.InvariantInfo), curaddr.ToString("X")); } if (extractchunks) { File.WriteAllBytes(i.ToString("D2", NumberFormatInfo.InvariantInfo) + ".bin", chunk); } NJS_OBJECT mdl = ProcessModel(chunk, verbose, curaddr + 8); //if (extractchunks) File.WriteAllBytes(i.ToString("D2", NumberFormatInfo.InvariantInfo) + "_p.bin", GetSections(mdl)); // Assume there is no description/texture for SAMDL project mode string[] metadata = new string[0]; string outFilename = splitfilenames[i]; if (splitfilenames[i].Contains("|")) { metadata = splitfilenames[i].Split('|'); // Filename|Description|Texture file outFilename = metadata[0]; } string outResult = outFilename + ".sa1mdl"; modellist.Add(i, mdl); if (metadata.Length > 1) { outResult += ("|" + metadata[1]); } if (metadata.Length > 2) { outResult += ("|" + metadata[2]); } sectionlist.Add(i, outResult); break; case 3: if (verbose > 0) { Console.WriteLine("\nSection {0} at {1} is a motion", i.ToString("D2", NumberFormatInfo.InvariantInfo), curaddr.ToString("X")); } if (extractchunks) { File.WriteAllBytes(i.ToString("D2", NumberFormatInfo.InvariantInfo) + ".bin", chunk); } string desc = null; if (splitfilenames[i].Contains("|")) { metadata = splitfilenames[i].Split('|'); // Filename|Description outFilename = metadata[0]; desc = metadata[1]; } else { outFilename = splitfilenames[i]; } NJS_MOTION mot = ProcessMotion(chunk, verbose, curaddr + 8); mot.Description = desc; //if (extractchunks) File.WriteAllBytes(i.ToString("D2", NumberFormatInfo.InvariantInfo) + "_p.bin", GetSections(mot)); animlist.Add(i, mot); sectionlist.Add(i, outFilename + ".saanim"); break; default: if (verbose > 0) { Console.WriteLine("\nSection {0} at {1} is an unknown type", i.ToString("D2", NumberFormatInfo.InvariantInfo), curaddr.ToString("X")); } if (extractchunks) { File.WriteAllBytes(i.ToString("D2", NumberFormatInfo.InvariantInfo) + ".bin", chunk); } sectionlist.Add(i, splitfilenames[i] + ".wtf"); break; } curaddr += chunk.Length + 8; } // Save models and animations foreach (var modelitem in modellist) { // If the filename field contains description/texture, split it string filenameString = splitfilenames[modelitem.Key]; if (splitfilenames[modelitem.Key].Contains("|")) { string[] filenameSplit = filenameString.Split('|'); filenameString = filenameSplit[0]; } List <string> anims = new List <string>(); foreach (var animitem in animlist) { string filenameStringAnim = splitfilenames[animitem.Key]; if (splitfilenames[animitem.Key].Contains("|")) { string[] filenameSplitAnim = filenameStringAnim.Split('|'); filenameStringAnim = filenameSplitAnim[0]; } if (modelitem.Value.CountAnimated() == animitem.Value.ModelParts) { if (File.Exists(Path.Combine(outdir, filenameStringAnim + ".saanim")) && !overwrite) { return; } if (!Directory.Exists(Path.GetDirectoryName(filenameStringAnim + ".saanim")) && Path.GetDirectoryName(filenameStringAnim + ".saanim") != "") { Directory.CreateDirectory(Path.GetDirectoryName(filenameStringAnim + ".saanim")); } animitem.Value.Save(filenameStringAnim + ".saanim"); System.Text.StringBuilder sb = new System.Text.StringBuilder(1024); PathRelativePathTo(sb, Path.GetFullPath(Path.Combine(outdir, filenameStringAnim + ".saanim")), 0, Path.GetFullPath(filenameStringAnim + ".saanim"), 0); anims.Add(sb.ToString()); } } if (File.Exists(Path.Combine(outdir, filenameString + ".sa1mdl")) && !overwrite) { return; } if (!Directory.Exists(Path.GetDirectoryName(filenameString + ".sa1mdl")) && Path.GetDirectoryName(filenameString + ".sa1mdl") != "") { Directory.CreateDirectory(Path.GetDirectoryName(filenameString + ".sa1mdl")); } ModelFile.CreateFile(filenameString + ".sa1mdl", modelitem.Value, anims.ToArray(), null, null, null, ModelFormat.Basic); } string sectionListFilename = Path.GetFileNameWithoutExtension(filename) + ".ini"; if (inifilename != null) { sectionListFilename = Path.GetFileNameWithoutExtension(inifilename) + "_data.ini"; } IniSerializer.Serialize(sectionlist, Path.Combine(outdir, sectionListFilename)); }
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>(); 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 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, 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": { 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; } 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); }
public static void Build(string filename) { nodenames.Clear(); modelfiles.Clear(); motionfiles.Clear(); byte[] fc; if (Path.GetExtension(filename).Equals(".prs", StringComparison.OrdinalIgnoreCase)) { fc = Prs.Decompress(filename); } else { fc = File.ReadAllBytes(filename); } string path = Path.Combine(Path.GetDirectoryName(Path.GetFullPath(filename)), Path.GetFileNameWithoutExtension(filename)); JsonSerializer js = new JsonSerializer(); EventIniData ini; using (TextReader tr = File.OpenText(Path.Combine(path, Path.ChangeExtension(Path.GetFileName(filename), ".json")))) using (JsonTextReader jtr = new JsonTextReader(tr)) ini = js.Deserialize <EventIniData>(jtr); uint key; bool battle = ini.Game == Game.SA2B; bool battlebeta = ini.Game == Game.SA2B; bool dcbeta = ini.Game == Game.SA2; if (fc[0] == 0x81) { if (fc[0x2B] <= 1 && fc[0x2A] == 0) { ByteConverter.BigEndian = true; key = 0x8125FE60; battle = true; } else { ByteConverter.BigEndian = true; key = 0x812FFE60; battlebeta = true; } } else { if ((fc[37] == 0x25) || (fc[38] == 0x22) || ((fc[36] == 0) && ((fc[1] == 0xFE) || (fc[1] == 0xF2) || ((fc[1] == 0x27) && fc[2] == 0x9F)))) { ByteConverter.BigEndian = false; key = 0xC600000; dcbeta = true; } else { ByteConverter.BigEndian = false; key = 0xC600000; } } List <byte> modelbytes = new List <byte>(fc); Dictionary <string, uint> labels = new Dictionary <string, uint>(); foreach (string file in ini.Files.Where(a => a.Key.EndsWith(".sa2mdl", StringComparison.OrdinalIgnoreCase) && HelperFunctions.FileHash(Path.Combine(path, a.Key)) != a.Value).Select(a => a.Key)) { modelbytes.AddRange(new ModelFile(Path.Combine(path, file)).Model.GetBytes((uint)(key + modelbytes.Count), false, labels, new List <uint>(), out uint _)); } if (battle) { foreach (string file in ini.Files.Where(a => a.Key.EndsWith(".sa2bmdl", StringComparison.OrdinalIgnoreCase) && HelperFunctions.FileHash(Path.Combine(path, a.Key)) != a.Value).Select(a => a.Key)) { modelbytes.AddRange(new ModelFile(Path.Combine(path, file)).Model.GetBytes((uint)(key + modelbytes.Count), false, labels, new List <uint>(), out uint _)); } List <byte> motionbytes = new List <byte>(new byte[(ini.Motions.Count + 1) * 8]); Dictionary <string, int> partcounts = new Dictionary <string, int>(ini.Motions.Count); foreach (string file in ini.Files.Where(a => a.Key.EndsWith(".saanim", StringComparison.OrdinalIgnoreCase)).Select(a => a.Key)) { NJS_MOTION motion = NJS_MOTION.Load(Path.Combine(path, file)); motionbytes.AddRange(motion.GetBytes((uint)motionbytes.Count, labels, out uint _)); partcounts.Add(motion.Name, motion.ModelParts); } byte[] mfc = motionbytes.ToArray(); for (int i = 0; i < ini.Motions.Count; i++) { if (ini.Motions[i] == null) { new byte[] { 0xFF, 0xFF, 0xFF, 0xFF } }
public static void ExportCPP(DllIniData IniData, Dictionary <string, bool> itemsToExport, string fileName) { using (TextWriter writer = File.CreateText(fileName)) { bool SA2 = IniData.Game == SA_Tools.SplitDLL.Game.SA2B; ModelFormat modelfmt = SA2 ? ModelFormat.Chunk : ModelFormat.BasicDX; LandTableFormat landfmt = SA2 ? LandTableFormat.SA2 : LandTableFormat.SADX; writer.WriteLine("// Generated by SA Tools DLL Mod Generator"); writer.WriteLine(); if (SA2) { writer.WriteLine("#include \"SA2ModLoader.h\""); } else { writer.WriteLine("#include \"SADXModLoader.h\""); } writer.WriteLine(); List <string> labels = new List <string>(); Dictionary <string, uint> texlists = new Dictionary <string, uint>(); foreach (KeyValuePair <string, FileTypeHash> item in IniData.Files.Where(i => itemsToExport[i.Key])) { switch (item.Value.Type) { case "landtable": LandTable tbl = LandTable.LoadFromFile(item.Key); texlists.Add(tbl.Name, tbl.TextureList); tbl.ToStructVariables(writer, landfmt, new List <string>()); labels.AddRange(tbl.GetLabels()); break; case "model": NJS_OBJECT mdl = new ModelFile(item.Key).Model; mdl.ToStructVariables(writer, modelfmt == ModelFormat.BasicDX, new List <string>()); labels.AddRange(mdl.GetLabels()); break; case "basicmodel": case "chunkmodel": case "gcmodel": mdl = new ModelFile(item.Key).Model; mdl.ToStructVariables(writer, false, new List <string>()); labels.AddRange(mdl.GetLabels()); break; case "basicdxmodel": mdl = new ModelFile(item.Key).Model; mdl.ToStructVariables(writer, true, new List <string>()); labels.AddRange(mdl.GetLabels()); break; case "animation": NJS_MOTION ani = NJS_MOTION.Load(item.Key); ani.ToStructVariables(writer); labels.Add(ani.Name); break; } writer.WriteLine(); } foreach (var item in IniData.DataItems.Where(i => itemsToExport[i.Filename])) { switch (item.Type) { case "animindexlist": { SortedDictionary <short, NJS_MOTION> anims = new SortedDictionary <short, NJS_MOTION>(); foreach (string file in Directory.GetFiles(item.Filename, "*.saanim")) { if (short.TryParse(Path.GetFileNameWithoutExtension(file), NumberStyles.Integer, NumberFormatInfo.InvariantInfo, out short i)) { anims.Add(i, NJS_MOTION.Load(file)); } } foreach (KeyValuePair <short, NJS_MOTION> obj in anims) { obj.Value.ToStructVariables(writer); writer.WriteLine(); } writer.WriteLine("AnimationIndex {0}[] = {{", item.Export); List <string> objs = new List <string>(anims.Count); foreach (KeyValuePair <short, NJS_MOTION> obj in anims) { objs.Add($"{{ {obj.Key}, {obj.Value.ModelParts}, &{obj.Value.Name} }}"); } objs.Add("{ -1 }"); writer.WriteLine("\t" + string.Join("," + Environment.NewLine + "\t", objs.ToArray())); writer.WriteLine("};"); } break; case "charaobjectdatalist": { foreach (string file in Directory.GetFiles(item.Filename, "*.sa2mdl")) { new ModelFile(file).Model.ToStructVariables(writer, false, new List <string>()); writer.WriteLine(); } foreach (string file in Directory.GetFiles(item.Filename, "*.saanim")) { NJS_MOTION.Load(file).ToStructVariables(writer); writer.WriteLine(); } var data = IniSerializer.Deserialize <CharaObjectData[]>(Path.Combine(item.Filename, "info.ini")); writer.WriteLine("CharaObjectData {0}[] = {{", item.Export); List <string> objs = new List <string>(data.Length); foreach (var obj in data) { objs.Add(obj.ToStruct()); } writer.WriteLine("\t" + string.Join("," + Environment.NewLine + "\t", objs.ToArray())); writer.WriteLine("};"); } break; case "kartspecialinfolist": { foreach (string file in Directory.GetFiles(item.Filename, "*.sa2mdl")) { new ModelFile(file).Model.ToStructVariables(writer, false, new List <string>()); writer.WriteLine(); } var data = IniSerializer.Deserialize <KartSpecialInfo[]>(Path.Combine(item.Filename, "info.ini")); writer.WriteLine("KartSpecialInfo {0}[] = {{", item.Export); List <string> objs = new List <string>(data.Length); for (int i = 0; i < data.Length; i++) { KartSpecialInfo obj = data[i]; objs.Add(obj.ToStruct()); texlists.Add($"{item.Export}[{i}]", obj.TexList); } writer.WriteLine("\t" + string.Join("," + Environment.NewLine + "\t", objs.ToArray())); writer.WriteLine("};"); } break; case "kartmodelsarray": { foreach (string file in Directory.GetFiles(item.Filename, "*.sa2bmdl")) { new ModelFile(file).Model.ToStructVariables(writer, false, new List <string>()); writer.WriteLine(); } foreach (string file in Directory.GetFiles(item.Filename, "*.sa1mdl")) { new ModelFile(file).Model.ToStructVariables(writer, false, new List <string>()); writer.WriteLine(); } var data = IniSerializer.Deserialize <CharaObjectData[]>(Path.Combine(item.Filename, "info.ini")); writer.WriteLine("KartModelsArray {0}[] = {{", item.Export); List <string> objs = new List <string>(data.Length); foreach (var obj in data) { objs.Add(obj.ToStruct()); } writer.WriteLine("\t" + string.Join("," + Environment.NewLine + "\t", objs.ToArray())); writer.WriteLine("};"); } break; case "motiontable": { foreach (string file in Directory.GetFiles(item.Filename, "*.saanim")) { NJS_MOTION.Load(file).ToStructVariables(writer); writer.WriteLine(); } var data = IniSerializer.Deserialize <MotionTableEntry[]>(Path.Combine(item.Filename, "info.ini")); writer.WriteLine("MotionTableEntry {0}[] = {{", item.Export); List <string> objs = new List <string>(data.Length); foreach (var obj in data) { objs.Add(obj.ToStruct()); } writer.WriteLine("\t" + string.Join("," + Environment.NewLine + "\t", objs.ToArray())); writer.WriteLine("};"); } break; } } writer.WriteLine("extern \"C\" __declspec(dllexport) void __cdecl Init(const char *path, const HelperFunctions &helperFunctions)"); writer.WriteLine("{"); writer.WriteLine("\tHMODULE handle = GetModuleHandle(L\"{0}\");", IniData.Name); List <string> exports = new List <string>(IniData.Items.Where(item => labels.Contains(item.Label)).Select(item => item.Export).Distinct()); foreach (KeyValuePair <string, string> item in IniData.Exports.Where(item => exports.Contains(item.Key))) { writer.WriteLine("\t{0}{1} = ({0})GetProcAddress(handle, \"{1}\");", typemap[item.Value], item.Key); } foreach (DllItemInfo item in IniData.Items.Where(item => labels.Contains(item.Label))) { if (typemap[IniData.Exports[item.Export]].EndsWith("**")) { writer.WriteLine("\t{0} = &{1};", item.ToString(), item.Label); } else { writer.WriteLine("\t*{0} = {1};", item.ToString(), item.Label); } } foreach (var item in IniData.DataItems.Where(item => itemsToExport[item.Filename])) { switch (item.Type) { case "animindexlist": case "charaobjectdatalist": case "kartspecialinfolist": case "kartmodelsarray": writer.WriteLine("\tHookExport(handle, \"{0}\", {0});", item.Export); break; default: writer.WriteLine("\t{0}{1}_exp = ({0})GetProcAddress(handle, \"{1}\");", typemap[item.Type], item.Export); writer.WriteLine("\t*{0}_exp = {0};", item.Export); break; } } if (texlists.Count > 0 && IniData.TexLists != null && IniData.TexLists.Items != null) { exports = new List <string>(IniData.TexLists.Where(item => texlists.Values.Contains(item.Key)).Select(item => item.Value.Export).Distinct()); foreach (KeyValuePair <string, string> item in IniData.Exports.Where(item => exports.Contains(item.Key))) { writer.WriteLine("\t{0}{1} = ({0})GetProcAddress(handle, \"{1}\");", typemap[item.Value], item.Key); } foreach (KeyValuePair <string, uint> item in texlists.Where(item => IniData.TexLists.ContainsKey(item.Value))) { DllTexListInfo tex = IniData.TexLists[item.Value]; string str; if (tex.Index.HasValue) { str = $"{tex.Export}[{tex.Index.Value}]"; } else { str = tex.Export; } writer.WriteLine("\t{0}.TexList = {1};", item.Key, str); } } writer.WriteLine("}"); writer.WriteLine(); writer.WriteLine("extern \"C\" __declspec(dllexport) const ModInfo {0}ModInfo = {{ ModLoaderVer }};", SA2 ? "SA2" : "SADX"); } }
public override void ProcessShapeMotionVertexData(NJS_MOTION motion, float frame, int animindex) { throw new NotImplementedException(); }
public abstract void ProcessShapeMotionVertexData(NJS_MOTION motion, int frame, int animindex);