public NJS_OBJECT Clone() { NJS_OBJECT result = (NJS_OBJECT)MemberwiseClone(); if (Attach != null) { result.Attach = Attach.Clone(); } result.Position = Position.Clone(); result.Rotation = Rotation.Clone(); result.Scale = Scale.Clone(); result.children = new List <NJS_OBJECT>(children.Count); result.Children = new ReadOnlyCollection <NJS_OBJECT>(result.children); if (children.Count > 0) { NJS_OBJECT child = children[0].Clone(); while (child != null) { result.children.Add(child); child = child.Sibling; } } if (Sibling != null) { result.Sibling = Sibling.Clone(); } return(result); }
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 NJS_OBJECT(byte[] file, int address, uint imageBase, ModelFormat format, NJS_OBJECT parent, Dictionary <int, string> labels, Dictionary <int, Attach> attaches) { if (labels.ContainsKey(address)) { Name = labels[address]; } else { Name = "object_" + address.ToString("X8"); } ObjectFlags flags = (ObjectFlags)ByteConverter.ToInt32(file, address); RotateZYX = (flags & ObjectFlags.RotateZYX) == ObjectFlags.RotateZYX; Animate = (flags & ObjectFlags.NoAnimate) == 0; Morph = (flags & ObjectFlags.NoMorph) == 0; int tmpaddr = ByteConverter.ToInt32(file, address + 4); if (tmpaddr != 0) { tmpaddr = (int)unchecked ((uint)tmpaddr - imageBase); if (attaches != null && attaches.ContainsKey(tmpaddr)) { Attach = attaches[tmpaddr]; } else { Attach = Attach.Load(file, tmpaddr, imageBase, format, labels); attaches.Add(tmpaddr, Attach); } } Position = new Vertex(file, address + 8); Rotation = new Rotation(file, address + 0x14); Scale = new Vertex(file, address + 0x20); Parent = parent; children = new List <NJS_OBJECT>(); Children = new ReadOnlyCollection <NJS_OBJECT>(children); NJS_OBJECT child = null; tmpaddr = ByteConverter.ToInt32(file, address + 0x2C); if (tmpaddr != 0) { tmpaddr = (int)unchecked ((uint)tmpaddr - imageBase); child = new NJS_OBJECT(file, tmpaddr, imageBase, format, this, labels, attaches); } while (child != null) { children.Add(child); child = child.Sibling; } tmpaddr = ByteConverter.ToInt32(file, address + 0x30); if (tmpaddr != 0) { tmpaddr = (int)unchecked ((uint)tmpaddr - imageBase); Sibling = new NJS_OBJECT(file, tmpaddr, imageBase, format, parent, labels, attaches); } //Assimp.AssimpContext context = new AssimpContext(); //Scene scene = context.ImportFile("F:\\untitled.obj", PostProcessSteps.Triangulate); //AssimpLoad(scene, scene.RootNode); }
public void RemoveChildAt(int index) { NJS_OBJECT child = children[index]; children.RemoveAt(index); child.Parent = null; }
public static Animation ReadHeader(byte[] file, int address, uint imageBase, ModelFormat format, Dictionary <int, string> labels) { NJS_OBJECT Model = new NJS_OBJECT(file, (int)(ByteConverter.ToUInt32(file, address) - imageBase), imageBase, format); return(new Animation(file, (int)(ByteConverter.ToUInt32(file, address + 4) - imageBase), imageBase, Model.CountAnimated(), labels)); }
public override void Init(IniLevelData data, byte act, Device dev) { SkyboxScale[] skyboxdata = SkyboxScaleList.Load("Levels/Twinkle Park/Skybox Data.ini"); if (skyboxdata.Length > act) Skybox_Scale = skyboxdata[act].Far.ToVector3(); model = ObjectHelper.LoadModel("Levels/Twinkle Park/Skybox model.sa1mdl"); meshes = ObjectHelper.GetMeshes(model, dev); NoRender = act == 1; }
public void AddChild(NJS_OBJECT child) { children.Add(child); child.Parent = this; if (child.Sibling != null) { AddChild(child.Sibling); } }
public override void Init(ObjectData data, string name, Device dev) { ballmodel = ObjectHelper.LoadModel("Objects/Common/Spike Ball/Spike Ball.sa1mdl"); ballmeshes = ObjectHelper.GetMeshes(ballmodel, dev); cylindermodel = ObjectHelper.LoadModel("Objects/Collision/Cylinder.sa1mdl"); cylindermeshes = ObjectHelper.GetMeshes(cylindermodel, dev); spheremodel = ObjectHelper.LoadModel("Objects/Collision/Sphere.sa1mdl"); spheremeshes = ObjectHelper.GetMeshes(spheremodel, dev); }
public override BoundingSphere GetBounds(SETItem item, NJS_OBJECT model) { float largestScale = (item.Scale.X + 10) / 5f; if (item.Scale.Y > largestScale) largestScale = (item.Scale.Y + 10) / 5f; if (item.Scale.Z > largestScale) largestScale = (item.Scale.Z + 10) / 5f; BoundingSphere boxSphere = new BoundingSphere() { Center = new Vertex(item.Position.X, item.Position.Y, item.Position.Z), Radius = largestScale }; return boxSphere; }
public override void Init(IniLevelData data, byte act, Device dev) { SkyboxScale[] skyboxdata = SkyboxScaleList.Load("Levels/Emerald Coast/Skybox Data.ini"); if (skyboxdata.Length > act) Skybox_Scale = skyboxdata[act].Far.ToVector3(); model1 = ObjectHelper.LoadModel("Levels/Emerald Coast/Skybox model.sa1mdl"); mesh1 = ObjectHelper.GetMeshes(model1, dev); model2 = ObjectHelper.LoadModel("Levels/Emerald Coast/Skybox bottom model.sa1mdl"); mesh2 = ObjectHelper.GetMeshes(model2, dev); }
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 COL(byte[] file, int address, uint imageBase, LandTableFormat format, Dictionary <int, string> labels, bool?forceBasic) { Bounds = new BoundingSphere(file, address); ModelFormat mfmt = 0; switch (format) { case LandTableFormat.SA1: mfmt = ModelFormat.Basic; break; case LandTableFormat.SADX: mfmt = ModelFormat.BasicDX; break; case LandTableFormat.SA2: if (forceBasic.HasValue && forceBasic.Value) { mfmt = ModelFormat.Basic; } else { mfmt = ModelFormat.Chunk; } break; } switch (format) { case LandTableFormat.SA1: case LandTableFormat.SADX: Unknown1 = ByteConverter.ToInt32(file, address + 0x10); Unknown2 = ByteConverter.ToInt32(file, address + 0x14); uint tmpaddr = ByteConverter.ToUInt32(file, address + 0x18) - imageBase; Model = new NJS_OBJECT(file, (int)tmpaddr, imageBase, mfmt, labels); Unknown3 = ByteConverter.ToInt32(file, address + 0x1C); Flags = ByteConverter.ToInt32(file, address + 0x20); break; case LandTableFormat.SA2: Flags = ByteConverter.ToInt32(file, address + 0x1C); if (!forceBasic.HasValue) { mfmt = Flags < 0 ? ModelFormat.Chunk : ModelFormat.Basic; } tmpaddr = ByteConverter.ToUInt32(file, address + 0x10) - imageBase; Model = new NJS_OBJECT(file, (int)tmpaddr, imageBase, mfmt, labels); Unknown2 = ByteConverter.ToInt32(file, address + 0x14); Unknown3 = ByteConverter.ToInt32(file, address + 0x18); break; } }
public NJS_OBJECT(byte[] file, int address, uint imageBase, ModelFormat format, Dictionary <int, string> labels) { if (labels.ContainsKey(address)) { Name = labels[address]; } else { Name = "object_" + address.ToString("X8"); } ObjectFlags flags = (ObjectFlags)ByteConverter.ToInt32(file, address); RotateZYX = (flags & ObjectFlags.RotateZYX) == ObjectFlags.RotateZYX; Animate = (flags & ObjectFlags.NoAnimate) == 0; Morph = (flags & ObjectFlags.NoMorph) == 0; int tmpaddr = ByteConverter.ToInt32(file, address + 4); if (tmpaddr != 0) { tmpaddr = (int)unchecked ((uint)tmpaddr - imageBase); Attach = Attach.Load(file, tmpaddr, imageBase, format, labels); } Position = new Vertex(file, address + 8); Rotation = new Rotation(file, address + 0x14); Scale = new Vertex(file, address + 0x20); Children = new List <NJS_OBJECT>(); NJS_OBJECT child = null; tmpaddr = ByteConverter.ToInt32(file, address + 0x2C); if (tmpaddr != 0) { tmpaddr = (int)unchecked ((uint)tmpaddr - imageBase); child = new NJS_OBJECT(file, tmpaddr, imageBase, format, labels); } while (child != null) { Children.Add(child); child = child.Sibling; } tmpaddr = ByteConverter.ToInt32(file, address + 0x30); if (tmpaddr != 0) { tmpaddr = (int)unchecked ((uint)tmpaddr - imageBase); Sibling = new NJS_OBJECT(file, tmpaddr, imageBase, format, labels); } }
public NJS_OBJECT ToChunkModel() { List <NJS_OBJECT> newchildren = new List <NJS_OBJECT>(Children.Count); foreach (NJS_OBJECT item in Children) { newchildren.Add(item.ToBasicModel()); } NJS_OBJECT result = new NJS_OBJECT(); if (Attach != null) { result.Attach = Attach.ToChunkModel(); } result.Position = Position; result.Rotation = Rotation; result.Scale = Scale; result.Children = newchildren; return(result); }
public COL(byte[] file, int address, uint imageBase, LandTableFormat format, Dictionary<int, string> labels, bool? forceBasic) { Bounds = new BoundingSphere(file, address); ModelFormat mfmt = 0; switch (format) { case LandTableFormat.SA1: mfmt = ModelFormat.Basic; break; case LandTableFormat.SADX: mfmt = ModelFormat.BasicDX; break; case LandTableFormat.SA2: if (forceBasic.HasValue && forceBasic.Value) mfmt = ModelFormat.Basic; else mfmt = ModelFormat.Chunk; break; } switch (format) { case LandTableFormat.SA1: case LandTableFormat.SADX: Unknown1 = ByteConverter.ToInt32(file, address + 0x10); Unknown2 = ByteConverter.ToInt32(file, address + 0x14); uint tmpaddr = ByteConverter.ToUInt32(file, address + 0x18) - imageBase; Model = new NJS_OBJECT(file, (int)tmpaddr, imageBase, mfmt, labels); Unknown3 = ByteConverter.ToInt32(file, address + 0x1C); Flags = ByteConverter.ToInt32(file, address + 0x20); break; case LandTableFormat.SA2: Flags = ByteConverter.ToInt32(file, address + 0x1C); if (!forceBasic.HasValue) mfmt = Flags < 0 ? ModelFormat.Chunk : ModelFormat.Basic; tmpaddr = ByteConverter.ToUInt32(file, address + 0x10) - imageBase; Model = new NJS_OBJECT(file, (int)tmpaddr, imageBase, mfmt, labels); Unknown2 = ByteConverter.ToInt32(file, address + 0x14); Unknown3 = ByteConverter.ToInt32(file, address + 0x18); break; } }
public NJS_OBJECT(byte[] file, int address, uint imageBase, ModelFormat format, Dictionary<int, string> labels) { if (labels.ContainsKey(address)) Name = labels[address]; else Name = "object_" + address.ToString("X8"); ObjectFlags flags = (ObjectFlags)ByteConverter.ToInt32(file, address); RotateZYX = (flags & ObjectFlags.RotateZYX) == ObjectFlags.RotateZYX; Animate = (flags & ObjectFlags.NoAnimate) == 0; Morph = (flags & ObjectFlags.NoMorph) == 0; int tmpaddr = ByteConverter.ToInt32(file, address + 4); if (tmpaddr != 0) { tmpaddr = (int)unchecked((uint)tmpaddr - imageBase); Attach = Attach.Load(file, tmpaddr, imageBase, format, labels); } Position = new Vertex(file, address + 8); Rotation = new Rotation(file, address + 0x14); Scale = new Vertex(file, address + 0x20); Children = new List<NJS_OBJECT>(); NJS_OBJECT child = null; tmpaddr = ByteConverter.ToInt32(file, address + 0x2C); if (tmpaddr != 0) { tmpaddr = (int)unchecked((uint)tmpaddr - imageBase); child = new NJS_OBJECT(file, tmpaddr, imageBase, format, labels); } while (child != null) { Children.Add(child); child = child.Sibling; } tmpaddr = ByteConverter.ToInt32(file, address + 0x30); if (tmpaddr != 0) { tmpaddr = (int)unchecked((uint)tmpaddr - imageBase); Sibling = new NJS_OBJECT(file, tmpaddr, imageBase, format, labels); } }
public GeoAnimData(byte[] file, int address, uint imageBase, LandTableFormat format, Dictionary<int, string> labels) { 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); Animation = Animation.ReadHeader(file, (int)(ByteConverter.ToUInt32(file, address + 0x10) - imageBase), imageBase, mfmt, labels); Unknown4 = ByteConverter.ToInt32(file, address + 0x14); }
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); } }
public override BoundingSphere GetBounds(SETItem item, NJS_OBJECT model) { return base.GetBounds(item, model); }
public COL(byte[] file, int address, uint imageBase, LandTableFormat format, Dictionary <int, string> labels, bool?forceBasic, Dictionary <int, Attach> attaches) { Bounds = new BoundingSphere(file, address); ModelFormat mfmt = 0; switch (format) { case LandTableFormat.SA1: mfmt = ModelFormat.Basic; break; case LandTableFormat.SADX: mfmt = ModelFormat.BasicDX; break; case LandTableFormat.SA2: if (forceBasic.HasValue && forceBasic.Value) { mfmt = ModelFormat.Basic; } else { mfmt = ModelFormat.Chunk; } break; case LandTableFormat.SA2B: if (forceBasic.HasValue && forceBasic.Value) { mfmt = ModelFormat.Basic; } else { mfmt = ModelFormat.GC; } break; } switch (format) { case LandTableFormat.SA1: case LandTableFormat.SADX: WidthY = ByteConverter.ToSingle(file, address + 0x10); WidthZ = ByteConverter.ToSingle(file, address + 0x14); uint tmpaddr = ByteConverter.ToUInt32(file, address + 0x18) - imageBase; Model = new NJS_OBJECT(file, (int)tmpaddr, imageBase, mfmt, labels, attaches); BlockBits = ByteConverter.ToInt32(file, address + 0x1C); Flags = ByteConverter.ToInt32(file, address + 0x20); break; case LandTableFormat.SA2: case LandTableFormat.SA2B: Flags = ByteConverter.ToInt32(file, address + 0x1C); if (!forceBasic.HasValue && Flags >= 0) { mfmt = ModelFormat.Basic; } tmpaddr = ByteConverter.ToUInt32(file, address + 0x10) - imageBase; Model = new NJS_OBJECT(file, (int)tmpaddr, imageBase, mfmt, labels, attaches); WidthZ = ByteConverter.ToInt32(file, address + 0x14); BlockBits = ByteConverter.ToInt32(file, address + 0x18); break; } }
public ModelFile(string filename) { int tmpaddr; bool be = ByteConverter.BigEndian; ByteConverter.BigEndian = false; byte[] file = File.ReadAllBytes(filename); ulong magic = ByteConverter.ToUInt64(file, 0) & FormatMask; byte version = file[7]; if (version > CurrentVersion) throw new FormatException("Not a valid SA1MDL/SA2MDL file."); Metadata = new Dictionary<uint, byte[]>(); Dictionary<int, string> labels = new Dictionary<int, string>(); if (version < 2) { if (version == 1) { tmpaddr = ByteConverter.ToInt32(file, 0x14); if (tmpaddr != 0) { int addr = ByteConverter.ToInt32(file, tmpaddr); while (addr != -1) { labels.Add(addr, file.GetCString(ByteConverter.ToInt32(file, tmpaddr + 4))); tmpaddr += 8; addr = ByteConverter.ToInt32(file, tmpaddr); } } } switch (magic) { case SA1MDL: Format = ModelFormat.Basic; break; case SA2MDL: Format = ModelFormat.Chunk; break; default: throw new FormatException("Not a valid SA1MDL/SA2MDL file."); } Model = new NJS_OBJECT(file, ByteConverter.ToInt32(file, 8), 0, Format, labels); tmpaddr = ByteConverter.ToInt32(file, 0xC); if (tmpaddr != 0) { List<string> animfiles = new List<string>(); int addr = ByteConverter.ToInt32(file, tmpaddr); while (addr != -1) { animfiles.Add(file.GetCString(addr)); tmpaddr += 4; addr = ByteConverter.ToInt32(file, tmpaddr); } animationFiles = animfiles.ToArray(); } else animationFiles = new string[0]; string path = Path.GetDirectoryName(filename); List<Animation> anims = new List<Animation>(); foreach (string item in animationFiles) anims.Add(Animation.Load(Path.Combine(path, item), Model.CountAnimated())); Animations = anims.AsReadOnly(); if (version == 1) { tmpaddr = ByteConverter.ToInt32(file, 0x10); if (tmpaddr != 0) { List<string> morphfiles = new List<string>(); int addr = ByteConverter.ToInt32(file, tmpaddr); while (addr != -1) { morphfiles.Add(file.GetCString(addr)); tmpaddr += 4; addr = ByteConverter.ToInt32(file, tmpaddr); } morphFiles = morphfiles.ToArray(); } else morphFiles = new string[0]; List<Animation> morphs = new List<Animation>(); foreach (string item in morphFiles) morphs.Add(Animation.Load(Path.Combine(path, item), Model.CountMorph())); Morphs = morphs.AsReadOnly(); } else { morphFiles = new string[0]; Morphs = new ReadOnlyCollection<Animation>(new List<Animation>()); } } else { animationFiles = new string[0]; morphFiles = new string[0]; tmpaddr = ByteConverter.ToInt32(file, 0xC); if (tmpaddr != 0) { bool finished = false; while (!finished) { ChunkTypes type = (ChunkTypes)ByteConverter.ToUInt32(file, tmpaddr); int chunksz = ByteConverter.ToInt32(file, tmpaddr + 4); int nextchunk = tmpaddr + 8 + chunksz; tmpaddr += 8; if (version == 2) { switch (type) { case ChunkTypes.Label: while (ByteConverter.ToInt64(file, tmpaddr) != -1) { labels.Add(ByteConverter.ToInt32(file, tmpaddr), file.GetCString(ByteConverter.ToInt32(file, tmpaddr + 4))); tmpaddr += 8; } break; case ChunkTypes.Animation: List<string> animfiles = new List<string>(); while (ByteConverter.ToInt32(file, tmpaddr) != -1) { animfiles.Add(file.GetCString(ByteConverter.ToInt32(file, tmpaddr))); tmpaddr += 4; } animationFiles = animfiles.ToArray(); break; case ChunkTypes.Morph: List<string> morphfiles = new List<string>(); while (ByteConverter.ToInt32(file, tmpaddr) != -1) { morphfiles.Add(file.GetCString(ByteConverter.ToInt32(file, tmpaddr))); tmpaddr += 4; } morphFiles = morphfiles.ToArray(); break; case ChunkTypes.Author: Author = file.GetCString(tmpaddr); break; case ChunkTypes.Tool: Tool = file.GetCString(tmpaddr); break; case ChunkTypes.Description: Description = file.GetCString(tmpaddr); break; case ChunkTypes.Texture: break; case ChunkTypes.End: finished = true; break; } } else { byte[] chunk = new byte[chunksz]; Array.Copy(file, tmpaddr, chunk, 0, chunksz); int chunkaddr = 0; switch (type) { case ChunkTypes.Label: while (ByteConverter.ToInt64(chunk, chunkaddr) != -1) { labels.Add(ByteConverter.ToInt32(chunk, chunkaddr), chunk.GetCString(ByteConverter.ToInt32(chunk, chunkaddr + 4))); chunkaddr += 8; } break; case ChunkTypes.Animation: List<string> animchunks = new List<string>(); while (ByteConverter.ToInt32(chunk, chunkaddr) != -1) { animchunks.Add(chunk.GetCString(ByteConverter.ToInt32(chunk, chunkaddr))); chunkaddr += 4; } animationFiles = animchunks.ToArray(); break; case ChunkTypes.Morph: List<string> morphchunks = new List<string>(); while (ByteConverter.ToInt32(chunk, chunkaddr) != -1) { morphchunks.Add(chunk.GetCString(ByteConverter.ToInt32(chunk, chunkaddr))); chunkaddr += 4; } morphFiles = morphchunks.ToArray(); break; case ChunkTypes.Author: Author = chunk.GetCString(chunkaddr); break; case ChunkTypes.Tool: Tool = chunk.GetCString(chunkaddr); break; case ChunkTypes.Description: Description = chunk.GetCString(chunkaddr); break; case ChunkTypes.End: finished = true; break; default: Metadata.Add((uint)type, chunk); break; } } tmpaddr = nextchunk; } } switch (magic) { case SA1MDL: Format = ModelFormat.Basic; break; case SA2MDL: Format = ModelFormat.Chunk; break; default: throw new FormatException("Not a valid SA1MDL/SA2MDL file."); } Model = new NJS_OBJECT(file, ByteConverter.ToInt32(file, 8), 0, Format, labels); string path = Path.GetDirectoryName(filename); List<Animation> anims = new List<Animation>(); foreach (string item in animationFiles) anims.Add(Animation.Load(Path.Combine(path, item), Model.CountAnimated())); Animations = anims.AsReadOnly(); List<Animation> morphs = new List<Animation>(); foreach (string item in morphFiles) morphs.Add(Animation.Load(Path.Combine(path, item), Model.CountMorph())); Morphs = morphs.AsReadOnly(); } ByteConverter.BigEndian = be; }
static void Main(string[] args) { string datafilename, inifilename; if (args.Length > 0) { datafilename = args[0]; Console.WriteLine("File: {0}", datafilename); } else { Console.Write("File: "); datafilename = Console.ReadLine(); } if (args.Length > 1) { inifilename = args[1]; Console.WriteLine("INI File: {0}", inifilename); } else { Console.Write("INI File: "); inifilename = Console.ReadLine(); } 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; } } SA_Tools.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; Console.WriteLine(item.Key + ": " + data.Address.ToString("X") + " → " + data.Filename); Directory.CreateDirectory(Path.GetDirectoryName(data.Filename)); switch (type) { case "landtable": new LandTable(datafile, address, imageBase, landfmt) { Description = item.Key, Tool = "split" }.SaveToFile(data.Filename, landfmt); break; case "model": { SonicRetro.SAModel.NJS_OBJECT mdl = new SonicRetro.SAModel.NJS_OBJECT(datafile, address, imageBase, modelfmt); 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(data.Filename, mdl, mdlanis, mdlmorphs, null, item.Key, "split", null, modelfmt); } break; case "basicmodel": { SonicRetro.SAModel.NJS_OBJECT mdl = new SonicRetro.SAModel.NJS_OBJECT(datafile, address, imageBase, ModelFormat.Basic); 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(data.Filename, mdl, mdlanis, mdlmorphs, null, item.Key, "split", null, ModelFormat.Basic); } break; case "basicdxmodel": { SonicRetro.SAModel.NJS_OBJECT mdl = new SonicRetro.SAModel.NJS_OBJECT(datafile, address, imageBase, ModelFormat.BasicDX); 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(data.Filename, mdl, mdlanis, mdlmorphs, null, item.Key, "split", null, ModelFormat.BasicDX); } break; case "chunkmodel": { SonicRetro.SAModel.NJS_OBJECT mdl = new SonicRetro.SAModel.NJS_OBJECT(datafile, address, imageBase, ModelFormat.Chunk); 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(data.Filename, mdl, mdlanis, mdlmorphs, null, item.Key, "split", null, ModelFormat.Chunk); } break; case "action": { AnimationHeader ani = new AnimationHeader(datafile, address, imageBase, modelfmt); ani.Animation.Name = filedesc; ani.Animation.Save(data.Filename); } break; case "animation": new Animation(datafile, address, imageBase, int.Parse(customProperties["numparts"], NumberStyles.AllowLeadingWhite | NumberStyles.AllowTrailingWhite, NumberFormatInfo.InvariantInfo)) { Name = filedesc } .Save(data.Filename); 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(data.Filename); } break; case "startpos": if (SA2) { SA2StartPosList.Load(datafile, address).Save(data.Filename); } else { SA1StartPosList.Load(datafile, address).Save(data.Filename); } break; case "texlist": TextureList.Load(datafile, address, imageBase).Save(data.Filename); break; case "leveltexlist": new LevelTextureList(datafile, address, imageBase).Save(data.Filename); break; case "triallevellist": TrialLevelList.Save(TrialLevelList.Load(datafile, address, imageBase), data.Filename); break; case "bosslevellist": BossLevelList.Save(BossLevelList.Load(datafile, address), data.Filename); break; case "fieldstartpos": FieldStartPosList.Load(datafile, address).Save(data.Filename); break; case "soundtestlist": SoundTestList.Load(datafile, address, imageBase).Save(data.Filename); break; case "musiclist": { int muscnt = int.Parse(customProperties["length"], NumberStyles.Integer, NumberFormatInfo.InvariantInfo); MusicList.Load(datafile, address, imageBase, muscnt).Save(data.Filename); } break; case "soundlist": SoundList.Load(datafile, address, imageBase).Save(data.Filename); 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(data.Filename); } break; case "nextlevellist": NextLevelList.Load(datafile, address).Save(data.Filename); break; case "cutscenetext": { int cnt = int.Parse(customProperties["length"], NumberStyles.Integer, NumberFormatInfo.InvariantInfo); new CutsceneText(datafile, address, imageBase, cnt).Save(data.Filename, 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(data.Filename, 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(data.Filename, 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), data.Filename); break; case "deathzone": { List <DeathZoneFlags> flags = new List <DeathZoneFlags>(); string path = Path.GetDirectoryName(data.Filename); List <string> hashes = new List <string>(); int num = 0; while (SA_Tools.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 SonicRetro.SAModel.NJS_OBJECT(datafile, datafile.GetPointer(address + 4, imageBase), imageBase, modelfmt), null, null, null, null, "split", null, modelfmt); hashes.Add(HelperFunctions.FileHash(file)); address += 8; } flags.ToArray().Save(data.Filename); hashes.Insert(0, HelperFunctions.FileHash(data.Filename)); 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(data.Filename); } break; case "stageselectlist": { int cnt = int.Parse(customProperties["count"], NumberStyles.Integer, NumberFormatInfo.InvariantInfo); StageSelectLevelList.Load(datafile, address, cnt).Save(data.Filename); } break; case "levelrankscores": LevelRankScoresList.Load(datafile, address).Save(data.Filename); break; case "levelranktimes": LevelRankTimesList.Load(datafile, address).Save(data.Filename); break; case "endpos": SA2EndPosList.Load(datafile, address).Save(data.Filename); break; case "animationlist": { int cnt = int.Parse(customProperties["count"], NumberStyles.Integer, NumberFormatInfo.InvariantInfo); SA2AnimationInfoList.Load(datafile, address, cnt).Save(data.Filename); } break; case "levelpathlist": { List <string> hashes = new List <string>(); ushort lvlnum = (ushort)SA_Tools.ByteConverter.ToUInt32(datafile, address); while (lvlnum != 0xFFFF) { int ptr = SA_Tools.ByteConverter.ToInt32(datafile, address + 4); if (ptr != 0) { ptr = (int)((uint)ptr - imageBase); SA1LevelAct level = new SA1LevelAct(lvlnum); string lvldir = Path.Combine(data.Filename, level.ToString()); PathList.Load(datafile, ptr, imageBase).Save(lvldir, out string[] lvlhashes); hashes.Add(level.ToString() + ":" + string.Join(",", lvlhashes)); } address += 8; lvlnum = (ushort)SA_Tools.ByteConverter.ToUInt32(datafile, address); } data.MD5Hash = string.Join("|", hashes.ToArray()); nohash = true; } break; case "stagelightdatalist": SA1StageLightDataList.Load(datafile, address).Save(data.Filename); break; case "weldlist": WeldList.Load(datafile, address, imageBase).Save(data.Filename); break; case "bmitemattrlist": BlackMarketItemAttributesList.Load(datafile, address, imageBase).Save(data.Filename); break; case "creditstextlist": CreditsTextList.Load(datafile, address, imageBase).Save(data.Filename); break; default: // raw binary { byte[] bin = new byte[int.Parse(customProperties["size"], NumberStyles.HexNumber)]; Array.Copy(datafile, address, bin, 0, bin.Length); File.WriteAllBytes(data.Filename, bin); } break; } if (!nohash) { data.MD5Hash = HelperFunctions.FileHash(data.Filename); } 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(); } IniSerializer.Serialize(masterobjlist, inifile.MasterObjectList); } IniSerializer.Serialize(inifile, Path.Combine(Environment.CurrentDirectory, Path.GetFileNameWithoutExtension(datafilename)) + "_data.ini"); timer.Stop(); Console.WriteLine("Split " + itemcount + " items in " + timer.Elapsed.TotalSeconds + " seconds."); Console.WriteLine(); }
static void Main(string[] args) { string datafilename, inifilename; if (args.Length > 0) { datafilename = args[0]; Console.WriteLine("File: {0}", datafilename); } else { Console.Write("File: "); datafilename = Console.ReadLine(); } if (args.Length > 1) { inifilename = args[1]; Console.WriteLine("INI File: {0}", inifilename); } else { Console.Write("INI File: "); inifilename = Console.ReadLine(); } byte[] datafile = File.ReadAllBytes(datafilename); IniData inifile = IniSerializer.Deserialize<IniData>(inifilename); SA_Tools.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; Console.WriteLine(item.Key + ": " + data.Address.ToString("X") + " → " + data.Filename); Directory.CreateDirectory(Path.GetDirectoryName(data.Filename)); switch (type) { case "landtable": new LandTable(datafile, address, imageBase, landfmt) { Description = item.Key, Tool = "split" }.SaveToFile(data.Filename, landfmt); break; case "model": { SonicRetro.SAModel.NJS_OBJECT mdl = new SonicRetro.SAModel.NJS_OBJECT(datafile, address, imageBase, modelfmt); 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(data.Filename, mdl, mdlanis, mdlmorphs, null, item.Key, "split", null, modelfmt); } break; case "basicmodel": { SonicRetro.SAModel.NJS_OBJECT mdl = new SonicRetro.SAModel.NJS_OBJECT(datafile, address, imageBase, ModelFormat.Basic); 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(data.Filename, mdl, mdlanis, mdlmorphs, null, item.Key, "split", null, ModelFormat.Basic); } break; case "basicdxmodel": { SonicRetro.SAModel.NJS_OBJECT mdl = new SonicRetro.SAModel.NJS_OBJECT(datafile, address, imageBase, ModelFormat.BasicDX); 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(data.Filename, mdl, mdlanis, mdlmorphs, null, item.Key, "split", null, ModelFormat.BasicDX); } break; case "chunkmodel": { SonicRetro.SAModel.NJS_OBJECT mdl = new SonicRetro.SAModel.NJS_OBJECT(datafile, address, imageBase, ModelFormat.Chunk); 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(data.Filename, mdl, mdlanis, mdlmorphs, null, item.Key, "split", null, ModelFormat.Chunk); } break; case "action": { AnimationHeader ani = new AnimationHeader(datafile, address, imageBase, modelfmt); ani.Animation.Name = filedesc; ani.Animation.Save(data.Filename); } break; case "animation": new Animation(datafile, address, imageBase, int.Parse(customProperties["numparts"], NumberStyles.AllowLeadingWhite | NumberStyles.AllowTrailingWhite, NumberFormatInfo.InvariantInfo)) { Name = filedesc } .Save(data.Filename); 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(data.Filename); } break; case "startpos": if (SA2) SA2StartPosList.Load(datafile, address).Save(data.Filename); else SA1StartPosList.Load(datafile, address).Save(data.Filename); break; case "texlist": TextureList.Load(datafile, address, imageBase).Save(data.Filename); break; case "leveltexlist": new LevelTextureList(datafile, address, imageBase).Save(data.Filename); break; case "triallevellist": TrialLevelList.Save(TrialLevelList.Load(datafile, address, imageBase), data.Filename); break; case "bosslevellist": BossLevelList.Save(BossLevelList.Load(datafile, address), data.Filename); break; case "fieldstartpos": FieldStartPosList.Load(datafile, address).Save(data.Filename); break; case "soundtestlist": SoundTestList.Load(datafile, address, imageBase).Save(data.Filename); break; case "musiclist": { int muscnt = int.Parse(customProperties["length"], NumberStyles.Integer, NumberFormatInfo.InvariantInfo); MusicList.Load(datafile, address, imageBase, muscnt).Save(data.Filename); } break; case "soundlist": SoundList.Load(datafile, address, imageBase).Save(data.Filename); 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(data.Filename); } break; case "nextlevellist": NextLevelList.Load(datafile, address).Save(data.Filename); break; case "cutscenetext": { int cnt = int.Parse(customProperties["length"], NumberStyles.Integer, NumberFormatInfo.InvariantInfo); string[] hashes; new CutsceneText(datafile, address, imageBase, cnt).Save(data.Filename, out hashes); data.MD5Hash = string.Join(",", hashes); nohash = true; } break; case "recapscreen": { int cnt = int.Parse(customProperties["length"], NumberStyles.Integer, NumberFormatInfo.InvariantInfo); string[][] hashes; RecapScreenList.Load(datafile, address, imageBase, cnt).Save(data.Filename, out 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); string[][] hashes; NPCTextList.Load(datafile, address, imageBase, cnt).Save(data.Filename, out 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), data.Filename); break; case "deathzone": { List<DeathZoneFlags> flags = new List<DeathZoneFlags>(); string path = Path.GetDirectoryName(data.Filename); List<string> hashes = new List<string>(); int num = 0; while (SA_Tools.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 SonicRetro.SAModel.NJS_OBJECT(datafile, datafile.GetPointer(address + 4, imageBase), imageBase, modelfmt), null, null, null, null, "split", null, modelfmt); hashes.Add(HelperFunctions.FileHash(file)); address += 8; } flags.ToArray().Save(data.Filename); hashes.Insert(0, HelperFunctions.FileHash(data.Filename)); 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(data.Filename); } break; case "stageselectlist": { int cnt = int.Parse(customProperties["count"], NumberStyles.Integer, NumberFormatInfo.InvariantInfo); StageSelectLevelList.Load(datafile, address, cnt).Save(data.Filename); } break; case "levelrankscores": LevelRankScoresList.Load(datafile, address).Save(data.Filename); break; case "levelranktimes": LevelRankTimesList.Load(datafile, address).Save(data.Filename); break; case "endpos": SA2EndPosList.Load(datafile, address).Save(data.Filename); break; case "animationlist": { int cnt = int.Parse(customProperties["count"], NumberStyles.Integer, NumberFormatInfo.InvariantInfo); SA2AnimationInfoList.Load(datafile, address, cnt).Save(data.Filename); } break; case "levelpathlist": { List<string> hashes = new List<string>(); ushort lvlnum = (ushort)SA_Tools.ByteConverter.ToUInt32(datafile, address); while (lvlnum != 0xFFFF) { int ptr = SA_Tools.ByteConverter.ToInt32(datafile, address + 4); if (ptr != 0) { ptr = (int)((uint)ptr - imageBase); SA1LevelAct level = new SA1LevelAct(lvlnum); string lvldir = Path.Combine(data.Filename, level.ToString()); string[] lvlhashes; PathList.Load(datafile, ptr, imageBase).Save(lvldir, out lvlhashes); hashes.Add(level.ToString() + ":" + string.Join(",", lvlhashes)); } address += 8; lvlnum = (ushort)SA_Tools.ByteConverter.ToUInt32(datafile, address); } data.MD5Hash = string.Join("|", hashes.ToArray()); nohash = true; } break; case "stagelightdatalist": SA1StageLightDataList.Load(datafile, address).Save(data.Filename); break; default: // raw binary { byte[] bin = new byte[int.Parse(customProperties["size"], NumberStyles.HexNumber)]; Array.Copy(datafile, address, bin, 0, bin.Length); File.WriteAllBytes(data.Filename, bin); } break; } if (!nohash) data.MD5Hash = HelperFunctions.FileHash(data.Filename); 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(); } IniSerializer.Serialize(masterobjlist, inifile.MasterObjectList); } IniSerializer.Serialize(inifile, Path.Combine(Environment.CurrentDirectory, Path.GetFileNameWithoutExtension(datafilename)) + "_data.ini"); timer.Stop(); Console.WriteLine("Split " + itemcount + " items in " + timer.Elapsed.TotalSeconds + " seconds."); Console.WriteLine(); }
public static void CreateFile(string filename, NJS_OBJECT model, string[] animationFiles, string[] morphFiles, string author, string description, string tool, Dictionary<uint, byte[]> metadata, ModelFormat format) { bool be = ByteConverter.BigEndian; ByteConverter.BigEndian = false; if (format == ModelFormat.BasicDX) format = ModelFormat.Basic; List<byte> file = new List<byte>(); ulong magic; switch (format) { case ModelFormat.Basic: case ModelFormat.BasicDX: magic = SA1MDLVer; break; case ModelFormat.Chunk: magic = SA2MDLVer; break; default: throw new ArgumentException("Cannot save " + format.ToString() + " format models to file!", "format"); } file.AddRange(ByteConverter.GetBytes(magic)); uint addr; Dictionary<string, uint> labels = new Dictionary<string, uint>(); byte[] mdl = model.GetBytes(0x10, false, labels, out addr); file.AddRange(ByteConverter.GetBytes(addr + 0x10)); file.AddRange(ByteConverter.GetBytes(mdl.Length + 0x10)); file.AddRange(mdl); if (labels.Count > 0) { List<byte> chunk = new List<byte>((labels.Count * 8) + 8); int straddr = (labels.Count * 8) + 8; List<byte> strbytes = new List<byte>(); foreach (KeyValuePair<string, uint> label in labels) { chunk.AddRange(ByteConverter.GetBytes(label.Value)); chunk.AddRange(ByteConverter.GetBytes(straddr + strbytes.Count)); strbytes.AddRange(Encoding.UTF8.GetBytes(label.Key)); strbytes.Add(0); strbytes.Align(4); } chunk.AddRange(ByteConverter.GetBytes(-1L)); chunk.AddRange(strbytes); file.AddRange(ByteConverter.GetBytes((uint)ChunkTypes.Label)); file.AddRange(ByteConverter.GetBytes(chunk.Count)); file.AddRange(chunk); } if (animationFiles != null && animationFiles.Length > 0) { List<byte> chunk = new List<byte>((animationFiles.Length + 1) * 4); int straddr = (animationFiles.Length + 1) * 4; List<byte> strbytes = new List<byte>(); for (int i = 0; i < animationFiles.Length; i++) { chunk.AddRange(ByteConverter.GetBytes(straddr + strbytes.Count)); strbytes.AddRange(Encoding.UTF8.GetBytes(animationFiles[i])); strbytes.Add(0); strbytes.Align(4); } chunk.AddRange(ByteConverter.GetBytes(-1)); chunk.AddRange(strbytes); file.AddRange(ByteConverter.GetBytes((uint)ChunkTypes.Animation)); file.AddRange(ByteConverter.GetBytes(chunk.Count)); file.AddRange(chunk); } if (morphFiles != null && morphFiles.Length > 0) { List<byte> chunk = new List<byte>((morphFiles.Length + 1) * 4); int straddr = (morphFiles.Length + 1) * 4; List<byte> strbytes = new List<byte>(); for (int i = 0; i < morphFiles.Length; i++) { chunk.AddRange(ByteConverter.GetBytes(straddr + strbytes.Count)); strbytes.AddRange(Encoding.UTF8.GetBytes(morphFiles[i])); strbytes.Add(0); strbytes.Align(4); } chunk.AddRange(ByteConverter.GetBytes(-1)); chunk.AddRange(strbytes); file.AddRange(ByteConverter.GetBytes((uint)ChunkTypes.Morph)); file.AddRange(ByteConverter.GetBytes(chunk.Count)); file.AddRange(chunk); } if (!string.IsNullOrEmpty(author)) { List<byte> chunk = new List<byte>(author.Length + 1); chunk.AddRange(Encoding.UTF8.GetBytes(author)); chunk.Add(0); chunk.Align(4); file.AddRange(ByteConverter.GetBytes((uint)ChunkTypes.Author)); file.AddRange(ByteConverter.GetBytes(chunk.Count)); file.AddRange(chunk); } if (!string.IsNullOrEmpty(description)) { List<byte> chunk = new List<byte>(description.Length + 1); chunk.AddRange(Encoding.UTF8.GetBytes(description)); chunk.Add(0); chunk.Align(4); file.AddRange(ByteConverter.GetBytes((uint)ChunkTypes.Description)); file.AddRange(ByteConverter.GetBytes(chunk.Count)); file.AddRange(chunk); } if (!string.IsNullOrEmpty(tool)) { List<byte> chunk = new List<byte>(tool.Length + 1); chunk.AddRange(Encoding.UTF8.GetBytes(tool)); chunk.Add(0); chunk.Align(4); file.AddRange(ByteConverter.GetBytes((uint)ChunkTypes.Tool)); file.AddRange(ByteConverter.GetBytes(chunk.Count)); file.AddRange(chunk); } if (metadata != null) { foreach (KeyValuePair<uint, byte[]> item in metadata) { file.AddRange(ByteConverter.GetBytes(item.Key)); file.AddRange(ByteConverter.GetBytes(item.Value.Length)); file.AddRange(item.Value); } } file.AddRange(ByteConverter.GetBytes((uint)ChunkTypes.End)); file.AddRange(new byte[4]); File.WriteAllBytes(filename, file.ToArray()); ByteConverter.BigEndian = be; }
public override void Init(ObjectData data, string name, Device dev) { model = ObjectHelper.LoadModel("Objects/Common/Dash Panel.sa1mdl"); meshes = ObjectHelper.GetMeshes(model, dev); }
public static void CreateFile(string filename, NJS_OBJECT model, string[] animationFiles, string[] morphFiles, string author, string description, string tool, Dictionary <uint, byte[]> metadata, ModelFormat format) { bool be = ByteConverter.BigEndian; ByteConverter.BigEndian = false; if (format == ModelFormat.BasicDX) { format = ModelFormat.Basic; } List <byte> file = new List <byte>(); ulong magic; switch (format) { case ModelFormat.Basic: case ModelFormat.BasicDX: magic = SA1MDLVer; break; case ModelFormat.Chunk: magic = SA2MDLVer; break; default: throw new ArgumentException("Cannot save " + format.ToString() + " format models to file!", "format"); } file.AddRange(ByteConverter.GetBytes(magic)); Dictionary <string, uint> labels = new Dictionary <string, uint>(); byte[] mdl = model.GetBytes(0x10, false, labels, out uint addr); file.AddRange(ByteConverter.GetBytes(addr + 0x10)); file.AddRange(ByteConverter.GetBytes(mdl.Length + 0x10)); file.AddRange(mdl); if (labels.Count > 0) { List <byte> chunk = new List <byte>((labels.Count * 8) + 8); int straddr = (labels.Count * 8) + 8; List <byte> strbytes = new List <byte>(); foreach (KeyValuePair <string, uint> label in labels) { chunk.AddRange(ByteConverter.GetBytes(label.Value)); chunk.AddRange(ByteConverter.GetBytes(straddr + strbytes.Count)); strbytes.AddRange(Encoding.UTF8.GetBytes(label.Key)); strbytes.Add(0); strbytes.Align(4); } chunk.AddRange(ByteConverter.GetBytes(-1L)); chunk.AddRange(strbytes); file.AddRange(ByteConverter.GetBytes((uint)ChunkTypes.Label)); file.AddRange(ByteConverter.GetBytes(chunk.Count)); file.AddRange(chunk); } if (animationFiles != null && animationFiles.Length > 0) { List <byte> chunk = new List <byte>((animationFiles.Length + 1) * 4); int straddr = (animationFiles.Length + 1) * 4; List <byte> strbytes = new List <byte>(); for (int i = 0; i < animationFiles.Length; i++) { chunk.AddRange(ByteConverter.GetBytes(straddr + strbytes.Count)); strbytes.AddRange(Encoding.UTF8.GetBytes(animationFiles[i])); strbytes.Add(0); strbytes.Align(4); } chunk.AddRange(ByteConverter.GetBytes(-1)); chunk.AddRange(strbytes); file.AddRange(ByteConverter.GetBytes((uint)ChunkTypes.Animation)); file.AddRange(ByteConverter.GetBytes(chunk.Count)); file.AddRange(chunk); } if (morphFiles != null && morphFiles.Length > 0) { List <byte> chunk = new List <byte>((morphFiles.Length + 1) * 4); int straddr = (morphFiles.Length + 1) * 4; List <byte> strbytes = new List <byte>(); for (int i = 0; i < morphFiles.Length; i++) { chunk.AddRange(ByteConverter.GetBytes(straddr + strbytes.Count)); strbytes.AddRange(Encoding.UTF8.GetBytes(morphFiles[i])); strbytes.Add(0); strbytes.Align(4); } chunk.AddRange(ByteConverter.GetBytes(-1)); chunk.AddRange(strbytes); file.AddRange(ByteConverter.GetBytes((uint)ChunkTypes.Morph)); file.AddRange(ByteConverter.GetBytes(chunk.Count)); file.AddRange(chunk); } if (!string.IsNullOrEmpty(author)) { List <byte> chunk = new List <byte>(author.Length + 1); chunk.AddRange(Encoding.UTF8.GetBytes(author)); chunk.Add(0); chunk.Align(4); file.AddRange(ByteConverter.GetBytes((uint)ChunkTypes.Author)); file.AddRange(ByteConverter.GetBytes(chunk.Count)); file.AddRange(chunk); } if (!string.IsNullOrEmpty(description)) { List <byte> chunk = new List <byte>(description.Length + 1); chunk.AddRange(Encoding.UTF8.GetBytes(description)); chunk.Add(0); chunk.Align(4); file.AddRange(ByteConverter.GetBytes((uint)ChunkTypes.Description)); file.AddRange(ByteConverter.GetBytes(chunk.Count)); file.AddRange(chunk); } if (!string.IsNullOrEmpty(tool)) { List <byte> chunk = new List <byte>(tool.Length + 1); chunk.AddRange(Encoding.UTF8.GetBytes(tool)); chunk.Add(0); chunk.Align(4); file.AddRange(ByteConverter.GetBytes((uint)ChunkTypes.Tool)); file.AddRange(ByteConverter.GetBytes(chunk.Count)); file.AddRange(chunk); } if (metadata != null) { foreach (KeyValuePair <uint, byte[]> item in metadata) { file.AddRange(ByteConverter.GetBytes(item.Key)); file.AddRange(ByteConverter.GetBytes(item.Value.Length)); file.AddRange(item.Value); } } file.AddRange(ByteConverter.GetBytes((uint)ChunkTypes.End)); file.AddRange(new byte[4]); File.WriteAllBytes(filename, file.ToArray()); ByteConverter.BigEndian = be; }
public NJS_OBJECT(Scene scene, Node node, NJS_OBJECT parent, string[] textures = null) { Parent = parent; AssimpLoad(scene, node, textures); }
public void AddChild(NJS_OBJECT child) { children.Add(child); child.Parent = this; }
// void AssimpLoad(Scene scene, Node node, string[] textures = null) { Name = node.Name; Vector3D translation; Vector3D scaling; Quaternion rotation; node.Transform.Decompose(out scaling, out rotation, out translation); Vector3D rotationConverted = Extensions.FromQ2(rotation); Position = new Vertex(translation.X, translation.Y, translation.Z); Rotation = new Rotation(Rotation.DegToBAMS(rotationConverted.X), Rotation.DegToBAMS(rotationConverted.Y), Rotation.DegToBAMS(rotationConverted.Z)); Scale = new Vertex(scaling.X, scaling.Y, scaling.Z); List <Mesh> meshes = new List <Mesh>(); foreach (int i in node.MeshIndices) { meshes.Add(scene.Meshes[i]); } //materials.Add(new NJS_MATERIAL() { DiffuseColor = System.Drawing.Color.White}); if (node.HasMeshes) { Attach = new BasicAttach(scene.Materials, meshes, textures); } else { Attach = null; } if (node.HasChildren) { //List<NJS_OBJECT> list = new List<NJS_OBJECT>(node.Children.Select(a => new NJS_OBJECT(scene, a, this))); List <NJS_OBJECT> list = new List <NJS_OBJECT>(); foreach (Node n in node.Children) { NJS_OBJECT t = new NJS_OBJECT(scene, n, this, textures); //HACK: workaround for those weird empty nodes created by most 3d editors if (n.Name == "") { t.Children[0].Position = t.Position; t.Children[0].Rotation = t.Rotation; t.Children[0].Scale = t.Scale; list.Add(t.Children[0]); } else { list.Add(t); } /*if (Parent != null) * { * if (t.Attach != null) * { * Parent.Attach = t.Attach; * if (Parent.children != null && t.children.Count > 0) * Parent.children.AddRange(t.children); * } * else * list.Add(t); * }*/ } Children = new ReadOnlyCollection <NJS_OBJECT>(list.ToArray()); } else { Children = new ReadOnlyCollection <NJS_OBJECT>(new List <NJS_OBJECT>().ToArray()); } }
static void Main(string[] args) { string dir = Environment.CurrentDirectory; try { Queue<string> argq = new Queue<string>(args); if (argq.Count > 0 && argq.Peek().Equals("/be", StringComparison.OrdinalIgnoreCase)) { ByteConverter.BigEndian = true; argq.Dequeue(); } string mdlfilename; if (argq.Count > 0) { mdlfilename = argq.Dequeue(); Console.WriteLine("File: {0}", mdlfilename); } else { Console.Write("File: "); mdlfilename = Console.ReadLine(); } mdlfilename = Path.GetFullPath(mdlfilename); string[] anifilenames = new string[argq.Count]; for (int j = 0; j < anifilenames.Length; j++) { Console.WriteLine("Animations: {0}", argq.Peek()); anifilenames[j] = Path.GetFullPath(argq.Dequeue()); } Environment.CurrentDirectory = Path.GetDirectoryName(mdlfilename); byte[] mdlfile = File.ReadAllBytes(mdlfilename); if (Path.GetExtension(mdlfilename).Equals(".prs", StringComparison.OrdinalIgnoreCase)) mdlfile = FraGag.Compression.Prs.Decompress(mdlfile); Directory.CreateDirectory(Path.GetFileNameWithoutExtension(mdlfilename)); int address = 0; int i = ByteConverter.ToInt32(mdlfile, address); SortedDictionary<int, int> modeladdrs = new SortedDictionary<int, int>(); while (i != -1) { modeladdrs[i] = ByteConverter.ToInt32(mdlfile, address + 4); address += 8; i = ByteConverter.ToInt32(mdlfile, address); } Dictionary<int, SonicRetro.SAModel.NJS_OBJECT> models = new Dictionary<int, SonicRetro.SAModel.NJS_OBJECT>(); Dictionary<int, string> modelnames = new Dictionary<int, string>(); List<string> partnames = new List<string>(); foreach (KeyValuePair<int, int> item in modeladdrs) { SonicRetro.SAModel.NJS_OBJECT obj = new SonicRetro.SAModel.NJS_OBJECT(mdlfile, item.Value, 0, ModelFormat.Chunk); modelnames[item.Key] = obj.Name; if (!partnames.Contains(obj.Name)) { models[item.Key] = obj; partnames.AddRange(obj.GetObjects().Select((o) => o.Name)); } } Dictionary<int, string> animfns = new Dictionary<int, string>(); Dictionary<int, Animation> anims = new Dictionary<int, Animation>(); foreach (string anifilename in anifilenames) { byte[] anifile = File.ReadAllBytes(anifilename); if (Path.GetExtension(anifilename).Equals(".prs", StringComparison.OrdinalIgnoreCase)) anifile = FraGag.Compression.Prs.Decompress(anifile); Directory.CreateDirectory(Path.GetFileNameWithoutExtension(anifilename)); address = 0; i = ByteConverter.ToInt16(anifile, address); while (i != -1) { anims[i] = new Animation(anifile, ByteConverter.ToInt32(anifile, address + 4), 0, ByteConverter.ToInt16(anifile, address + 2)); animfns[i] = Path.Combine(Path.GetFileNameWithoutExtension(anifilename), i.ToString(NumberFormatInfo.InvariantInfo) + ".saanim"); address += 8; i = ByteConverter.ToInt16(anifile, address); } } foreach (KeyValuePair<int, SonicRetro.SAModel.NJS_OBJECT> model in models) { List<string> animlist = new List<string>(); foreach (KeyValuePair<int, Animation> anim in anims) if (model.Value.CountAnimated() == anim.Value.ModelParts) animlist.Add("../" + animfns[anim.Key]); ModelFile.CreateFile(Path.Combine(Path.GetFileNameWithoutExtension(mdlfilename), model.Key.ToString(NumberFormatInfo.InvariantInfo) + ".sa2mdl"), model.Value, animlist.ToArray(), null, null, null, "splitMDL", null, ModelFormat.Chunk); } IniSerializer.Serialize(modelnames, new IniCollectionSettings(IniCollectionMode.IndexOnly), Path.Combine(Path.GetFileNameWithoutExtension(mdlfilename), Path.GetFileNameWithoutExtension(mdlfilename) + ".ini")); foreach (KeyValuePair<int, Animation> anim in anims) anim.Value.Save(animfns[anim.Key]); } finally { Environment.CurrentDirectory = dir; } }
public NJS_OBJECT ToChunkModel() { List<NJS_OBJECT> newchildren = new List<NJS_OBJECT>(Children.Count); foreach (NJS_OBJECT item in Children) newchildren.Add(item.ToBasicModel()); NJS_OBJECT result = new NJS_OBJECT(); if (Attach != null) result.Attach = Attach.ToChunkModel(); result.Position = Position; result.Rotation = Rotation; result.Scale = Scale; result.Children = newchildren; return result; }
public override void Init(IniLevelData data, byte act, Device dev) { carriermdl = ObjectHelper.LoadModel("Levels/Sky Chase/Egg Carrier model.sa1mdl"); carriermesh = ObjectHelper.GetMeshes(carriermdl, dev); }
public void InsertChild(int index, NJS_OBJECT child) { children.Insert(index, child); child.Parent = this; }
public ModelFile(string filename) { int tmpaddr; bool be = ByteConverter.BigEndian; ByteConverter.BigEndian = false; byte[] file = File.ReadAllBytes(filename); ulong magic = ByteConverter.ToUInt64(file, 0) & FormatMask; byte version = file[7]; if (version > CurrentVersion) { throw new FormatException("Not a valid SA1MDL/SA2MDL file."); } Metadata = new Dictionary <uint, byte[]>(); Dictionary <int, string> labels = new Dictionary <int, string>(); if (version < 2) { if (version == 1) { tmpaddr = ByteConverter.ToInt32(file, 0x14); if (tmpaddr != 0) { int addr = ByteConverter.ToInt32(file, tmpaddr); while (addr != -1) { labels.Add(addr, file.GetCString(ByteConverter.ToInt32(file, tmpaddr + 4))); tmpaddr += 8; addr = ByteConverter.ToInt32(file, tmpaddr); } } } switch (magic) { case SA1MDL: Format = ModelFormat.Basic; break; case SA2MDL: Format = ModelFormat.Chunk; break; default: throw new FormatException("Not a valid SA1MDL/SA2MDL file."); } Model = new NJS_OBJECT(file, ByteConverter.ToInt32(file, 8), 0, Format, labels); tmpaddr = ByteConverter.ToInt32(file, 0xC); if (tmpaddr != 0) { List <string> animfiles = new List <string>(); int addr = ByteConverter.ToInt32(file, tmpaddr); while (addr != -1) { animfiles.Add(file.GetCString(addr)); tmpaddr += 4; addr = ByteConverter.ToInt32(file, tmpaddr); } animationFiles = animfiles.ToArray(); } else { animationFiles = new string[0]; } string path = Path.GetDirectoryName(filename); List <Animation> anims = new List <Animation>(); foreach (string item in animationFiles) { anims.Add(Animation.Load(Path.Combine(path, item), Model.CountAnimated())); } Animations = anims.AsReadOnly(); if (version == 1) { tmpaddr = ByteConverter.ToInt32(file, 0x10); if (tmpaddr != 0) { List <string> morphfiles = new List <string>(); int addr = ByteConverter.ToInt32(file, tmpaddr); while (addr != -1) { morphfiles.Add(file.GetCString(addr)); tmpaddr += 4; addr = ByteConverter.ToInt32(file, tmpaddr); } morphFiles = morphfiles.ToArray(); } else { morphFiles = new string[0]; } List <Animation> morphs = new List <Animation>(); foreach (string item in morphFiles) { morphs.Add(Animation.Load(Path.Combine(path, item), Model.CountMorph())); } Morphs = morphs.AsReadOnly(); } else { morphFiles = new string[0]; Morphs = new ReadOnlyCollection <Animation>(new List <Animation>()); } } else { animationFiles = new string[0]; morphFiles = new string[0]; tmpaddr = ByteConverter.ToInt32(file, 0xC); if (tmpaddr != 0) { bool finished = false; while (!finished) { ChunkTypes type = (ChunkTypes)ByteConverter.ToUInt32(file, tmpaddr); int chunksz = ByteConverter.ToInt32(file, tmpaddr + 4); int nextchunk = tmpaddr + 8 + chunksz; tmpaddr += 8; if (version == 2) { switch (type) { case ChunkTypes.Label: while (ByteConverter.ToInt64(file, tmpaddr) != -1) { labels.Add(ByteConverter.ToInt32(file, tmpaddr), file.GetCString(ByteConverter.ToInt32(file, tmpaddr + 4))); tmpaddr += 8; } break; case ChunkTypes.Animation: List <string> animfiles = new List <string>(); while (ByteConverter.ToInt32(file, tmpaddr) != -1) { animfiles.Add(file.GetCString(ByteConverter.ToInt32(file, tmpaddr))); tmpaddr += 4; } animationFiles = animfiles.ToArray(); break; case ChunkTypes.Morph: List <string> morphfiles = new List <string>(); while (ByteConverter.ToInt32(file, tmpaddr) != -1) { morphfiles.Add(file.GetCString(ByteConverter.ToInt32(file, tmpaddr))); tmpaddr += 4; } morphFiles = morphfiles.ToArray(); break; case ChunkTypes.Author: Author = file.GetCString(tmpaddr); break; case ChunkTypes.Tool: Tool = file.GetCString(tmpaddr); break; case ChunkTypes.Description: Description = file.GetCString(tmpaddr); break; case ChunkTypes.Texture: break; case ChunkTypes.End: finished = true; break; } } else { byte[] chunk = new byte[chunksz]; Array.Copy(file, tmpaddr, chunk, 0, chunksz); int chunkaddr = 0; switch (type) { case ChunkTypes.Label: while (ByteConverter.ToInt64(chunk, chunkaddr) != -1) { labels.Add(ByteConverter.ToInt32(chunk, chunkaddr), chunk.GetCString(ByteConverter.ToInt32(chunk, chunkaddr + 4))); chunkaddr += 8; } break; case ChunkTypes.Animation: List <string> animchunks = new List <string>(); while (ByteConverter.ToInt32(chunk, chunkaddr) != -1) { animchunks.Add(chunk.GetCString(ByteConverter.ToInt32(chunk, chunkaddr))); chunkaddr += 4; } animationFiles = animchunks.ToArray(); break; case ChunkTypes.Morph: List <string> morphchunks = new List <string>(); while (ByteConverter.ToInt32(chunk, chunkaddr) != -1) { morphchunks.Add(chunk.GetCString(ByteConverter.ToInt32(chunk, chunkaddr))); chunkaddr += 4; } morphFiles = morphchunks.ToArray(); break; case ChunkTypes.Author: Author = chunk.GetCString(chunkaddr); break; case ChunkTypes.Tool: Tool = chunk.GetCString(chunkaddr); break; case ChunkTypes.Description: Description = chunk.GetCString(chunkaddr); break; case ChunkTypes.End: finished = true; break; default: Metadata.Add((uint)type, chunk); break; } } tmpaddr = nextchunk; } } switch (magic) { case SA1MDL: Format = ModelFormat.Basic; break; case SA2MDL: Format = ModelFormat.Chunk; break; default: throw new FormatException("Not a valid SA1MDL/SA2MDL file."); } Model = new NJS_OBJECT(file, ByteConverter.ToInt32(file, 8), 0, Format, labels); string path = Path.GetDirectoryName(filename); List <Animation> anims = new List <Animation>(); foreach (string item in animationFiles) { anims.Add(Animation.Load(Path.Combine(path, item), Model.CountAnimated())); } Animations = anims.AsReadOnly(); List <Animation> morphs = new List <Animation>(); foreach (string item in morphFiles) { morphs.Add(Animation.Load(Path.Combine(path, item), Model.CountMorph())); } Morphs = morphs.AsReadOnly(); } ByteConverter.BigEndian = be; }
public void RemoveChild(NJS_OBJECT child) { children.Remove(child); child.Parent = null; }
public override BoundingSphere GetBounds(SETItem item, NJS_OBJECT model) { BoundingSphere bounds = new BoundingSphere(item.Position, item.Scale.X); return bounds; }
public AnimationHeader(byte[] file, int address, uint imageBase, ModelFormat format, Dictionary<int, string> labels) { Model = new NJS_OBJECT(file, (int)(ByteConverter.ToUInt32(file, address) - imageBase), imageBase, format); Animation = new Animation(file, (int)(ByteConverter.ToUInt32(file, address + 4) - imageBase), imageBase, Model.CountAnimated(), labels); }
public override void Init(ObjectData data, string name, Device dev) { model = ObjectHelper.LoadModel("Objects/Collision/Cylinder.sa1mdl"); meshes = ObjectHelper.GetMeshes(model, dev); }
public ModelFile(byte[] file, string filename = null) { int tmpaddr; bool be = ByteConverter.BigEndian; ByteConverter.BigEndian = false; ulong magic = ByteConverter.ToUInt64(file, 0) & FormatMask; byte version = file[7]; if (version > CurrentVersion) { throw new FormatException("Not a valid SA1MDL/SA2MDL file."); } Metadata = new Dictionary <uint, byte[]>(); Dictionary <int, string> labels = new Dictionary <int, string>(); Dictionary <int, Attach> attaches = new Dictionary <int, Attach>(); if (version < 2) { if (version == 1) { tmpaddr = ByteConverter.ToInt32(file, 0x14); if (tmpaddr != 0) { int addr = ByteConverter.ToInt32(file, tmpaddr); while (addr != -1) { labels.Add(addr, file.GetCString(ByteConverter.ToInt32(file, tmpaddr + 4))); tmpaddr += 8; addr = ByteConverter.ToInt32(file, tmpaddr); } } } switch (magic) { case SA1MDL: Format = ModelFormat.Basic; break; case SA2MDL: Format = ModelFormat.Chunk; break; default: throw new FormatException("Not a valid SA1MDL/SA2MDL file."); } Model = new NJS_OBJECT(file, ByteConverter.ToInt32(file, 8), 0, Format, labels, attaches); if (filename != null) { tmpaddr = ByteConverter.ToInt32(file, 0xC); if (tmpaddr != 0) { List <string> animfiles = new List <string>(); int addr = ByteConverter.ToInt32(file, tmpaddr); while (addr != -1) { animfiles.Add(file.GetCString(addr)); tmpaddr += 4; addr = ByteConverter.ToInt32(file, tmpaddr); } animationFiles = animfiles.ToArray(); } else { animationFiles = new string[0]; } string path = Path.GetDirectoryName(filename); List <NJS_MOTION> anims = new List <NJS_MOTION>(); try { foreach (string item in animationFiles) { anims.Add(NJS_MOTION.Load(Path.Combine(path, item), Model.CountAnimated())); } } catch { anims.Clear(); } Animations = anims.AsReadOnly(); } } else { animationFiles = new string[0]; tmpaddr = ByteConverter.ToInt32(file, 0xC); if (tmpaddr != 0) { bool finished = false; while (!finished) { ChunkTypes type = (ChunkTypes)ByteConverter.ToUInt32(file, tmpaddr); int chunksz = ByteConverter.ToInt32(file, tmpaddr + 4); int nextchunk = tmpaddr + 8 + chunksz; tmpaddr += 8; if (version == 2) { switch (type) { case ChunkTypes.Label: while (ByteConverter.ToInt64(file, tmpaddr) != -1) { labels.Add(ByteConverter.ToInt32(file, tmpaddr), file.GetCString(ByteConverter.ToInt32(file, tmpaddr + 4))); tmpaddr += 8; } break; case ChunkTypes.Animation: List <string> animfiles = new List <string>(); while (ByteConverter.ToInt32(file, tmpaddr) != -1) { animfiles.Add(file.GetCString(ByteConverter.ToInt32(file, tmpaddr))); tmpaddr += 4; } animationFiles = animfiles.ToArray(); break; case ChunkTypes.Morph: break; case ChunkTypes.Author: Author = file.GetCString(tmpaddr); break; case ChunkTypes.Tool: break; case ChunkTypes.Description: Description = file.GetCString(tmpaddr); break; case ChunkTypes.Texture: break; case ChunkTypes.End: finished = true; break; } } else { byte[] chunk = new byte[chunksz]; Array.Copy(file, tmpaddr, chunk, 0, chunksz); int chunkaddr = 0; switch (type) { case ChunkTypes.Label: while (ByteConverter.ToInt64(chunk, chunkaddr) != -1) { labels.Add(ByteConverter.ToInt32(chunk, chunkaddr), chunk.GetCString(ByteConverter.ToInt32(chunk, chunkaddr + 4))); chunkaddr += 8; } break; case ChunkTypes.Animation: List <string> animchunks = new List <string>(); while (ByteConverter.ToInt32(chunk, chunkaddr) != -1) { animchunks.Add(chunk.GetCString(ByteConverter.ToInt32(chunk, chunkaddr))); chunkaddr += 4; } animationFiles = animchunks.ToArray(); break; case ChunkTypes.Morph: break; case ChunkTypes.Author: Author = chunk.GetCString(chunkaddr); break; case ChunkTypes.Tool: break; case ChunkTypes.Description: Description = chunk.GetCString(chunkaddr); break; case ChunkTypes.End: finished = true; break; default: Metadata.Add((uint)type, chunk); break; } } tmpaddr = nextchunk; } } switch (magic) { case SA1MDL: Format = ModelFormat.Basic; break; case SA2MDL: Format = ModelFormat.Chunk; break; case SA2BMDL: Format = ModelFormat.GC; break; default: throw new FormatException("Not a valid SA1MDL/SA2MDL file."); } Model = new NJS_OBJECT(file, ByteConverter.ToInt32(file, 8), 0, Format, labels, attaches); if (filename != null) { string path = Path.GetDirectoryName(filename); if (File.Exists(Path.GetFileNameWithoutExtension(filename) + ".action")) { using (TextReader tr = File.OpenText(Path.GetFileNameWithoutExtension(filename) + ".action")) { List <string> animlist = new List <string>(); int count = File.ReadLines(Path.GetFileNameWithoutExtension(filename) + ".action").Count(); for (int i = 0; i < count; i++) { string line = tr.ReadLine(); if (File.Exists(Path.Combine(path, line))) { animlist.Add(line); } } animationFiles = animlist.ToArray(); } } List <NJS_MOTION> anims = new List <NJS_MOTION>(); try { foreach (string item in animationFiles) { if (Path.GetExtension(item).ToLowerInvariant() == ".json") { JsonSerializer js = new JsonSerializer() { Culture = System.Globalization.CultureInfo.InvariantCulture }; using (TextReader tr = File.OpenText(Path.Combine(path, item))) { using (JsonTextReader jtr = new JsonTextReader(tr)) anims.Add(js.Deserialize <NJS_MOTION>(jtr)); } } else { anims.Add(NJS_MOTION.Load(Path.Combine(path, item), Model.CountAnimated())); } } } catch { anims.Clear(); } Animations = anims.AsReadOnly(); } } ByteConverter.BigEndian = be; }