private static string GetMotion(byte[] fc, int address, uint key, string fn, List <NJS_MOTION> motions, int cnt) { NJS_MOTION mtn = null; if (motions != null) { mtn = motions[ByteConverter.ToInt32(fc, address)]; } else { int ptr3 = fc.GetPointer(address, key); if (ptr3 != 0) { mtn = new NJS_MOTION(fc, ptr3, key, cnt); } } if (mtn == null) { return(null); } if (!motionfiles.ContainsKey(mtn.Name) || motionfiles[mtn.Name].Filename == null) { motionfiles[mtn.Name] = new MotionInfo(fn, mtn); } return(mtn.Name); }
public EventEntity(byte[] file, int address, uint imageBase, bool battle, Dictionary <string, NJS_OBJECT> models, List <NJS_MOTION> motions) { Model = Event.GetModel(file, address, imageBase, models); if (battle) { Motion = motions[ByteConverter.ToInt32(file, address + 4)]; ShapeMotion = motions[ByteConverter.ToInt32(file, address + 8)]; ShadowModel = Event.GetModel(file, address + 16, imageBase, models); Position = new Vertex(file, address + 24); Flags = ByteConverter.ToUInt32(file, address + 40); } else { int ptr = file.GetPointer(address + 4, imageBase); if (ptr != 0) { Motion = new NJS_MOTION(file, ptr, imageBase, Model.CountAnimated()); } ptr = file.GetPointer(address + 8, imageBase); if (ptr != 0) { ShapeMotion = new NJS_MOTION(file, ptr, imageBase, Model.CountMorph()); } Position = new Vertex(file, address + 16); Flags = ByteConverter.ToUInt32(file, address + 28); } }
public Event(string filename) { byte[] fc; if (Path.GetExtension(filename).Equals(".prs", StringComparison.OrdinalIgnoreCase)) { fc = Prs.Decompress(filename); } else { fc = File.ReadAllBytes(filename); } bool battle = false; uint key; if (fc[0] == 0x81) { ByteConverter.BigEndian = true; key = 0x8125FE60; battle = true; } else { ByteConverter.BigEndian = false; key = 0xC600000; } List <NJS_MOTION> motions = null; if (battle) { motions = ReadMotionFile(Path.ChangeExtension(filename, null) + "motion.bin"); } Dictionary <string, NJS_OBJECT> models = new Dictionary <string, NJS_OBJECT>(); int ptr = fc.GetPointer(0x20, key); if (ptr != 0) { int cnt = battle ? 18 : 16; Upgrades = new EventUpgrade[cnt]; for (int i = 0; i < cnt; i++) { Upgrades[i] = new EventUpgrade(fc, ptr, key, models); ptr += EventUpgrade.Size; } } int gcnt = ByteConverter.ToInt32(fc, 8); ptr = fc.GetPointer(0, key); if (ptr != 0) { Scenes = new List <EventScene>(); for (int gn = 0; gn <= gcnt; gn++) { Scenes.Add(new EventScene(fc, ptr, key, battle, models, motions)); ptr += EventScene.Size; } } }
public EventScene(byte[] file, int address, uint imageBase, bool battle, Dictionary <string, NJS_OBJECT> models, List <NJS_MOTION> motions) { int ptr = file.GetPointer(address, imageBase); if (ptr != 0) { int cnt = ByteConverter.ToInt32(file, address + 4); Entities = new List <EventEntity>(cnt); for (int i = 0; i < cnt; i++) { Entities.Add(new EventEntity(file, ptr, imageBase, battle, models, motions)); ptr += EventEntity.Size(battle); } } ptr = file.GetPointer(address + 8, imageBase); if (ptr != 0) { int cnt = ByteConverter.ToInt32(file, address + 12); CameraMotions = new List <NJS_MOTION>(cnt); for (int i = 0; i < cnt; i++) { if (battle) { CameraMotions.Add(motions[ByteConverter.ToInt32(file, ptr)]); } else { CameraMotions.Add(new NJS_MOTION(file, file.GetPointer(ptr, imageBase), imageBase, 1)); } ptr += sizeof(int); } } ptr = file.GetPointer(address + 24, imageBase); if (ptr != 0) { Big = new EventBig(file, ptr, imageBase, battle, models, motions); } FrameCount = ByteConverter.ToInt32(file, address + 28); }
public static List <NJS_MOTION> ReadMotionFile(string filename) { List <NJS_MOTION> motions = new List <NJS_MOTION>(); byte[] fc = File.ReadAllBytes(filename); int addr = 0; while (ByteConverter.ToInt64(fc, addr) != 0) { int ptr = ByteConverter.ToInt32(fc, addr); if (ptr == -1) { motions.Add(null); } else { motions.Add(new NJS_MOTION(fc, ptr, 0, ByteConverter.ToInt32(fc, addr + 4))); } addr += 8; } return(motions); }
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); Dictionary <int, Attach> attaches = new Dictionary <int, Attach>(); 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, attaches)); 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, attaches)); 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: case LandTableFormat.SA2B: 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), attaches)); 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 void SaveToFile(string filename, LandTableFormat format) { bool be = ByteConverter.BigEndian; ByteConverter.BigEndian = false; if (format == LandTableFormat.SADX) { format = LandTableFormat.SA1; } List <byte> file = new List <byte>(); ulong magic; switch (format) { case LandTableFormat.SA1: magic = SA1LVLVer; break; case LandTableFormat.SA2: magic = SA2LVLVer; break; case LandTableFormat.SA2B: magic = SA2BLVLVer; break; default: throw new ArgumentException("Cannot save " + format + " format levels to file!", "format"); } file.AddRange(ByteConverter.GetBytes(magic)); Dictionary <string, uint> labels = new Dictionary <string, uint>(); byte[] lvl = GetBytes(0x10, format, labels, out uint addr); file.AddRange(ByteConverter.GetBytes(addr + 0x10)); file.AddRange(ByteConverter.GetBytes(lvl.Length + 0x10)); file.AddRange(lvl); if (labels.Count > 0) { List <byte> chunk = new List <byte>(labels.Count * 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 (!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); } 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 byte[] GetBytes(uint imageBase, LandTableFormat format, Dictionary <string, uint> labels, out uint address) { List <byte> result = new List <byte>(); byte[] tmpbyte; uint[] colmdladdrs = new uint[COL.Count]; uint tmpaddr; List <COL> cnk = new List <COL>(); List <COL> bas = new List <COL>(); foreach (COL item in COL) { if (item.Model.Attach is BasicAttach) { bas.Add(item); } else { cnk.Add(item); } } COL.Clear(); COL.AddRange(cnk); COL.AddRange(bas); for (int i = 0; i < COL.Count; i++) { if (labels.ContainsKey(COL[i].Model.Name)) { colmdladdrs[i] = labels[COL[i].Model.Name]; } else { result.Align(4); tmpbyte = COL[i].Model.GetBytes(imageBase + (uint)result.Count, format == LandTableFormat.SADX, labels, out tmpaddr); colmdladdrs[i] = tmpaddr + (uint)result.Count + imageBase; result.AddRange(tmpbyte); } } uint[] animmdladdrs = new uint[Anim.Count]; uint[] animaniaddrs = new uint[Anim.Count]; for (int i = 0; i < Anim.Count; i++) { if (labels.ContainsKey(Anim[i].Model.Name)) { animmdladdrs[i] = labels[Anim[i].Model.Name]; } else { result.Align(4); tmpbyte = Anim[i].Model.GetBytes(imageBase + (uint)result.Count, format == LandTableFormat.SADX, labels, out tmpaddr); animmdladdrs[i] = tmpaddr + (uint)result.Count + imageBase; result.AddRange(tmpbyte); } uint mtnaddr; if (labels.ContainsKey(Anim[i].Animation.Name)) { mtnaddr = labels[Anim[i].Animation.Name]; } else { result.Align(4); result.AddRange(Anim[i].Animation.GetBytes(imageBase + (uint)result.Count, labels, out mtnaddr)); } result.Align(4); animaniaddrs[i] = (uint)result.Count + imageBase; result.AddRange(ByteConverter.GetBytes(animmdladdrs[i])); result.AddRange(ByteConverter.GetBytes(mtnaddr)); } uint coladdr; if (COL.Count > 0) { if (labels.ContainsKey(COLName)) { coladdr = labels[COLName]; } else { coladdr = imageBase + (uint)result.Count; labels.Add(COLName, coladdr); for (int i = 0; i < COL.Count; i++) { result.Align(4); result.AddRange(COL[i].GetBytes(imageBase + (uint)result.Count, colmdladdrs[i], format)); } } } else { coladdr = 0; } uint animaddr; if (Anim.Count > 0) { if (labels.ContainsKey(AnimName)) { animaddr = labels[AnimName]; } else { animaddr = imageBase + (uint)result.Count; labels.Add(AnimName, animaddr); for (int i = 0; i < Anim.Count; i++) { result.Align(4); result.AddRange(Anim[i].GetBytes(imageBase + (uint)result.Count, animmdladdrs[i], animaniaddrs[i])); } } } else { animaddr = 0; } result.Align(4); uint texnameaddr = 0; if (TextureFileName != null) { texnameaddr = imageBase + (uint)result.Count; result.AddRange(Encoding.ASCII.GetBytes(TextureFileName)); result.Add(0); } result.Align(4); address = (uint)result.Count; result.AddRange(ByteConverter.GetBytes((ushort)COL.Count)); switch (format) { case LandTableFormat.SA1: case LandTableFormat.SADX: result.AddRange(ByteConverter.GetBytes((ushort)Anim.Count)); result.AddRange(ByteConverter.GetBytes(Flags)); result.AddRange(ByteConverter.GetBytes(Unknown1)); result.AddRange(ByteConverter.GetBytes(coladdr)); result.AddRange(ByteConverter.GetBytes(animaddr)); result.AddRange(ByteConverter.GetBytes(texnameaddr)); result.AddRange(ByteConverter.GetBytes(TextureList)); result.AddRange(ByteConverter.GetBytes(Unknown2)); result.AddRange(ByteConverter.GetBytes(Unknown3)); break; case LandTableFormat.SA2: case LandTableFormat.SA2B: result.AddRange(ByteConverter.GetBytes((ushort)cnk.Count)); result.AddRange(new byte[8]); // TODO: figure out what these do result.AddRange(ByteConverter.GetBytes(Unknown1)); result.AddRange(ByteConverter.GetBytes(coladdr)); result.AddRange(ByteConverter.GetBytes(animaddr)); result.AddRange(ByteConverter.GetBytes(texnameaddr)); result.AddRange(ByteConverter.GetBytes(TextureList)); break; } labels.Add(Name, address + imageBase); return(result.ToArray()); }
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; 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: 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: 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, 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, Metadata = meta }; ByteConverter.BigEndian = be; return(table); } if (magic == SA2BLVL) { LandTable table = new LandTable(file, ByteConverter.ToInt32(file, 8), 0, LandTableFormat.SA2B, labels) { Author = author, Description = description, Metadata = meta }; ByteConverter.BigEndian = be; return(table); } ByteConverter.BigEndian = be; throw new FormatException("Not a valid SA1LVL/SA2LVL file."); }
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 PolyChunkMaterial(byte[] file, int address) { Header = ByteConverter.ToUInt16(file, address); address += sizeof(ushort); Size = ByteConverter.ToUInt16(file, address); address += sizeof(ushort); switch (Type) { case ChunkType.Material_Diffuse: case ChunkType.Material_DiffuseAmbient: case ChunkType.Material_DiffuseSpecular: case ChunkType.Material_DiffuseAmbientSpecular: case ChunkType.Material_Diffuse2: case ChunkType.Material_DiffuseAmbient2: case ChunkType.Material_DiffuseSpecular2: case ChunkType.Material_DiffuseAmbientSpecular2: Diffuse = VColor.FromBytes(file, address, ColorType.ARGB8888_16); address += VColor.Size(ColorType.ARGB8888_16); break; } switch (Type) { case ChunkType.Material_Ambient: case ChunkType.Material_DiffuseAmbient: case ChunkType.Material_AmbientSpecular: case ChunkType.Material_DiffuseAmbientSpecular: case ChunkType.Material_Ambient2: case ChunkType.Material_DiffuseAmbient2: case ChunkType.Material_AmbientSpecular2: case ChunkType.Material_DiffuseAmbientSpecular2: Ambient = VColor.FromBytes(file, address, ColorType.XRGB8888_16); address += VColor.Size(ColorType.XRGB8888_16); break; } switch (Type) { case ChunkType.Material_Specular: case ChunkType.Material_DiffuseSpecular: case ChunkType.Material_AmbientSpecular: case ChunkType.Material_DiffuseAmbientSpecular: case ChunkType.Material_Specular2: case ChunkType.Material_DiffuseSpecular2: case ChunkType.Material_AmbientSpecular2: case ChunkType.Material_DiffuseAmbientSpecular2: Specular = VColor.FromBytes(file, address, ColorType.XRGB8888_16); SpecularExponent = (byte)(ByteConverter.ToUInt16(file, address + 2) >> 8); address += VColor.Size(ColorType.XRGB8888_16); break; } switch (Type) { case ChunkType.Material_Diffuse2: case ChunkType.Material_Ambient2: case ChunkType.Material_DiffuseAmbient2: case ChunkType.Material_Specular2: case ChunkType.Material_DiffuseSpecular2: case ChunkType.Material_AmbientSpecular2: case ChunkType.Material_DiffuseAmbientSpecular2: Second = true; break; } }
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 byte[] GetBytes() { VertexChunk next = null; int vertlimit; int vertcount = Vertices.Count; switch (Type) { case ChunkType.Vertex_VertexSH: vertlimit = 65535 / 4; if (Vertices.Count > vertlimit) { next = new VertexChunk(Type) { Vertices = Vertices.Skip(vertlimit).ToList() }; vertcount = vertlimit; } break; case ChunkType.Vertex_VertexNormalSH: vertlimit = 65535 / 8; if (Vertices.Count > vertlimit) { next = new VertexChunk(Type) { Vertices = Vertices.Skip(vertlimit).ToList(), Normals = Normals.Skip(vertlimit).ToList() }; vertcount = vertlimit; } break; case ChunkType.Vertex_Vertex: vertlimit = 65535 / 3; if (Vertices.Count > vertlimit) { next = new VertexChunk(Type) { Vertices = Vertices.Skip(vertlimit).ToList() }; vertcount = vertlimit; } break; case ChunkType.Vertex_VertexDiffuse8: vertlimit = 65535 / 4; if (Vertices.Count > vertlimit) { next = new VertexChunk(Type) { Vertices = Vertices.Skip(vertlimit).ToList(), Diffuse = Diffuse.Skip(vertlimit).ToList() }; vertcount = vertlimit; } break; case ChunkType.Vertex_VertexUserFlags: vertlimit = 65535 / 4; if (Vertices.Count > vertlimit) { next = new VertexChunk(Type) { Vertices = Vertices.Skip(vertlimit).ToList(), UserFlags = UserFlags.Skip(vertlimit).ToList() }; vertcount = vertlimit; } break; case ChunkType.Vertex_VertexNinjaFlags: vertlimit = 65535 / 4; if (Vertices.Count > vertlimit) { next = new VertexChunk(Type) { Vertices = Vertices.Skip(vertlimit).ToList(), NinjaFlags = NinjaFlags.Skip(vertlimit).ToList() }; vertcount = vertlimit; } break; case ChunkType.Vertex_VertexDiffuseSpecular5: case ChunkType.Vertex_VertexDiffuseSpecular4: vertlimit = 65535 / 4; if (Vertices.Count > vertlimit) { next = new VertexChunk(Type) { Vertices = Vertices.Skip(vertlimit).ToList(), Diffuse = Diffuse.Skip(vertlimit).ToList(), Specular = Specular.Skip(vertlimit).ToList() }; vertcount = vertlimit; } break; case ChunkType.Vertex_VertexNormal: vertlimit = 65535 / 6; if (Vertices.Count > vertlimit) { next = new VertexChunk(Type) { Vertices = Vertices.Skip(vertlimit).ToList(), Normals = Normals.Skip(vertlimit).ToList() }; vertcount = vertlimit; } break; case ChunkType.Vertex_VertexNormalDiffuse8: vertlimit = 65535 / 7; if (Vertices.Count > vertlimit) { next = new VertexChunk(Type) { Vertices = Vertices.Skip(vertlimit).ToList(), Normals = Normals.Skip(vertlimit).ToList(), Diffuse = Diffuse.Skip(vertlimit).ToList() }; vertcount = vertlimit; } break; case ChunkType.Vertex_VertexNormalUserFlags: vertlimit = 65535 / 7; if (Vertices.Count > vertlimit) { next = new VertexChunk(Type) { Vertices = Vertices.Skip(vertlimit).ToList(), Normals = Normals.Skip(vertlimit).ToList(), UserFlags = UserFlags.Skip(vertlimit).ToList() }; vertcount = vertlimit; } break; case ChunkType.Vertex_VertexNormalNinjaFlags: vertlimit = 65535 / 7; if (Vertices.Count > vertlimit) { next = new VertexChunk(Type) { Vertices = Vertices.Skip(vertlimit).ToList(), Normals = Normals.Skip(vertlimit).ToList(), NinjaFlags = NinjaFlags.Skip(vertlimit).ToList() }; vertcount = vertlimit; } break; case ChunkType.Vertex_VertexNormalDiffuseSpecular5: case ChunkType.Vertex_VertexNormalDiffuseSpecular4: vertlimit = 65535 / 4; if (Vertices.Count > vertlimit) { next = new VertexChunk(Type) { Vertices = Vertices.Skip(vertlimit).ToList(), Normals = Normals.Skip(vertlimit).ToList(), Diffuse = Diffuse.Skip(vertlimit).ToList(), Specular = Specular.Skip(vertlimit).ToList() }; vertcount = vertlimit; } break; case ChunkType.End: break; default: throw new NotSupportedException("Unsupported chunk type " + Type + "."); } SetVertCount(vertcount); switch (Type) { case ChunkType.Vertex_Vertex: Size = (ushort)(vertcount * 3 + 1); break; case ChunkType.Vertex_VertexSH: case ChunkType.Vertex_VertexDiffuse8: case ChunkType.Vertex_VertexUserFlags: case ChunkType.Vertex_VertexNinjaFlags: case ChunkType.Vertex_VertexDiffuseSpecular5: case ChunkType.Vertex_VertexDiffuseSpecular4: Size = (ushort)(vertcount * 4 + 1); break; case ChunkType.Vertex_VertexNormal: Size = (ushort)(vertcount * 6 + 1); break; case ChunkType.Vertex_VertexNormalDiffuse8: case ChunkType.Vertex_VertexNormalUserFlags: case ChunkType.Vertex_VertexNormalNinjaFlags: case ChunkType.Vertex_VertexNormalDiffuseSpecular5: case ChunkType.Vertex_VertexNormalDiffuseSpecular4: Size = (ushort)(vertcount * 7 + 1); break; case ChunkType.Vertex_VertexNormalSH: Size = (ushort)(vertcount * 8 + 1); break; } List <byte> result = new List <byte>((Size * 4) + 4); result.AddRange(ByteConverter.GetBytes(Header1)); result.AddRange(ByteConverter.GetBytes(Header2)); for (int i = 0; i < vertcount; i++) { switch (Type) { case ChunkType.Vertex_VertexSH: result.AddRange(Vertices[i].GetBytes()); result.AddRange(ByteConverter.GetBytes(1.0f)); break; case ChunkType.Vertex_VertexNormalSH: result.AddRange(Vertices[i].GetBytes()); result.AddRange(ByteConverter.GetBytes(1.0f)); result.AddRange(Normals[i].GetBytes()); result.AddRange(ByteConverter.GetBytes(1.0f)); break; case ChunkType.Vertex_Vertex: result.AddRange(Vertices[i].GetBytes()); break; case ChunkType.Vertex_VertexDiffuse8: result.AddRange(Vertices[i].GetBytes()); result.AddRange(VColor.GetBytes(Diffuse[i], ColorType.ARGB8888_32)); break; case ChunkType.Vertex_VertexUserFlags: result.AddRange(Vertices[i].GetBytes()); result.AddRange(ByteConverter.GetBytes(UserFlags[i])); break; case ChunkType.Vertex_VertexNinjaFlags: result.AddRange(Vertices[i].GetBytes()); result.AddRange(ByteConverter.GetBytes(NinjaFlags[i])); break; case ChunkType.Vertex_VertexDiffuseSpecular5: result.AddRange(Vertices[i].GetBytes()); result.AddRange(ByteConverter.GetBytes( ByteConverter.ToUInt16(VColor.GetBytes(Diffuse[i], ColorType.RGB565), 0) | (ByteConverter.ToUInt16(VColor.GetBytes(Specular[i], ColorType.RGB565), 0) << 16))); break; case ChunkType.Vertex_VertexDiffuseSpecular4: result.AddRange(Vertices[i].GetBytes()); result.AddRange(ByteConverter.GetBytes( ByteConverter.ToUInt16(VColor.GetBytes(Diffuse[i], ColorType.ARGB4444), 0) | (ByteConverter.ToUInt16(VColor.GetBytes(Specular[i], ColorType.RGB565), 0) << 16))); break; case ChunkType.Vertex_VertexNormal: result.AddRange(Vertices[i].GetBytes()); result.AddRange(Normals[i].GetBytes()); break; case ChunkType.Vertex_VertexNormalDiffuse8: result.AddRange(Vertices[i].GetBytes()); result.AddRange(Normals[i].GetBytes()); result.AddRange(VColor.GetBytes(Diffuse[i], ColorType.ARGB8888_32)); break; case ChunkType.Vertex_VertexNormalUserFlags: result.AddRange(Vertices[i].GetBytes()); result.AddRange(Normals[i].GetBytes()); result.AddRange(ByteConverter.GetBytes(UserFlags[i])); break; case ChunkType.Vertex_VertexNormalNinjaFlags: result.AddRange(Vertices[i].GetBytes()); result.AddRange(Normals[i].GetBytes()); result.AddRange(ByteConverter.GetBytes(NinjaFlags[i])); break; case ChunkType.Vertex_VertexNormalDiffuseSpecular5: result.AddRange(Vertices[i].GetBytes()); result.AddRange(Normals[i].GetBytes()); result.AddRange(ByteConverter.GetBytes( ByteConverter.ToUInt16(VColor.GetBytes(Diffuse[i], ColorType.RGB565), 0) | (ByteConverter.ToUInt16(VColor.GetBytes(Specular[i], ColorType.RGB565), 0) << 16))); break; case ChunkType.Vertex_VertexNormalDiffuseSpecular4: result.AddRange(Vertices[i].GetBytes()); result.AddRange(Normals[i].GetBytes()); result.AddRange(ByteConverter.GetBytes( ByteConverter.ToUInt16(VColor.GetBytes(Diffuse[i], ColorType.ARGB4444), 0) | (ByteConverter.ToUInt16(VColor.GetBytes(Specular[i], ColorType.RGB565), 0) << 16))); break; } } if (next != null) { result.AddRange(next.GetBytes()); } return(result.ToArray()); }
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 < GetVertCount(); 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 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 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); 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(); } } ByteConverter.BigEndian = be; }
public static void CreateFile(string filename, NJS_OBJECT model, string[] animationFiles, string author, string description, 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; case ModelFormat.GC: magic = SA2BMDLVer; 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 (!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 (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 void SaveToFile(string filename) { bool be = ByteConverter.BigEndian; ByteConverter.BigEndian = false; List <byte> file = new List <byte>(); ulong magic; switch (Format) { case ModelFormat.Basic: magic = SA1MDLVer; break; case ModelFormat.Chunk: magic = SA2MDLVer; break; case ModelFormat.GC: magic = SA2BMDLVer; 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); string path = Path.GetDirectoryName(filename); 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 (Animations.Count > 0) { List <byte> chunk = new List <byte>((Animations.Count + 1) * 4); int straddr = (Animations.Count + 1) * 4; List <byte> strbytes = new List <byte>(); for (int i = 0; i < Animations.Count; i++) { Animations[i].Save(Path.Combine(path, animationFiles[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 (!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); } 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 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 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 override byte[] GetBytes(uint imageBase, bool DX, Dictionary <string, uint> labels, out uint address) { List <byte> result = new List <byte>(); uint materialAddress = 0; if (Material != null && Material.Count > 0) { if (labels.ContainsKey(MaterialName)) { materialAddress = labels[MaterialName]; } else { materialAddress = imageBase; labels.Add(MaterialName, materialAddress); foreach (NJS_MATERIAL item in Material) { result.AddRange(item.GetBytes()); } } } uint meshAddress; if (labels.ContainsKey(MeshName)) { meshAddress = labels[MeshName]; } else { uint[] polyAddrs = new uint[Mesh.Count]; uint[] polyNormalAddrs = new uint[Mesh.Count]; uint[] vColorAddrs = new uint[Mesh.Count]; uint[] uVAddrs = new uint[Mesh.Count]; for (int i = 0; i < Mesh.Count; i++) { if (labels.ContainsKey(Mesh[i].PolyName)) { polyAddrs[i] = labels[Mesh[i].PolyName]; } else { result.Align(4); polyAddrs[i] = (uint)result.Count + imageBase; labels.Add(Mesh[i].PolyName, polyAddrs[i]); for (int j = 0; j < Mesh[i].Poly.Count; j++) { result.AddRange(Mesh[i].Poly[j].GetBytes()); } } } for (int i = 0; i < Mesh.Count; i++) { if (Mesh[i].PolyNormal != null && Mesh[i].PolyNormal.Length > 0) { if (labels.ContainsKey(Mesh[i].PolyNormalName)) { polyNormalAddrs[i] = labels[Mesh[i].PolyNormalName]; } else { result.Align(4); polyNormalAddrs[i] = (uint)result.Count + imageBase; labels.Add(Mesh[i].PolyNormalName, polyNormalAddrs[i]); for (int j = 0; j < Mesh[i].PolyNormal.Length; j++) { result.AddRange(Mesh[i].PolyNormal[j].GetBytes()); } } } } for (int i = 0; i < Mesh.Count; i++) { if (Mesh[i].VColor != null && Mesh[i].VColor.Length > 0) { if (labels.ContainsKey(Mesh[i].VColorName)) { vColorAddrs[i] = labels[Mesh[i].VColorName]; } else { result.Align(4); vColorAddrs[i] = (uint)result.Count + imageBase; labels.Add(Mesh[i].VColorName, vColorAddrs[i]); for (int j = 0; j < Mesh[i].VColor.Length; j++) { result.AddRange(VColor.GetBytes(Mesh[i].VColor[j])); } } } } for (int i = 0; i < Mesh.Count; i++) { if (Mesh[i].UV != null && Mesh[i].UV.Length > 0) { if (labels.ContainsKey(Mesh[i].UVName)) { uVAddrs[i] = labels[Mesh[i].UVName]; } else { result.Align(4); uVAddrs[i] = (uint)result.Count + imageBase; labels.Add(Mesh[i].UVName, uVAddrs[i]); for (int j = 0; j < Mesh[i].UV.Length; j++) { result.AddRange(Mesh[i].UV[j].GetBytes()); } } } } result.Align(4); meshAddress = (uint)result.Count + imageBase; labels.Add(MeshName, meshAddress); for (int i = 0; i < Mesh.Count; i++) { result.AddRange(Mesh[i].GetBytes(polyAddrs[i], polyNormalAddrs[i], vColorAddrs[i], uVAddrs[i], DX)); } } result.Align(4); uint vertexAddress; if (labels.ContainsKey(VertexName)) { vertexAddress = labels[VertexName]; } else { vertexAddress = (uint)result.Count + imageBase; labels.Add(VertexName, vertexAddress); foreach (Vertex item in Vertex) { if (item == null) { result.AddRange(new byte[SAModel.Vertex.Size]); } else { result.AddRange(item.GetBytes()); } } } result.Align(4); uint normalAddress; if (labels.ContainsKey(NormalName)) { normalAddress = labels[NormalName]; } else { normalAddress = (uint)result.Count + imageBase; labels.Add(NormalName, normalAddress); foreach (Vertex item in Normal) { if (item == null) { result.AddRange(new byte[SAModel.Vertex.Size]); } else { result.AddRange(item.GetBytes()); } } } result.Align(4); address = (uint)result.Count; result.AddRange(ByteConverter.GetBytes(vertexAddress)); result.AddRange(ByteConverter.GetBytes(normalAddress)); result.AddRange(ByteConverter.GetBytes(Vertex.Length)); result.AddRange(ByteConverter.GetBytes(meshAddress)); result.AddRange(ByteConverter.GetBytes(materialAddress)); result.AddRange(ByteConverter.GetBytes((short)Mesh.Count)); result.AddRange(ByteConverter.GetBytes((short)Material.Count)); result.AddRange(Bounds.GetBytes()); if (DX) { result.AddRange(new byte[4]); } labels.Add(Name, address + imageBase); return(result.ToArray()); }
public Strip(byte[] file, int address, ChunkType type, byte userFlags) { Indexes = new ushort[Math.Abs(ByteConverter.ToInt16(file, address))]; Reversed = (ByteConverter.ToUInt16(file, address) & 0x8000) == 0x8000; address += 2; switch (type) { case ChunkType.Strip_StripUVN: case ChunkType.Strip_StripUVH: case ChunkType.Strip_StripUVN2: case ChunkType.Strip_StripUVH2: UVs = new UV[Indexes.Length]; break; case ChunkType.Strip_StripColor: VColors = new Color[Indexes.Length]; break; case ChunkType.Strip_StripUVNColor: case ChunkType.Strip_StripUVHColor: UVs = new UV[Indexes.Length]; VColors = new Color[Indexes.Length]; break; } if (userFlags > 0) { UserFlags1 = new ushort[Indexes.Length - 2]; } if (userFlags > 1) { UserFlags2 = new ushort[Indexes.Length - 2]; } if (userFlags > 2) { UserFlags3 = new ushort[Indexes.Length - 2]; } for (int i = 0; i < Indexes.Length; i++) { Indexes[i] = ByteConverter.ToUInt16(file, address); address += 2; switch (type) { case ChunkType.Strip_StripUVN: case ChunkType.Strip_StripUVNColor: case ChunkType.Strip_StripUVN2: UVs[i] = new UV(file, address, false); address += UV.Size; break; case ChunkType.Strip_StripUVH: case ChunkType.Strip_StripUVHColor: case ChunkType.Strip_StripUVH2: UVs[i] = new UV(file, address, true); address += UV.Size; break; } switch (type) { case ChunkType.Strip_StripColor: case ChunkType.Strip_StripUVNColor: case ChunkType.Strip_StripUVHColor: VColors[i] = VColor.FromBytes(file, address, ColorType.ARGB8888_16); address += VColor.Size(ColorType.ARGB8888_16); break; } if (i > 1) { if (userFlags > 0) { UserFlags1[i - 2] = ByteConverter.ToUInt16(file, address); address += 2; if (userFlags > 1) { UserFlags2[i - 2] = ByteConverter.ToUInt16(file, address); address += 2; if (userFlags > 2) { UserFlags3[i - 2] = ByteConverter.ToUInt16(file, address); address += 2; } } } } } }
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 PolyChunkTinyTextureID(byte[] file, int address) { Header = ByteConverter.ToUInt16(file, address); Second = Type == ChunkType.Tiny_TextureID2; Data = ByteConverter.ToUInt16(file, address + 2); }
public Vertex(byte[] file, int address) { X = ByteConverter.ToSingle(file, address); Y = ByteConverter.ToSingle(file, address + 4); Z = ByteConverter.ToSingle(file, address + 8); }
public byte[] GetBytes(uint imageBase, Dictionary <string, uint> labels, out uint address) { List <byte> result = new List <byte>(); uint[] posoffs = new uint[ModelParts]; int[] posframes = new int[ModelParts]; uint[] rotoffs = new uint[ModelParts]; int[] rotframes = new int[ModelParts]; uint[] scloffs = new uint[ModelParts]; int[] sclframes = new int[ModelParts]; bool hasPos = false, hasRot = false, hasScl = false; foreach (KeyValuePair <int, AnimModelData> model in Models) { if (model.Value.Position.Count > 0) { hasPos = true; result.Align(4); posoffs[model.Key] = imageBase + (uint)result.Count; posframes[model.Key] = model.Value.Position.Count; foreach (KeyValuePair <int, Vertex> item in model.Value.Position) { result.AddRange(ByteConverter.GetBytes(item.Key)); result.AddRange(item.Value.GetBytes()); } } if (model.Value.Rotation.Count > 0) { hasRot = true; result.Align(4); rotoffs[model.Key] = imageBase + (uint)result.Count; rotframes[model.Key] = model.Value.Rotation.Count; foreach (KeyValuePair <int, Rotation> item in model.Value.Rotation) { result.AddRange(ByteConverter.GetBytes(item.Key)); result.AddRange(item.Value.GetBytes()); } } if (model.Value.Scale.Count > 0) { hasScl = true; result.Align(4); scloffs[model.Key] = imageBase + (uint)result.Count; sclframes[model.Key] = model.Value.Scale.Count; foreach (KeyValuePair <int, Vertex> item in model.Value.Scale) { result.AddRange(ByteConverter.GetBytes(item.Key)); result.AddRange(item.Value.GetBytes()); } } } result.Align(4); uint modeldata = imageBase + (uint)result.Count; if (hasPos & !hasRot & !hasScl) { hasRot = true; } if (hasRot & !hasPos & !hasScl) { hasPos = true; } if (hasScl & !hasPos & !hasRot) { hasRot = true; } for (int i = 0; i < ModelParts; i++) { if (hasPos) { result.AddRange(ByteConverter.GetBytes(posoffs[i])); } if (hasRot) { result.AddRange(ByteConverter.GetBytes(rotoffs[i])); } if (hasScl) { result.AddRange(ByteConverter.GetBytes(scloffs[i])); } if (hasPos) { result.AddRange(ByteConverter.GetBytes(posframes[i])); } if (hasRot) { result.AddRange(ByteConverter.GetBytes(rotframes[i])); } if (hasScl) { result.AddRange(ByteConverter.GetBytes(sclframes[i])); } } result.Align(4); address = (uint)result.Count; result.AddRange(ByteConverter.GetBytes(modeldata)); result.AddRange(ByteConverter.GetBytes(Frames)); AnimFlags flags = 0; ushort numpairs = 0; if (hasPos) { flags |= AnimFlags.Translate; numpairs++; } if (hasRot) { flags |= AnimFlags.Rotate; numpairs++; } if (hasScl) { flags |= AnimFlags.Scale; numpairs++; } result.AddRange(ByteConverter.GetBytes((ushort)flags)); result.AddRange(ByteConverter.GetBytes(numpairs)); return(result.ToArray()); }
public byte[] GetBytes(uint imageBase, bool DX, Dictionary <string, uint> labels, out uint address) { for (int i = 1; i < Children.Count; i++) { Children[i - 1].Sibling = Children[i]; } List <byte> result = new List <byte>(); uint childaddr = 0; uint siblingaddr = 0; uint attachaddr = 0; byte[] tmpbyte; if (Children.Count > 0) { if (labels.ContainsKey(Children[0].Name)) { childaddr = labels[Children[0].Name]; } else { result.Align(4); result.AddRange(Children[0].GetBytes(imageBase, DX, labels, out childaddr)); childaddr += imageBase; } } if (Sibling != null) { if (labels.ContainsKey(Sibling.Name)) { siblingaddr = labels[Sibling.Name]; } else { result.Align(4); tmpbyte = Sibling.GetBytes(imageBase + (uint)result.Count, DX, labels, out siblingaddr); siblingaddr += imageBase + (uint)result.Count; result.AddRange(tmpbyte); } } if (Attach != null) { if (labels.ContainsKey(Attach.Name)) { attachaddr = labels[Attach.Name]; } else { result.Align(4); tmpbyte = Attach.GetBytes(imageBase + (uint)result.Count, DX, labels, out attachaddr); attachaddr += imageBase + (uint)result.Count; result.AddRange(tmpbyte); } } result.Align(4); address = (uint)result.Count; ObjectFlags flags = GetFlags(); result.AddRange(ByteConverter.GetBytes((int)flags)); result.AddRange(ByteConverter.GetBytes(attachaddr)); result.AddRange(Position.GetBytes()); result.AddRange(Rotation.GetBytes()); result.AddRange(Scale.GetBytes()); result.AddRange(ByteConverter.GetBytes(childaddr)); result.AddRange(ByteConverter.GetBytes(siblingaddr)); labels.Add(Name, address + imageBase); return(result.ToArray()); }
static void Main(string[] args) { if (args.Length == 0) { Console.Write("Filename: "); args = new string[] { Console.ReadLine().Trim('"') }; } foreach (string filename in args) { byte[] fc; if (Path.GetExtension(filename).Equals(".prs", StringComparison.OrdinalIgnoreCase)) { fc = Prs.Decompress(filename); } else { fc = File.ReadAllBytes(filename); } string path = Path.Combine(Path.GetDirectoryName(Path.GetFullPath(filename)), Path.GetFileNameWithoutExtension(filename)); EventIniData ini = IniSerializer.Deserialize <EventIniData>(Path.Combine(path, Path.ChangeExtension(Path.GetFileName(filename), ".ini"))); uint key; if (fc[0] == 0x81) { ByteConverter.BigEndian = true; key = 0x8125FE60; } else { ByteConverter.BigEndian = false; key = 0xC600000; } List <byte> modelbytes = new List <byte>(fc); Dictionary <string, uint> labels = new Dictionary <string, uint>(); foreach (string file in ini.Files.Where(a => HelperFunctions.FileHash(Path.Combine(path, a.Key)) != a.Value).Select(a => a.Key)) { modelbytes.AddRange(new ModelFile(Path.Combine(path, file)).Model.GetBytes((uint)(key + modelbytes.Count), false, labels, out uint address)); } fc = modelbytes.ToArray(); int ptr = fc.GetPointer(0x20, key); if (ptr != 0) { for (int i = 0; i < (ini.Game == Game.SA2B ? 18 : 16); i++) { UpgradeInfo info = ini.Upgrades[i]; if (info.RootNode != null) { if (labels.ContainsKey(info.RootNode)) { ByteConverter.GetBytes(labels[info.RootNode]).CopyTo(fc, ptr); } if (labels.ContainsKey(info.AttachNode1)) { ByteConverter.GetBytes(labels[info.AttachNode1]).CopyTo(fc, ptr + 4); } if (labels.ContainsKey(info.Model1)) { ByteConverter.GetBytes(labels[info.Model1]).CopyTo(fc, ptr + 8); } if (info.AttachNode2 != null && labels.ContainsKey(info.AttachNode2)) { ByteConverter.GetBytes(labels[info.AttachNode2]).CopyTo(fc, ptr + 12); } if (info.Model2 != null && labels.ContainsKey(info.Model2)) { ByteConverter.GetBytes(labels[info.Model2]).CopyTo(fc, ptr + 16); } } ptr += 0x14; } } int gcnt = ByteConverter.ToInt32(fc, 8); ptr = fc.GetPointer(0, key); if (ptr != 0) { for (int gn = 0; gn <= gcnt; gn++) { GroupInfo info = ini.Groups[gn]; int ptr2 = fc.GetPointer(ptr, key); int ecnt = Math.Min(ByteConverter.ToInt32(fc, ptr + 4), info.Entities?.Count ?? 0); if (ptr2 != 0) { for (int en = 0; en < ecnt; en++) { if (labels.ContainsKey(info.Entities[en])) { ByteConverter.GetBytes(labels[info.Entities[en]]).CopyTo(fc, ptr2); } ptr2 += ini.Game == Game.SA2B ? 0x2C : 0x20; } } ptr2 = fc.GetPointer(ptr + 0x18, key); if (ptr2 != 0 && info.Big != null && labels.ContainsKey(info.Big)) { ByteConverter.GetBytes(labels[info.Big]).CopyTo(fc, ptr2); } ptr += 0x20; } } ptr = fc.GetPointer(0x18, key); if (ptr != 0) { for (int i = 0; i < 18; i++) { if (ini.MechParts.ContainsKey(i) && labels.ContainsKey(ini.MechParts[i])) { ByteConverter.GetBytes(labels[ini.MechParts[i]]).CopyTo(fc, ptr); } ptr += 4; } } ptr = fc.GetPointer(0x1C, key); if (ptr != 0 && ini.TailsTails != null && labels.ContainsKey(ini.TailsTails)) { ByteConverter.GetBytes(labels[ini.TailsTails]).CopyTo(fc, ptr); } if (Path.GetExtension(filename).Equals(".prs", StringComparison.OrdinalIgnoreCase)) { Prs.Compress(fc, filename); } else { File.WriteAllBytes(filename, fc); } } }
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; if (fc[0] == 0x81) { Console.WriteLine("File is in GC/PC format."); ByteConverter.BigEndian = true; key = 0x8125FE60; ini.Game = Game.SA2B; } else { Console.WriteLine("File is in DC format."); ByteConverter.BigEndian = false; key = 0xC600000; ini.Game = Game.SA2; } List <string> nodenames = new List <string>(); Dictionary <string, KeyValuePair <string, NJS_OBJECT> > modelfiles = new Dictionary <string, KeyValuePair <string, NJS_OBJECT> >(); int ptr = fc.GetPointer(0x20, key); if (ptr != 0) { for (int i = 0; i < (ini.Game == Game.SA2B ? 18 : 16); i++) { UpgradeInfo info = new UpgradeInfo(); info.RootNode = GetModel(fc, ptr, key, Path.Combine(path, $"Upgrade {i + 1} Root.sa2mdl"), nodenames, modelfiles); 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, Path.Combine(path, $"Upgrade {i + 1} Model 1.sa2mdl"), nodenames, modelfiles); ptr2 = fc.GetPointer(ptr + 0xC, key); if (ptr2 != 0) { info.AttachNode2 = $"object_{ptr2:X8}"; } info.Model2 = GetModel(fc, ptr + 0x10, key, Path.Combine(path, $"Upgrade {i + 1} Model 2.sa2mdl"), nodenames, modelfiles); } ini.Upgrades.Add(info); ptr += 0x14; } } else { Console.WriteLine("Event contains no character upgrades."); } int gcnt = ByteConverter.ToInt32(fc, 8); ptr = fc.GetPointer(0, key); if (ptr != 0) { Console.WriteLine("Event contains {0} group(s).", gcnt + 1); for (int gn = 0; gn <= gcnt; gn++) { GroupInfo info = new GroupInfo(); int ptr2 = fc.GetPointer(ptr, key); int ecnt = ByteConverter.ToInt32(fc, ptr + 4); if (ptr2 != 0) { Console.WriteLine("Group {0} contains {1} entit{2}.", gn + 1, ecnt, ecnt == 1 ? "y" : "ies"); for (int en = 0; en < ecnt; en++) { string name = GetModel(fc, ptr2, key, Path.Combine(path, $"Group {gn + 1} Entity {en + 1} Model.sa2mdl"), nodenames, modelfiles); if (name != null) { info.Entities.Add(name); } ptr2 += ini.Game == Game.SA2B ? 0x2C : 0x20; } } else { Console.WriteLine("Group {0} contains no entities.", gn + 1); } ptr2 = fc.GetPointer(ptr + 0x18, key); if (ptr2 != 0) { info.Big = GetModel(fc, ptr2, key, Path.Combine(path, $"Group {gn + 1} Big Model.sa2mdl"), nodenames, modelfiles); } ini.Groups.Add(info); ptr += 0x20; } } else { Console.WriteLine("Event contains no groups."); } ptr = fc.GetPointer(0x18, key); if (ptr != 0) { for (int i = 0; i < 93; i++) { string name = GetModel(fc, ptr, key, Path.Combine(path, $"Mech Part {i + 1}.sa2mdl"), nodenames, modelfiles); if (name != null) { ini.MechParts.Add(i, name); } ptr += 4; } } else { Console.WriteLine("Event contains no mech parts."); } ptr = fc.GetPointer(0x1C, key); if (ptr != 0) { ini.TailsTails = GetModel(fc, ptr, key, Path.Combine(path, $"Tails tails.sa2mdl"), nodenames, modelfiles); } else { Console.WriteLine("Event does not contain Tails' tails."); } foreach (var item in modelfiles) { ModelFile.CreateFile(item.Value.Key, item.Value.Value, null, null, null, null, null, ModelFormat.Chunk); ini.Files.Add(Path.GetFileName(item.Value.Key), HelperFunctions.FileHash(item.Value.Key)); } IniSerializer.Serialize(ini, Path.Combine(path, Path.ChangeExtension(Path.GetFileName(filename), ".ini"))); } }
public static PolyChunk Load(byte[] file, int address) { ChunkType type = (ChunkType)(ByteConverter.ToUInt16(file, address) & 0xFF); switch (type) { case ChunkType.Null: return(new PolyChunkNull(file, address)); case ChunkType.Bits_BlendAlpha: return(new PolyChunkBitsBlendAlpha(file, address)); case ChunkType.Bits_MipmapDAdjust: return(new PolyChunkBitsMipmapDAdjust(file, address)); case ChunkType.Bits_SpecularExponent: return(new PolyChunkBitsSpecularExponent(file, address)); case ChunkType.Bits_CachePolygonList: return(new PolyChunkBitsCachePolygonList(file, address)); case ChunkType.Bits_DrawPolygonList: return(new PolyChunkBitsDrawPolygonList(file, address)); case ChunkType.Tiny_TextureID: case ChunkType.Tiny_TextureID2: return(new PolyChunkTinyTextureID(file, address)); case ChunkType.Material_Diffuse: case ChunkType.Material_Ambient: case ChunkType.Material_DiffuseAmbient: case ChunkType.Material_Specular: case ChunkType.Material_DiffuseSpecular: case ChunkType.Material_AmbientSpecular: case ChunkType.Material_DiffuseAmbientSpecular: case ChunkType.Material_Diffuse2: case ChunkType.Material_Ambient2: case ChunkType.Material_DiffuseAmbient2: case ChunkType.Material_Specular2: case ChunkType.Material_DiffuseSpecular2: case ChunkType.Material_AmbientSpecular2: case ChunkType.Material_DiffuseAmbientSpecular2: return(new PolyChunkMaterial(file, address)); case ChunkType.Material_Bump: return(new PolyChunkMaterialBump(file, address)); case ChunkType.Volume_Polygon3: case ChunkType.Volume_Polygon4: case ChunkType.Volume_Strip: return(new PolyChunkVolume(file, address)); case ChunkType.Strip_Strip: case ChunkType.Strip_StripUVN: case ChunkType.Strip_StripUVH: case ChunkType.Strip_StripColor: case ChunkType.Strip_StripUVNColor: case ChunkType.Strip_StripUVHColor: case ChunkType.Strip_Strip2: case ChunkType.Strip_StripUVN2: case ChunkType.Strip_StripUVH2: return(new PolyChunkStrip(file, address)); case ChunkType.End: return(new PolyChunkEnd(file, address)); default: throw new NotSupportedException("Unsupported chunk type " + type + " at " + address.ToString("X8") + "."); } }