/// <summary> /// Load a material from a file buffer, with labels. /// </summary> /// <param name="file">byte array representing file</param> /// <param name="address">address of this material within 'file' byte array.</param> /// <param name="labels"></param> public NJS_MATERIAL(byte[] file, int address, Dictionary <int, string> labels) { if (ByteConverter.BigEndian) { //"Reverse" is for the order used in SADX Gamecube if (ByteConverter.Reverse) { DiffuseColor = Color.FromArgb(file[address + 3], file[address], file[address + 1], file[address + 2]); SpecularColor = Color.FromArgb(file[address + 7], file[address + 4], file[address + 5], file[address + 6]); } else { DiffuseColor = Color.FromArgb(file[address], file[address + 1], file[address + 2], file[address + 3]); SpecularColor = Color.FromArgb(file[address + 4], file[address + 5], file[address + 6], file[address + 7]); } } else { DiffuseColor = Color.FromArgb(file[address + 3], file[address + 2], file[address + 1], file[address]); SpecularColor = Color.FromArgb(file[address + 7], file[address + 6], file[address + 5], file[address + 4]); } Exponent = ByteConverter.ToSingle(file, address + 8); TextureID = ByteConverter.ToInt32(file, address + 0xC); Flags = ByteConverter.ToUInt32(file, address + 0x10); }
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); } }
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); }
public Animation(byte[] file, int address, uint imageBase, int nummodels, Dictionary <int, string> labels) { if (labels.ContainsKey(address)) { Name = labels[address]; } else { Name = "animation_" + address.ToString("X8"); } Int32 ptr = address; Frames = ByteConverter.ToInt32(file, ptr + 4); AnimFlags animtype = (AnimFlags)ByteConverter.ToUInt16(file, ptr + 8); int framesize = (ByteConverter.ToUInt16(file, ptr + 10) & 0xF) * 8; ptr = (int)(ByteConverter.ToUInt32(file, ptr) - imageBase); for (int i = 0; i < nummodels; i++) { Models.Add(i, new AnimModelData(file, ptr + (i * framesize), imageBase, animtype)); if (Models[i].Position.Count == 0 & Models[i].Rotation.Count == 0 & Models[i].Scale.Count == 0) { Models.Remove(i); } } ModelParts = nummodels; }
/// <summary> /// Load a material from a file buffer, with labels. /// </summary> /// <param name="file">byte array representing file</param> /// <param name="address">address of this material within 'file' byte array.</param> /// <param name="labels"></param> public NJS_MATERIAL(byte[] file, int address, Dictionary <int, string> labels) { DiffuseColor = Color.FromArgb(ByteConverter.ToInt32(file, address)); SpecularColor = Color.FromArgb(ByteConverter.ToInt32(file, address + 4)); Exponent = ByteConverter.ToSingle(file, address + 8); TextureID = ByteConverter.ToInt32(file, address + 0xC); Flags = ByteConverter.ToUInt32(file, address + 0x10); }
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 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 static Color FromBytes(byte[] file, int address, ColorType type) { switch (type) { case ColorType.ARGB8888_32: return(Color.FromArgb(ByteConverter.ToInt32(file, address))); case ColorType.XRGB8888_32: return(Color.FromArgb(unchecked ((int)(ByteConverter.ToUInt32(file, address) | 0xFF000000u)))); case ColorType.ARGB8888_16: return(Color.FromArgb((ByteConverter.ToUInt16(file, address + 2) << 16) | ByteConverter.ToUInt16(file, address))); case ColorType.XRGB8888_16: return(Color.FromArgb(unchecked ((int)((uint)((ByteConverter.ToUInt16(file, address + 2) << 16) | ByteConverter.ToUInt16(file, address)) | 0xFF000000u)))); case ColorType.ARGB4444: ushort value = ByteConverter.ToUInt16(file, address); int a = value >> 12; int r = (value >> 8) & 0xF; int g = (value >> 4) & 0xF; int b = value & 0xF; return(Color.FromArgb( a | (a << 4), r | (r << 4), g | (g << 4), b | (b << 4) )); case ColorType.RGB565: value = ByteConverter.ToUInt16(file, address); r = value >> 11; g = (value >> 5) & 0x3F; b = value & 0x1F; return(Color.FromArgb( r << 3 | r >> 2, g << 2 | g >> 4, b << 3 | b >> 2 )); } throw new ArgumentOutOfRangeException("type"); }
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 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 BasicAttach(byte[] file, int address, uint imageBase, bool DX, Dictionary <int, string> labels) : this() { if (labels.ContainsKey(address)) { Name = labels[address]; } else { Name = "attach_" + address.ToString("X8"); } Vertex = new Vertex[ByteConverter.ToInt32(file, address + 8)]; Normal = new Vertex[Vertex.Length]; int tmpaddr = (int)(ByteConverter.ToUInt32(file, address) - imageBase); if (labels.ContainsKey(tmpaddr)) { VertexName = labels[tmpaddr]; } else { VertexName = "vertex_" + tmpaddr.ToString("X8"); } for (int i = 0; i < Vertex.Length; i++) { Vertex[i] = new Vertex(file, tmpaddr); tmpaddr += SAModel.Vertex.Size; } tmpaddr = ByteConverter.ToInt32(file, address + 4); if (tmpaddr != 0) { tmpaddr = (int)((uint)tmpaddr - imageBase); if (labels.ContainsKey(tmpaddr)) { NormalName = labels[tmpaddr]; } else { NormalName = "normal_" + tmpaddr.ToString("X8"); } for (int i = 0; i < Vertex.Length; i++) { Normal[i] = new Vertex(file, tmpaddr); tmpaddr += SAModel.Vertex.Size; } } else { for (int i = 0; i < Vertex.Length; i++) { Normal[i] = new Vertex(0, 1, 0); } } int maxmat = -1; int meshcnt = ByteConverter.ToInt16(file, address + 0x14); tmpaddr = ByteConverter.ToInt32(file, address + 0xC); if (tmpaddr != 0) { tmpaddr = (int)unchecked ((uint)tmpaddr - imageBase); if (labels.ContainsKey(tmpaddr)) { MeshName = labels[tmpaddr]; } else { MeshName = "meshlist_" + tmpaddr.ToString("X8"); } for (int i = 0; i < meshcnt; i++) { Mesh.Add(new NJS_MESHSET(file, tmpaddr, imageBase, labels)); maxmat = Math.Max(maxmat, Mesh[i].MaterialID); tmpaddr += NJS_MESHSET.Size(DX); } } // fixes case where model declares material array as shorter than it really is int matcnt = Math.Max(ByteConverter.ToInt16(file, address + 0x16), maxmat + 1); tmpaddr = ByteConverter.ToInt32(file, address + 0x10); if (tmpaddr != 0) { tmpaddr = (int)unchecked ((uint)tmpaddr - imageBase); if (labels.ContainsKey(tmpaddr)) { MaterialName = labels[tmpaddr]; } else { MaterialName = "matlist_" + tmpaddr.ToString("X8"); } for (int i = 0; i < matcnt; i++) { Material.Add(new NJS_MATERIAL(file, tmpaddr, labels)); tmpaddr += NJS_MATERIAL.Size; } } Bounds = new BoundingSphere(file, address + 0x18); }
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 VertexChunk(byte[] file, int address) : this() { Header1 = ByteConverter.ToUInt32(file, address); Header2 = ByteConverter.ToUInt32(file, address + 4); address = address + 8; for (int i = 0; i < VertexCount; i++) { switch (Type) { case ChunkType.Vertex_VertexSH: Vertices.Add(new Vertex(file, address)); address += Vertex.Size + sizeof(float); break; case ChunkType.Vertex_VertexNormalSH: Vertices.Add(new Vertex(file, address)); address += Vertex.Size + sizeof(float); Normals.Add(new Vertex(file, address)); address += Vertex.Size + sizeof(float); break; case ChunkType.Vertex_Vertex: Vertices.Add(new Vertex(file, address)); address += Vertex.Size; break; case ChunkType.Vertex_VertexDiffuse8: Vertices.Add(new Vertex(file, address)); address += Vertex.Size; Diffuse.Add(VColor.FromBytes(file, address, ColorType.ARGB8888_32)); address += VColor.Size(ColorType.ARGB8888_32); break; case ChunkType.Vertex_VertexUserFlags: Vertices.Add(new Vertex(file, address)); address += Vertex.Size; UserFlags.Add(ByteConverter.ToUInt32(file, address)); address += sizeof(uint); break; case ChunkType.Vertex_VertexNinjaFlags: Vertices.Add(new Vertex(file, address)); address += Vertex.Size; NinjaFlags.Add(ByteConverter.ToUInt32(file, address)); address += sizeof(uint); break; case ChunkType.Vertex_VertexDiffuseSpecular5: Vertices.Add(new Vertex(file, address)); address += Vertex.Size; uint tmpcolor = ByteConverter.ToUInt32(file, address); address += sizeof(uint); Diffuse.Add(VColor.FromBytes(ByteConverter.GetBytes((ushort)(tmpcolor & 0xFFFF)), 0, ColorType.RGB565)); Specular.Add(VColor.FromBytes(ByteConverter.GetBytes((ushort)(tmpcolor >> 16)), 0, ColorType.RGB565)); break; case ChunkType.Vertex_VertexDiffuseSpecular4: Vertices.Add(new Vertex(file, address)); address += Vertex.Size; tmpcolor = ByteConverter.ToUInt32(file, address); address += sizeof(uint); Diffuse.Add(VColor.FromBytes(ByteConverter.GetBytes((ushort)(tmpcolor & 0xFFFF)), 0, ColorType.ARGB4444)); Specular.Add(VColor.FromBytes(ByteConverter.GetBytes((ushort)(tmpcolor >> 16)), 0, ColorType.RGB565)); break; case ChunkType.Vertex_VertexNormal: Vertices.Add(new Vertex(file, address)); address += Vertex.Size; Normals.Add(new Vertex(file, address)); address += Vertex.Size; break; case ChunkType.Vertex_VertexNormalDiffuse8: Vertices.Add(new Vertex(file, address)); address += Vertex.Size; Normals.Add(new Vertex(file, address)); address += Vertex.Size; Diffuse.Add(VColor.FromBytes(file, address, ColorType.ARGB8888_32)); address += VColor.Size(ColorType.ARGB8888_32); break; case ChunkType.Vertex_VertexNormalUserFlags: Vertices.Add(new Vertex(file, address)); address += Vertex.Size; Normals.Add(new Vertex(file, address)); address += Vertex.Size; UserFlags.Add(ByteConverter.ToUInt32(file, address)); address += sizeof(uint); break; case ChunkType.Vertex_VertexNormalNinjaFlags: Vertices.Add(new Vertex(file, address)); address += Vertex.Size; Normals.Add(new Vertex(file, address)); address += Vertex.Size; NinjaFlags.Add(ByteConverter.ToUInt32(file, address)); address += sizeof(uint); break; case ChunkType.Vertex_VertexNormalDiffuseSpecular5: Vertices.Add(new Vertex(file, address)); address += Vertex.Size; Normals.Add(new Vertex(file, address)); address += Vertex.Size; tmpcolor = ByteConverter.ToUInt32(file, address); address += sizeof(uint); Diffuse.Add(VColor.FromBytes(ByteConverter.GetBytes((ushort)(tmpcolor & 0xFFFF)), 0, ColorType.RGB565)); Specular.Add(VColor.FromBytes(ByteConverter.GetBytes((ushort)(tmpcolor >> 16)), 0, ColorType.RGB565)); break; case ChunkType.Vertex_VertexNormalDiffuseSpecular4: Vertices.Add(new Vertex(file, address)); address += Vertex.Size; Normals.Add(new Vertex(file, address)); address += Vertex.Size; tmpcolor = ByteConverter.ToUInt32(file, address); address += sizeof(uint); Diffuse.Add(VColor.FromBytes(ByteConverter.GetBytes((ushort)(tmpcolor & 0xFFFF)), 0, ColorType.ARGB4444)); Specular.Add(VColor.FromBytes(ByteConverter.GetBytes((ushort)(tmpcolor >> 16)), 0, ColorType.RGB565)); break; default: throw new NotSupportedException("Unsupported chunk type " + Type + " at " + address.ToString("X8") + "."); } } }
public AnimModelData(byte[] file, int address, uint imageBase, AnimFlags animtype) { uint posoff = 0, rotoff = 0, scaoff = 0; if ((animtype & AnimFlags.Translate) == AnimFlags.Translate) { posoff = ByteConverter.ToUInt32(file, address); if (posoff > 0) { posoff = posoff - imageBase; } address += 4; } if ((animtype & AnimFlags.Rotate) == AnimFlags.Rotate) { rotoff = ByteConverter.ToUInt32(file, address); if (rotoff > 0) { rotoff = rotoff - imageBase; } address += 4; } if ((animtype & AnimFlags.Scale) == AnimFlags.Scale) { scaoff = ByteConverter.ToUInt32(file, address); if (scaoff > 0) { scaoff = scaoff - imageBase; } address += 4; } try { int tmpaddr; if ((animtype & AnimFlags.Translate) == AnimFlags.Translate) { int posframes = ByteConverter.ToInt32(file, address); if (posframes > 0) { tmpaddr = (int)posoff; for (int i = 0; i < posframes; i++) { Position.Add(ByteConverter.ToInt32(file, tmpaddr), new Vertex(file, tmpaddr + 4)); tmpaddr += 16; } } address += 4; } if ((animtype & AnimFlags.Rotate) == AnimFlags.Rotate) { int rotframes = ByteConverter.ToInt32(file, address); if (rotframes > 0) { tmpaddr = (int)rotoff; for (int i = 0; i < rotframes; i++) { Rotation.Add(ByteConverter.ToInt32(file, tmpaddr), new Rotation(file, tmpaddr + 4)); tmpaddr += 16; } } address += 4; } if ((animtype & AnimFlags.Scale) == AnimFlags.Scale) { int scaframes = ByteConverter.ToInt32(file, address); if (scaframes > 0) { tmpaddr = (int)scaoff; for (int i = 0; i < scaframes; i++) { Scale.Add(ByteConverter.ToInt32(file, tmpaddr), new Vertex(file, tmpaddr + 4)); tmpaddr += 16; } } address += 4; } } catch (ArgumentOutOfRangeException) { } }
public static LandTable LoadFromFile(string filename) { 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 SA1LVL/SA2LVL file."); } Dictionary <int, string> labels = new Dictionary <int, string>(); string author = null, description = null, tool = null; Dictionary <uint, byte[]> meta = new Dictionary <uint, byte[]>(); if (version < 2) { if (version == 1) { int tmpaddr = ByteConverter.ToInt32(file, 0xC); 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); } } } } else { int 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.Author: author = file.GetCString(tmpaddr); break; case ChunkTypes.Tool: tool = file.GetCString(tmpaddr); break; case ChunkTypes.Description: description = file.GetCString(tmpaddr); 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.Author: author = chunk.GetCString(0); break; case ChunkTypes.Tool: tool = chunk.GetCString(0); break; case ChunkTypes.Description: description = chunk.GetCString(0); break; case ChunkTypes.End: finished = true; break; default: meta.Add((uint)type, chunk); break; } } tmpaddr = nextchunk; } } } if (magic == SA1LVL) { LandTable table = new LandTable(file, ByteConverter.ToInt32(file, 8), 0, LandTableFormat.SA1, labels) { Author = author, Description = description, Tool = tool, Metadata = meta }; ByteConverter.BigEndian = be; return(table); } if (magic == SA2LVL) { LandTable table = new LandTable(file, ByteConverter.ToInt32(file, 8), 0, LandTableFormat.SA2, labels) { Author = author, Description = description, Tool = tool, Metadata = meta }; ByteConverter.BigEndian = be; return(table); } ByteConverter.BigEndian = be; throw new FormatException("Not a valid SA1LVL/SA2LVL file."); }
static void Main(string[] args) { if (args.Length == 0) { Console.Write("Filename: "); args = new string[] { Console.ReadLine().Trim('"') }; } foreach (string filename in args) { Console.WriteLine("Splitting file {0}...", filename); byte[] fc; if (Path.GetExtension(filename).Equals(".prs", StringComparison.OrdinalIgnoreCase)) { fc = Prs.Decompress(filename); } else { fc = File.ReadAllBytes(filename); } EventIniData ini = new EventIniData() { Name = Path.GetFileNameWithoutExtension(filename) }; string path = Directory.CreateDirectory(Path.Combine(Path.GetDirectoryName(Path.GetFullPath(filename)), Path.GetFileNameWithoutExtension(filename))).FullName; uint key; List <NJS_MOTION> motions = null; bool battle; if (fc[0] == 0x81) { Console.WriteLine("File is in GC/PC format."); ByteConverter.BigEndian = true; key = 0x8125FE60; ini.Game = Game.SA2B; battle = true; motions = ReadMotionFile(Path.ChangeExtension(filename, null) + "motion.bin"); ini.Motions = motions.Select(a => a?.Name).ToList(); foreach (var mtn in motions.Where(a => a != null)) { motionfiles[mtn.Name] = new MotionInfo(null, mtn); } } else { Console.WriteLine("File is in DC format."); ByteConverter.BigEndian = false; key = 0xC600000; ini.Game = Game.SA2; battle = false; } int ptr = fc.GetPointer(0x20, key); if (ptr != 0) { for (int i = 0; i < (battle ? 18 : 16); i++) { string upnam = upgradenames[i]; string chnam = upnam; switch (i) { case 0: chnam = "Sonic"; break; case 4: chnam = "Shadow"; break; case 6: chnam = "Knuckles"; break; case 12: chnam = "Rouge"; break; case 16: chnam = "Mech Tails"; break; case 17: chnam = "Mech Eggman"; break; } UpgradeInfo info = new UpgradeInfo(); info.RootNode = GetModel(fc, ptr, key, $"{chnam} Root.sa2mdl"); if (info.RootNode != null) { int ptr2 = fc.GetPointer(ptr + 4, key); if (ptr2 != 0) { info.AttachNode1 = $"object_{ptr2:X8}"; } info.Model1 = GetModel(fc, ptr + 8, key, $"{upnam} Model 1.sa2mdl"); ptr2 = fc.GetPointer(ptr + 0xC, key); if (ptr2 != 0) { info.AttachNode2 = $"object_{ptr2:X8}"; } info.Model2 = GetModel(fc, ptr + 0x10, key, $"{upnam} Model 2.sa2mdl"); } ini.Upgrades.Add(info); ptr += 0x14; } } else { Console.WriteLine("Event contains no character upgrades."); } ptr = fc.GetPointer(0x18, key); if (ptr != 0) { for (int i = 0; i < 93; i++) { string name = GetModel(fc, ptr, key, $"Mech Part {i + 1}.sa2mdl"); if (name != null) { ini.MechParts.Add(i, name); } ptr += 4; } } else { Console.WriteLine("Event contains no mech parts."); } int gcnt = ByteConverter.ToInt32(fc, 8); ptr = fc.GetPointer(0, key); if (ptr != 0) { Console.WriteLine("Event contains {0} scene(s).", gcnt + 1); for (int gn = 0; gn <= gcnt; gn++) { Directory.CreateDirectory(Path.Combine(path, $"Scene {gn + 1}")); SceneInfo scn = new SceneInfo(); int ptr2 = fc.GetPointer(ptr, key); int ecnt = ByteConverter.ToInt32(fc, ptr + 4); if (ptr2 != 0) { Console.WriteLine("Scene {0} contains {1} entit{2}.", gn + 1, ecnt, ecnt == 1 ? "y" : "ies"); for (int en = 0; en < ecnt; en++) { EntityInfo ent = new EntityInfo(); ent.Model = GetModel(fc, ptr2, key, $"Scene {gn + 1}\\Entity {en + 1} Model.sa2mdl"); if (ent.Model != null) { ent.Motion = GetMotion(fc, ptr2 + 4, key, $"Scene {gn + 1}\\Entity {en + 1} Motion.saanim", motions, modelfiles[ent.Model].Model.CountAnimated()); if (ent.Motion != null) { modelfiles[ent.Model].Motions.Add(motionfiles[ent.Motion].Filename); } ent.ShapeMotion = GetMotion(fc, ptr2 + 8, key, $"Scene {gn + 1}\\Entity {en + 1} Shape Motion.saanim", motions, modelfiles[ent.Model].Model.CountMorph()); if (ent.ShapeMotion != null) { modelfiles[ent.Model].Motions.Add(motionfiles[ent.ShapeMotion].Filename); } } if (battle) { ent.GCModel = GetGCModel(fc, ptr2 + 12, key, $"Scene {gn + 1}\\Entity {en + 1} GC Model.sa2bmdl"); ent.ShadowModel = GetModel(fc, ptr2 + 16, key, $"Scene {gn + 1}\\Entity {en + 1} Shadow Model.sa2mdl"); ent.Position = new Vertex(fc, ptr2 + 24); ent.Flags = ByteConverter.ToUInt32(fc, ptr2 + 40); } else { ent.Position = new Vertex(fc, ptr2 + 16); ent.Flags = ByteConverter.ToUInt32(fc, ptr2 + 28); } scn.Entities.Add(ent); ptr2 += ini.Game == Game.SA2B ? 0x2C : 0x20; } } else { Console.WriteLine("Scene {0} contains no entities.", gn + 1); } ptr2 = fc.GetPointer(ptr + 8, key); if (ptr2 != 0) { int cnt = ByteConverter.ToInt32(fc, ptr + 12); for (int i = 0; i < cnt; i++) { scn.CameraMotions.Add(GetMotion(fc, ptr2, key, $"Scene {gn + 1}\\Camera Motion {i + 1}.saanim", motions, 1)); ptr2 += sizeof(int); } } ptr2 = fc.GetPointer(ptr + 0x18, key); if (ptr2 != 0) { BigInfo big = new BigInfo(); big.Model = GetModel(fc, ptr2, key, $"Scene {gn + 1}\\Big Model.sa2mdl"); if (big.Model != null) { int anicnt = modelfiles[big.Model].Model.CountAnimated(); int ptr3 = fc.GetPointer(ptr2 + 4, key); if (ptr3 != 0) { int cnt = ByteConverter.ToInt32(fc, ptr2 + 8); for (int i = 0; i < cnt; i++) { big.Motions.Add(new string[] { GetMotion(fc, ptr3, key, $"Scene {gn + 1}\\Big Motion {i + 1}a.saanim", motions, anicnt), GetMotion(fc, ptr3 + 4, key, $"Scene {gn + 1}\\Big Motion {i + 1}b.saanim", motions, anicnt) }); ptr3 += 8; } } } big.Unknown = ByteConverter.ToInt32(fc, ptr2 + 12); scn.Big = big; } scn.FrameCount = ByteConverter.ToInt32(fc, ptr + 28); ini.Scenes.Add(scn); ptr += 0x20; } } else { Console.WriteLine("Event contains no scenes."); } ptr = fc.GetPointer(0x1C, key); if (ptr != 0) { ini.TailsTails = GetModel(fc, ptr, key, $"Tails' tails.sa2mdl"); } else { Console.WriteLine("Event does not contain Tails' tails."); } foreach (var item in motionfiles.Values) { string fn = item.Filename ?? $"Unknown Motion {motions.IndexOf(item.Motion)}.saanim"; string fp = Path.Combine(path, fn); item.Motion.Save(fp); ini.Files.Add(fn, HelperFunctions.FileHash(fp)); } foreach (var item in modelfiles.Values) { string fp = Path.Combine(path, item.Filename); ModelFile.CreateFile(fp, item.Model, item.Motions.ToArray(), null, null, null, item.Format); ini.Files.Add(item.Filename, HelperFunctions.FileHash(fp)); } JsonSerializer js = new JsonSerializer { Formatting = Formatting.Indented, NullValueHandling = NullValueHandling.Ignore }; using (var tw = File.CreateText(Path.Combine(path, Path.ChangeExtension(Path.GetFileName(filename), ".json")))) js.Serialize(tw, ini); } }
public NJS_MESHSET(byte[] file, int address, uint imageBase, Dictionary <int, string> labels) { MaterialID = ByteConverter.ToUInt16(file, address); PolyType = (Basic_PolyType)(MaterialID >> 0xE); MaterialID &= 0x3FFF; Poly[] polys = new Poly[ByteConverter.ToInt16(file, address + 2)]; int tmpaddr = (int)(ByteConverter.ToUInt32(file, address + 4) - imageBase); if (labels.ContainsKey(tmpaddr)) { PolyName = labels[tmpaddr]; } else { PolyName = "poly_" + tmpaddr.ToString("X8"); } int striptotal = 0; for (int i = 0; i < polys.Length; i++) { polys[i] = SAModel.Poly.CreatePoly(PolyType, file, tmpaddr); striptotal += polys[i].Indexes.Length; tmpaddr += polys[i].Size; } Poly = new ReadOnlyCollection <Poly>(polys); PAttr = ByteConverter.ToInt32(file, address + 8); tmpaddr = ByteConverter.ToInt32(file, address + 0xC); if (tmpaddr != 0) { tmpaddr = (int)unchecked ((uint)tmpaddr - imageBase); if (labels.ContainsKey(tmpaddr)) { PolyNormalName = labels[tmpaddr]; } else { PolyNormalName = "polynormal_" + tmpaddr.ToString("X8"); } PolyNormal = new Vertex[polys.Length]; for (int i = 0; i < polys.Length; i++) { PolyNormal[i] = new Vertex(file, tmpaddr); tmpaddr += Vertex.Size; } } else { PolyNormalName = "polynormal_" + Extensions.GenerateIdentifier(); } tmpaddr = ByteConverter.ToInt32(file, address + 0x10); if (tmpaddr != 0) { tmpaddr = (int)unchecked ((uint)tmpaddr - imageBase); if (labels.ContainsKey(tmpaddr)) { VColorName = labels[tmpaddr]; } else { VColorName = "vcolor_" + tmpaddr.ToString("X8"); } VColor = new Color[striptotal]; for (int i = 0; i < striptotal; i++) { VColor[i] = SAModel.VColor.FromBytes(file, tmpaddr); tmpaddr += SAModel.VColor.Size(ColorType.ARGB8888_32); } } else { VColorName = "vcolor_" + Extensions.GenerateIdentifier(); } tmpaddr = ByteConverter.ToInt32(file, address + 0x14); if (tmpaddr != 0) { tmpaddr = (int)unchecked ((uint)tmpaddr - imageBase); if (labels.ContainsKey(tmpaddr)) { UVName = labels[tmpaddr]; } else { UVName = "uv_" + tmpaddr.ToString("X8"); } UV = new UV[striptotal]; for (int i = 0; i < striptotal; i++) { UV[i] = new UV(file, tmpaddr); tmpaddr += SAModel.UV.Size; } } else { UVName = "uv_" + Extensions.GenerateIdentifier(); } }
public ChunkAttach(byte[] file, int address, uint imageBase, Dictionary <int, string> labels) : this() { if (labels.ContainsKey(address)) { Name = labels[address]; } else { Name = "attach_" + address.ToString("X8"); } ChunkType ctype; int tmpaddr = ByteConverter.ToInt32(file, address); if (tmpaddr != 0) { tmpaddr = (int)unchecked ((uint)tmpaddr - imageBase); Vertex = new List <VertexChunk>(); if (labels.ContainsKey(tmpaddr)) { VertexName = labels[tmpaddr]; } else { VertexName = "vertex_" + tmpaddr.ToString("X8"); } ctype = (ChunkType)(ByteConverter.ToUInt32(file, tmpaddr) & 0xFF); while (ctype != ChunkType.End) { VertexChunk chunk = new VertexChunk(file, tmpaddr); Vertex.Add(chunk); tmpaddr += (chunk.Size * 4) + 4; ctype = (ChunkType)(ByteConverter.ToUInt32(file, tmpaddr) & 0xFF); } } tmpaddr = ByteConverter.ToInt32(file, address + 4); if (tmpaddr != 0) { tmpaddr = (int)unchecked ((uint)tmpaddr - imageBase); Poly = new List <PolyChunk>(); if (labels.ContainsKey(tmpaddr)) { PolyName = labels[tmpaddr]; } else { PolyName = "poly_" + tmpaddr.ToString("X8"); } PolyChunk chunk = PolyChunk.Load(file, tmpaddr); while (chunk.Type != ChunkType.End) { if (chunk.Type != ChunkType.Null) { Poly.Add(chunk); } tmpaddr += chunk.ByteSize; chunk = PolyChunk.Load(file, tmpaddr); } } Bounds = new BoundingSphere(file, address + 8); }
public static Color FromBytes(byte[] file, int address, ColorType type) { switch (type) { case ColorType.ARGB8888_32: if (address > file.Length - 4) { return(Color.FromArgb(0, 0, 0, 0)); } if (ByteConverter.BigEndian) { //"Reverse" is for the order used in SADX Gamecube if (ByteConverter.Reverse) { return(Color.FromArgb(file[address + 3], file[address], file[address + 1], file[address + 2])); } else { return(Color.FromArgb(file[address], file[address + 1], file[address + 2], file[address + 3])); } } else { return(Color.FromArgb(file[address + 3], file[address + 2], file[address + 1], file[address])); } case ColorType.XRGB8888_32: return(Color.FromArgb(unchecked ((int)(ByteConverter.ToUInt32(file, address) | 0xFF000000u)))); case ColorType.ARGB8888_16: return(Color.FromArgb((ByteConverter.ToUInt16(file, address + 2) << 16) | ByteConverter.ToUInt16(file, address))); case ColorType.XRGB8888_16: return(Color.FromArgb(unchecked ((int)((uint)((ByteConverter.ToUInt16(file, address + 2) << 16) | ByteConverter.ToUInt16(file, address)) | 0xFF000000u)))); case ColorType.ARGB4444: ushort value = ByteConverter.ToUInt16(file, address); int a = value >> 12; int r = (value >> 8) & 0xF; int g = (value >> 4) & 0xF; int b = value & 0xF; return(Color.FromArgb( a | (a << 4), r | (r << 4), g | (g << 4), b | (b << 4) )); case ColorType.RGB565: value = ByteConverter.ToUInt16(file, address); r = value >> 11; g = (value >> 5) & 0x3F; b = value & 0x1F; return(Color.FromArgb( r << 3 | r >> 2, g << 2 | g >> 4, b << 3 | b >> 2 )); } throw new ArgumentOutOfRangeException("type"); }
public LandTable(byte[] file, int address, uint imageBase, LandTableFormat format, Dictionary <int, string> labels) { Format = format; if (labels.ContainsKey(address)) { Name = labels[address]; } else { Name = "landtable_" + address.ToString("X8"); } short colcnt = ByteConverter.ToInt16(file, address); switch (format) { case LandTableFormat.SA1: case LandTableFormat.SADX: short anicnt = ByteConverter.ToInt16(file, address + 2); Flags = ByteConverter.ToInt32(file, address + 4); Unknown1 = ByteConverter.ToSingle(file, address + 8); COL = new List <COL>(); int tmpaddr = ByteConverter.ToInt32(file, address + 0xC); if (tmpaddr != 0) { tmpaddr = (int)unchecked ((uint)tmpaddr - imageBase); if (labels.ContainsKey(tmpaddr)) { COLName = labels[tmpaddr]; } else { COLName = "collist_" + tmpaddr.ToString("X8"); } for (int i = 0; i < colcnt; i++) { COL.Add(new COL(file, tmpaddr, imageBase, format, labels)); tmpaddr += SAModel.COL.Size(format); } } else { COLName = "collist_" + Extensions.GenerateIdentifier(); } Anim = new List <GeoAnimData>(); tmpaddr = ByteConverter.ToInt32(file, address + 0x10); if (tmpaddr != 0) { tmpaddr = (int)unchecked ((uint)tmpaddr - imageBase); if (labels.ContainsKey(tmpaddr)) { AnimName = labels[tmpaddr]; } else { AnimName = "animlist_" + tmpaddr.ToString("X8"); } for (int i = 0; i < anicnt; i++) { Anim.Add(new GeoAnimData(file, tmpaddr, imageBase, format, labels)); tmpaddr += GeoAnimData.Size; } } else { AnimName = "animlist_" + Extensions.GenerateIdentifier(); } tmpaddr = ByteConverter.ToInt32(file, address + 0x14); if (tmpaddr != 0) { tmpaddr = (int)unchecked ((uint)tmpaddr - imageBase); TextureFileName = file.GetCString(tmpaddr, Encoding.ASCII); } TextureList = ByteConverter.ToUInt32(file, address + 0x18); Unknown2 = ByteConverter.ToInt32(file, address + 0x1C); Unknown3 = ByteConverter.ToInt32(file, address + 0x20); break; case LandTableFormat.SA2: short cnkcnt = ByteConverter.ToInt16(file, address + 2); Unknown1 = ByteConverter.ToSingle(file, address + 0xC); COL = new List <COL>(); tmpaddr = ByteConverter.ToInt32(file, address + 0x10); if (tmpaddr != 0) { tmpaddr = (int)unchecked ((uint)tmpaddr - imageBase); if (labels.ContainsKey(tmpaddr)) { COLName = labels[tmpaddr]; } else { COLName = "collist_" + tmpaddr.ToString("X8"); } for (int i = 0; i < colcnt; i++) { COL.Add(new COL(file, tmpaddr, imageBase, format, labels, cnkcnt < 0 ? null : (bool?)(i >= cnkcnt))); tmpaddr += SAModel.COL.Size(format); } } else { COLName = "collist_" + Extensions.GenerateIdentifier(); } Anim = new List <GeoAnimData>(); AnimName = "animlist_" + Extensions.GenerateIdentifier(); tmpaddr = ByteConverter.ToInt32(file, address + 0x18); if (tmpaddr != 0) { tmpaddr = (int)unchecked ((uint)tmpaddr - imageBase); TextureFileName = file.GetCString(tmpaddr, Encoding.ASCII); } TextureList = ByteConverter.ToUInt32(file, address + 0x1C); break; } Metadata = new Dictionary <uint, byte[]>(); }
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; }