public MsaAnimation(Subfile basis) { dataSectionOffset = Utility.ReadUInt32BigEndian(basis.filebytes, 0x6C); boneTableRelativeOffset = Utility.ReadUInt32BigEndian(basis.filebytes, 0x40); firstTableOffset = Utility.ReadUInt32BigEndian(basis.filebytes, 0x84); firstTableCount = Utility.ReadUInt32BigEndian(basis.filebytes, 0x88); secondTableOffset = Utility.ReadUInt32BigEndian(basis.filebytes, 0x8C); secondTableCount = Utility.ReadUInt32BigEndian(basis.filebytes, 0x90); for (int i = 0; i < firstTableCount; i++) { table1.Add(new Table1Entry(basis.filebytes, (int)(firstTableOffset + (i * 0x0C)), this)); } for (int i = 0; i < secondTableCount; i++) { table2.Add(new Table2Entry(basis.filebytes, (int)(secondTableOffset + (i * 0x0C)))); } uint boneTableCount = Utility.ReadUInt32BigEndian(basis.filebytes, (int)(dataSectionOffset + boneTableRelativeOffset + 0x80)); for (int i = 0; i < boneTableCount; i++) { boneTable.Add(new BoneTableEntry(basis.filebytes, (int)(dataSectionOffset + boneTableRelativeOffset + 0xF0 + (i * 0x20)))); } }
public hkBoxShape(Subfile basis, int objectOffset) { x = Utility.ReverseEndianSingle(BitConverter.ToSingle(basis.filebytes, objectOffset + 0x10)); y = Utility.ReverseEndianSingle(BitConverter.ToSingle(basis.filebytes, objectOffset + 0x14)); z = Utility.ReverseEndianSingle(BitConverter.ToSingle(basis.filebytes, objectOffset + 0x18)); unk = Utility.ReverseEndianSingle(BitConverter.ToSingle(basis.filebytes, objectOffset + 0x1C)); }
public int classOffset; //offset to the type of object in the class table public hkxVirtualFixupEntry(Subfile basis, int pos) { objectOffset = Utility.ReverseEndianSigned(BitConverter.ToInt32(basis.filebytes, pos)); pos += 4; pos += 4; //skip unk, always 0? classOffset = Utility.ReverseEndianSigned(BitConverter.ToInt32(basis.filebytes, pos)); }
public MaterialSet(Subfile basis) { int number_of_materials = Utility.ReadInt32BigEndian(basis.filebytes, 0x0C); List <ulong> hashes = new List <ulong>(); for (int i = 0; i < number_of_materials; i++) { hashes.Add(Utility.ReadUInt64BigEndian(basis.filebytes, 0x18 + (i * 8))); } foreach (Subfile s in global.activePackage.subfiles) { if (!(s.typeID == (uint)global.TypeID.MATD_MSK || s.typeID == (uint)global.TypeID.MATD_MSA)) { continue; } if (hashes.Contains(s.hash)) { if (s.filebytes == null || s.filebytes.Length == 0) { s.Load(); } mats.Add(s.matd); } } }
public static List <hkSimpleMeshShapeTriangle> ParseHkTriangleArray(Subfile basis, int offset) { int length = Utility.ReverseEndianSigned(BitConverter.ToInt32(basis.filebytes, offset + 4)); int capacityAndFlags = Utility.ReverseEndianSigned(BitConverter.ToInt32(basis.filebytes, offset + 8)); if (length == 0) { return(null); } Console.WriteLine("Going to look for offset " + offset + " in the local fixup table"); offset = basis.hkx.getLocalFixUpOffset(offset - basis.hkx.dataSection.sectionOffset) + basis.hkx.dataSection.sectionOffset; //go to the offset of the actual array, using the local fixup table //now begins the unique bit List <hkSimpleMeshShapeTriangle> output = new List <hkSimpleMeshShapeTriangle>(); for (int i = 0; i < length; i++) { hkSimpleMeshShapeTriangle t = new hkSimpleMeshShapeTriangle(); t.v1 = Utility.ReverseEndianSigned(BitConverter.ToInt32(basis.filebytes, offset)); t.v2 = Utility.ReverseEndianSigned(BitConverter.ToInt32(basis.filebytes, offset + 4)); t.v3 = Utility.ReverseEndianSigned(BitConverter.ToInt32(basis.filebytes, offset + 8)); output.Add(t); //Console.WriteLine("triangle: " + t.v1 + "," + t.v2 + "," + t.v3); offset += 12; } return(output); }
public static List <int> ParseHkIntArray(Subfile basis, int offset) { int length = Utility.ReverseEndianSigned(BitConverter.ToInt32(basis.filebytes, offset + 4)); int capacityAndFlags = Utility.ReverseEndianSigned(BitConverter.ToInt32(basis.filebytes, offset + 8)); if (length == 0) { return(null); } Console.WriteLine("Going to look for offset " + offset + " in the local fixup table"); offset = basis.hkx.getLocalFixUpOffset(offset - basis.hkx.dataSection.sectionOffset) + basis.hkx.dataSection.sectionOffset; //go to the offset of the actual array, using the local fixup table //now begins the unique bit List <int> output = new List <int>(); for (int i = 0; i < length; i++) { output.Add(Utility.ReverseEndianSigned(BitConverter.ToInt32(basis.filebytes, offset))); Console.WriteLine("int: " + output[output.Count - 1]); offset += 4; } return(output); }
public static List <Vertex> ParseHkVertexArray(Subfile basis, int offset) { int length = Utility.ReverseEndianSigned(BitConverter.ToInt32(basis.filebytes, offset + 4)); int capacityAndFlags = Utility.ReverseEndianSigned(BitConverter.ToInt32(basis.filebytes, offset + 8)); if (length == 0) { return(null); } Console.WriteLine("Going to look for offset " + offset + " in the local fixup table"); offset = basis.hkx.getLocalFixUpOffset(offset - basis.hkx.dataSection.sectionOffset) + basis.hkx.dataSection.sectionOffset; //go to the offset of the actual array, using the local fixup table //now begins the unique bit List <Vertex> output = new List <Vertex>(); for (int i = 0; i < length; i++) { Vertex v = new Vertex(); v.position.x = Utility.ReverseEndianSingle(BitConverter.ToSingle(basis.filebytes, offset)); v.position.y = Utility.ReverseEndianSingle(BitConverter.ToSingle(basis.filebytes, offset + 4)); v.position.z = Utility.ReverseEndianSingle(BitConverter.ToSingle(basis.filebytes, offset + 8)); v.W = Utility.ReverseEndianSingle(BitConverter.ToSingle(basis.filebytes, offset + 12)); output.Add(v); //Console.WriteLine("vertex: "+v.X+","+v.Y+","+v.Z); offset += 16; } return(output); }
private void backtrackToModelToolStripMenuItem_Click(object sender, EventArgs e) { Subfile s = treeNodesAndSubfiles[FileTree.SelectedNode]; s.Load(); backtrackSubfile(s); }
public MaterialData(Subfile basis) { parameters = new List <Param>(); filename = basis.filename; int pos = 0x10; // a lot of the offsets are measured from 0x10 //now read the parameters! pos += 0x08; int lengthOfDataSection = Utility.ReadInt32BigEndian(basis.filebytes, pos); pos += 4; int numberOfParams = Utility.ReadInt32BigEndian(basis.filebytes, pos); pos += 4; //now the param list begins. Each param has 0x10 bytes describing it, with an offset to later in the file (falling within the data section, but measured from 0x10) for (int i = 0; i < numberOfParams; i++) { parameters.Add(new Param(basis.filebytes, pos)); pos += 0x10; } //now process the data for each parameter foreach (Param parameter in parameters) { pos = 0x10 + parameter.dataOffset; switch (parameter.paramType) { case MaterialParameter.diffuseMap: ulong hash_of_diffuse_texture = (ulong)Utility.ReadUInt32BigEndian(basis.filebytes, pos); pos += 4; hash_of_diffuse_texture |= ((ulong)Utility.ReadUInt32BigEndian(basis.filebytes, pos)) << 32; pos += 4; if (hash_of_diffuse_texture == 0x58CC31A1AC11E901) { Console.WriteLine("Here it is!!"); } uint typeID_of_diffuse_texture = Utility.ReadUInt32BigEndian(basis.filebytes, pos); pos += 4; parameter.diffuse_texture = global.activePackage.FindFileByHashAndTypeID(hash_of_diffuse_texture, typeID_of_diffuse_texture); break; default: Console.WriteLine("Unhandled material parameter: " + parameter.paramType); break; } } }
public LLMF(Subfile basis) { int pos = 0x28; int numLevelModels = Utility.ReadInt32BigEndian(basis.filebytes, pos); pos += 4; int numObjects = Utility.ReadInt32BigEndian(basis.filebytes, pos); pos += 4; pos = 0x48; for (int i = 0; i < numLevelModels; i++) { levelModels.Add(new LevelEntry() { modelHash = Utility.ReverseEndianULong(BitConverter.ToUInt64(basis.filebytes, pos)) }); pos += 8; } for (int i = 0; i < numObjects; i++) { ObjectEntry newObject = new ObjectEntry(); newObject.modelHash = Utility.ReverseEndianULong(BitConverter.ToUInt64(basis.filebytes, pos)); pos += 8; newObject.X = Utility.ReadSingleBigEndian(basis.filebytes, pos); pos += 4; newObject.Y = Utility.ReadSingleBigEndian(basis.filebytes, pos); pos += 4; newObject.Z = Utility.ReadSingleBigEndian(basis.filebytes, pos); pos += 4; newObject.xRot = Utility.ReadSingleBigEndian(basis.filebytes, pos); pos += 4; newObject.yRot = Utility.ReadSingleBigEndian(basis.filebytes, pos); pos += 4; newObject.zRot = Utility.ReadSingleBigEndian(basis.filebytes, pos); pos += 4; newObject.xScale = Utility.ReadSingleBigEndian(basis.filebytes, pos); pos += 4; newObject.yScale = Utility.ReadSingleBigEndian(basis.filebytes, pos); pos += 4; newObject.zScale = Utility.ReadSingleBigEndian(basis.filebytes, pos); pos += 4; newObject.materialCount = Utility.ReadInt32BigEndian(basis.filebytes, pos); pos += 4; for (int j = 0; j < newObject.materialCount; j++) { pos += 16; } objects.Add(newObject); } }
public hkxDataSection(Subfile basis, int offset) { int pos = offset; while (basis.filebytes[pos] != 0x00) { name += (char)basis.filebytes[pos]; pos++; } pos = offset + 0x14; //skips over 4-byte magic 0x000000FF sectionOffset = Utility.ReverseEndianSigned(BitConverter.ToInt32(basis.filebytes, pos)); pos += 4; localFixupTableOffset = Utility.ReverseEndianSigned(BitConverter.ToInt32(basis.filebytes, pos)); pos += 4; globalFixupTableOffset = Utility.ReverseEndianSigned(BitConverter.ToInt32(basis.filebytes, pos)); pos += 4; virtualFixupTableOffset = Utility.ReverseEndianSigned(BitConverter.ToInt32(basis.filebytes, pos)); pos += 4; val4 = Utility.ReverseEndianSigned(BitConverter.ToInt32(basis.filebytes, pos)); pos += 4; val5 = Utility.ReverseEndianSigned(BitConverter.ToInt32(basis.filebytes, pos)); pos += 4; val6 = Utility.ReverseEndianSigned(BitConverter.ToInt32(basis.filebytes, pos)); pos += 4; //now actually read the sections pos = sectionOffset + localFixupTableOffset; while (pos + 0x08 <= sectionOffset + globalFixupTableOffset) { localFixupTable.Add(new hkxLocalFixupEntry(basis, pos)); pos += 0x08; } pos = sectionOffset + virtualFixupTableOffset; while (pos + 0x0C <= sectionOffset + val4) { virtualFixupTable.Add(new hkxVirtualFixupEntry(basis, pos)); pos += 0x0C; } }
public void ParseHavokObject(Subfile basis, hkxDataSection.hkxVirtualFixupEntry entry) { for (int i = 0; i < classTable.entries.Count; i++) { if (classTable.entries[i].offset == entry.classOffset) { hkxClassTable.hkxClassTableEntry target = classTable.entries[i]; switch (target.name) { case "hkBoxShape": hkBoxShape newBoxShape = new hkBoxShape(basis, dataSection.sectionOffset + entry.objectOffset); havokObjects.Add(newBoxShape); break; case "hkSimpleMeshShape": hkSimpleMeshShape newSimpleMeshShape = new hkSimpleMeshShape(basis, dataSection.sectionOffset + entry.objectOffset); havokObjects.Add(newSimpleMeshShape); obj.Add("o object" + entry.objectOffset); Console.WriteLine("vert count: " + newSimpleMeshShape.vertices.Count); foreach (Vertex v in newSimpleMeshShape.vertices) { obj.Add("v " + v.position.x + " " + v.position.y + " " + v.position.z); } foreach (hkSimpleMeshShapeTriangle f in newSimpleMeshShape.triangles) { obj.Add("f " + (f.v1 + 1 + cumulativeVertCount) + " " + (f.v2 + 1 + cumulativeVertCount) + " " + (f.v3 + 1 + cumulativeVertCount)); } cumulativeVertCount += newSimpleMeshShape.vertices.Count; break; default: Console.WriteLine("Unhandled havok object type: " + target.name); break; } break; } } }
private void replaceToolStripMenuItem_Click(object sender, EventArgs e) { OpenFileDialog openFileDialog1 = new OpenFileDialog(); Subfile s = treeNodesAndSubfiles[FileTree.SelectedNode]; s.Load(); openFileDialog1.Title = "Replace " + s.filename; openFileDialog1.Filter = "All files (*.*)|*.*"; openFileDialog1.CheckFileExists = true; openFileDialog1.CheckPathExists = true; if (openFileDialog1.ShowDialog() == DialogResult.OK) { s.filebytes = File.ReadAllBytes(openFileDialog1.FileName); } }
public hkxClassTableEntry(hkxClassTable parent, Subfile basis, int pos) { ID = BitConverter.ToUInt32(basis.filebytes, pos); pos += 4; unk = basis.filebytes[pos]; pos++; offset = pos - parent.sectionOffset; while (basis.filebytes[pos] != 0x00) { name += (char)basis.filebytes[pos]; pos++; } pos++; endOffset = pos; }
public hkxClassTable(Subfile basis, int offset) { int pos = offset; while (basis.filebytes[pos] != 0x00) { name += (char)basis.filebytes[pos]; pos++; } pos = offset + 0x14; //skips over 4-byte magic 0x000000FF sectionOffset = Utility.ReverseEndianSigned(BitConverter.ToInt32(basis.filebytes, pos)); pos += 4; endOfClassTableOffset = Utility.ReverseEndianSigned(BitConverter.ToInt32(basis.filebytes, pos)); pos += 4; val2 = Utility.ReverseEndianSigned(BitConverter.ToInt32(basis.filebytes, pos)); pos += 4; val3 = Utility.ReverseEndianSigned(BitConverter.ToInt32(basis.filebytes, pos)); pos += 4; val4 = Utility.ReverseEndianSigned(BitConverter.ToInt32(basis.filebytes, pos)); pos += 4; val5 = Utility.ReverseEndianSigned(BitConverter.ToInt32(basis.filebytes, pos)); pos += 4; val6 = Utility.ReverseEndianSigned(BitConverter.ToInt32(basis.filebytes, pos)); pos += 4; //now actually read the entries pos = sectionOffset; while (pos < endOfClassTableOffset) { hkxClassTableEntry newEntry = new hkxClassTableEntry(this, basis, pos); if (newEntry.ID != 0xFFFFFFFF) { entries.Add(newEntry); } pos = newEntry.endOffset; Console.WriteLine("Added hkx class entry: " + newEntry.name); } Console.WriteLine(entries.Count); }
public hkSimpleMeshShape(Subfile basis, int objectOffset) { Console.WriteLine("Exporting a hkSimpleMeshShape"); if (basis.filebytes[objectOffset + 16] == 0) { disableWelding = false; } else { disableWelding = true; } vertices = havokUtility.ParseHkVertexArray(basis, objectOffset + 20); triangles = havokUtility.ParseHkTriangleArray(basis, objectOffset + 32); materialIndices = havokUtility.ParseHkIntArray(basis, objectOffset + 44); radius = Utility.ReverseEndianSingle(BitConverter.ToSingle(basis.filebytes, objectOffset + 56)); }
public hkxFile(Subfile basis) { basis.hkx = this; obj = new List <string>(); cumulativeVertCount = 0; byte[] withoutHeader = new byte[basis.filebytes.Length - 0x20]; Array.Copy(basis.filebytes, 0x20, withoutHeader, 0x00, basis.filebytes.Length - 0x20); basis.filebytes = withoutHeader; classTable = new hkxClassTable(basis, 0x40); dataSection = new hkxDataSection(basis, 0x70); foreach (hkxDataSection.hkxVirtualFixupEntry virtualFixupEntry in dataSection.virtualFixupTable) { ParseHavokObject(basis, virtualFixupEntry); } System.IO.File.WriteAllLines(basis.filename + ".obj", obj.ToArray()); }
private void convertModelToolStripMenuItem_Click(object sender, EventArgs e) { OpenFileDialog openFileDialog2 = new OpenFileDialog(); openFileDialog2.Title = "Select mdl file"; openFileDialog2.DefaultExt = "mdl"; openFileDialog2.Filter = "MySims 3d models (*.mdl, *.rmdl)|*.mdl;*.rmdl"; openFileDialog2.CheckFileExists = true; openFileDialog2.CheckPathExists = true; openFileDialog2.Multiselect = true; if (openFileDialog2.ShowDialog() == DialogResult.OK) { foreach (string filename in openFileDialog2.FileNames) { if (Path.GetExtension(filename) == ".mdl") { ConvertSkyHeroesModel(filename, File.ReadAllBytes(filename)); } else { Subfile s = new Subfile(); s.filebytes = File.ReadAllBytes(filename); s.rmdl = new RevoModel(s); SaveFileDialog saveFileDialog1 = new SaveFileDialog(); saveFileDialog1.Title = "Save model"; saveFileDialog1.FileName = ""; saveFileDialog1.CheckPathExists = true; saveFileDialog1.Filter = "Wavefront OBJ (*.obj)|*.obj"; if (saveFileDialog1.ShowDialog() == DialogResult.OK) { s.rmdl.GenerateObj(saveFileDialog1.FileName); } } } } }
private void exportAllContextMenuStripButton_Click(object sender, EventArgs e) { SaveFileDialog saveFileDialog1 = new SaveFileDialog(); saveFileDialog1.Title = "Select destination folder"; saveFileDialog1.FileName = "Save here"; saveFileDialog1.CheckPathExists = false; saveFileDialog1.CheckFileExists = false; saveFileDialog1.Filter = "Directory |directory"; if (saveFileDialog1.ShowDialog() == DialogResult.OK) { foreach (TreeNode node in FileTree.Nodes[0].Nodes) { bool was_loaded_already = false; Subfile target = treeNodesAndSubfiles[node]; if (target.filebytes == null || target.filebytes.Length == 0) { target.Load(); } else { was_loaded_already = true; } target.ExportFile(true, Path.Combine(Path.GetDirectoryName(saveFileDialog1.FileName), target.filename)); if (!was_loaded_already) { target.Unload(); } } } MessageBox.Show("Export complete."); }
public void LoadPackage() { int currenttypeindexbeingprocessed = 0; int instancesprocessedofcurrenttype = 0; using (BinaryReader reader = new BinaryReader(File.Open(filename, FileMode.Open, FileAccess.ReadWrite, FileShare.ReadWrite))) { uint magic = 0; magic = Utility.ReverseEndian(reader.ReadUInt32()); switch (magic) { case 0x44425046: //DBPF packageType = PackageType.Kingdom; break; case 0x46504244: //FPBD packageType = PackageType.Agents; break; case 0x030502ED: //Skyheroes case 0x03051771: //Skyheroes packageType = PackageType.SkyHeroes; reader.Close(); LoadSkyHeroesPackage(); return; default: MessageBox.Show("This is not a valid package!", "Package not supported", MessageBoxButtons.OK, MessageBoxIcon.Information); Console.WriteLine("invalid file magic"); break; } if (packageType == PackageType.Agents) { majorversion = Utility.ReverseEndian(reader.ReadUInt32()); minorversion = Utility.ReverseEndian(reader.ReadUInt32()); unknown1 = Utility.ReverseEndian(reader.ReadUInt32()); unknown2 = Utility.ReverseEndian(reader.ReadUInt32()); unknown3 = Utility.ReverseEndian(reader.ReadUInt32()); date = DateTime.FromBinary(Utility.ReverseEndianLong(reader.ReadInt64())); indexmajorversion = Utility.ReverseEndian(reader.ReadUInt32()); filecount = Utility.ReverseEndian(reader.ReadUInt32()); indexoffsetdeprecated = Utility.ReverseEndian(reader.ReadUInt32()); indexsize = Utility.ReverseEndian(reader.ReadUInt32()); holeentrycount = Utility.ReverseEndian(reader.ReadUInt32()); holeoffset = Utility.ReverseEndian(reader.ReadUInt32()); holesize = Utility.ReverseEndian(reader.ReadUInt32()); indexminorversion = Utility.ReverseEndian(reader.ReadUInt32()); unknown4 = Utility.ReverseEndian(reader.ReadUInt32()); indexoffset = Utility.ReverseEndian(reader.ReadUInt32()); unknown5 = Utility.ReverseEndian(reader.ReadUInt32()); unknown6 = Utility.ReverseEndian(reader.ReadUInt32()); reserved1 = Utility.ReverseEndian(reader.ReadUInt32()); reserved2 = Utility.ReverseEndian(reader.ReadUInt32()); } else { majorversion = reader.ReadUInt32(); minorversion = reader.ReadUInt32(); unknown1 = reader.ReadUInt32(); unknown2 = reader.ReadUInt32(); unknown3 = reader.ReadUInt32(); date = DateTime.FromBinary(Utility.ReverseEndianLong(reader.ReadInt64())); indexmajorversion = reader.ReadUInt32(); filecount = reader.ReadUInt32(); indexoffsetdeprecated = reader.ReadUInt32(); indexsize = reader.ReadUInt32(); holeentrycount = reader.ReadUInt32(); holeoffset = reader.ReadUInt32(); holesize = reader.ReadUInt32(); indexminorversion = reader.ReadUInt32(); indexoffset = reader.ReadUInt32(); unknown5 = reader.ReadUInt32(); unknown6 = reader.ReadUInt32(); reserved1 = reader.ReadUInt32(); reserved2 = reader.ReadUInt32(); } Console.WriteLine("Package date: " + date.ToString()); reader.BaseStream.Position = indexoffset; //index header if (packageType == PackageType.Agents) { indexnumberofentries = Utility.ReverseEndianULong(reader.ReadUInt64()); for (uint i = 0; i < indexnumberofentries; i++) //a bunch of entries that describe how many files there are of each type { IndexEntry newEntry = new IndexEntry(); newEntry.typeID = Utility.ReverseEndian(reader.ReadUInt32()); newEntry.groupID = Utility.ReverseEndian(reader.ReadUInt32()); newEntry.typeNumberOfInstances = Utility.ReverseEndian(reader.ReadUInt32()); newEntry.indexnulls = Utility.ReverseEndian(reader.ReadUInt32()); IndexEntries.Add(newEntry); } currenttypeindexbeingprocessed = 0; instancesprocessedofcurrenttype = 0; for (uint i = 0; i < filecount; i++) //go through the files, they are organised by type, one type after the other. (So X number of type A, as described above, then Y number of type B...) { Subfile newSubfile = new Subfile(); newSubfile.hash = Utility.ReverseEndianULong(reader.ReadUInt64()); newSubfile.fileoffset = Utility.ReverseEndian(reader.ReadUInt32()); newSubfile.filesize = Utility.ReverseEndian(reader.ReadUInt32()); newSubfile.typeID = IndexEntries[currenttypeindexbeingprocessed].typeID; newSubfile.groupID = IndexEntries[currenttypeindexbeingprocessed].groupID; if (IndexEntries[currenttypeindexbeingprocessed].groupID != 0) //it's compressed { newSubfile.should_be_compressed_when_in_package = true; newSubfile.uncompressedsize = Utility.ReverseEndian(reader.ReadUInt32()); Console.WriteLine("Found a compressed file"); } instancesprocessedofcurrenttype++; if (instancesprocessedofcurrenttype == IndexEntries[currenttypeindexbeingprocessed].typeNumberOfInstances) { Console.WriteLine("Processed " + instancesprocessedofcurrenttype + " instances of " + currenttypeindexbeingprocessed); currenttypeindexbeingprocessed++; instancesprocessedofcurrenttype = 0; } subfiles.Add(newSubfile); } } else //MySims and MySims Kingdom use these { MSKindexversion = reader.ReadUInt32(); if (MSKindexversion == 0) { Console.WriteLine("index version 0"); for (uint i = 0; i < filecount; i++) { Subfile newSubfile = new Subfile(); newSubfile.typeID = reader.ReadUInt32(); newSubfile.groupID = reader.ReadUInt32(); newSubfile.hash = (ulong)(reader.ReadUInt32()) << 32; newSubfile.hash |= (ulong)(reader.ReadUInt32()); newSubfile.fileoffset = reader.ReadUInt32(); newSubfile.filesize = reader.ReadUInt32() & 0x7FFFFFFF; newSubfile.uncompressedsize = reader.ReadUInt32(); reader.BaseStream.Position += 0x04; //flags if (newSubfile.filesize == newSubfile.uncompressedsize) { newSubfile.uncompressedsize = 0; } else { newSubfile.should_be_compressed_when_in_package = true; } subfiles.Add(newSubfile); } } else if (MSKindexversion == 1) { MessageBox.Show("Index version 1 not implemented!"); } else if (MSKindexversion == 2) { Console.WriteLine("index version 2"); reader.BaseStream.Position += 4; for (uint i = 0; i < filecount; i++) { Subfile newSubfile = new Subfile(); newSubfile.typeID = reader.ReadUInt32(); newSubfile.hash = (ulong)(reader.ReadUInt32()) << 32; newSubfile.hash |= (ulong)(reader.ReadUInt32()); newSubfile.fileoffset = reader.ReadUInt32(); newSubfile.filesize = reader.ReadUInt32() & 0x7FFFFFFF; newSubfile.uncompressedsize = reader.ReadUInt32(); reader.BaseStream.Position += 0x04; //flags if (newSubfile.filesize == newSubfile.uncompressedsize) { newSubfile.uncompressedsize = 0; } else { newSubfile.should_be_compressed_when_in_package = true; } subfiles.Add(newSubfile); } } else if (MSKindexversion == 3) { Console.WriteLine("index version 3"); uint allFilesTypeID = reader.ReadUInt32(); reader.BaseStream.Position += 4; for (uint i = 0; i < filecount; i++) { Subfile newSubfile = new Subfile(); newSubfile.typeID = allFilesTypeID; newSubfile.hash = (ulong)(reader.ReadUInt32()) << 32; newSubfile.hash |= (ulong)(reader.ReadUInt32()); newSubfile.fileoffset = reader.ReadUInt32(); newSubfile.filesize = reader.ReadUInt32() & 0x7FFFFFFF; newSubfile.uncompressedsize = reader.ReadUInt32(); reader.BaseStream.Position += 0x04; //flags if (newSubfile.filesize == newSubfile.uncompressedsize) { newSubfile.uncompressedsize = 0; } else { newSubfile.should_be_compressed_when_in_package = true; } subfiles.Add(newSubfile); } } else if (MSKindexversion == 4) { Console.WriteLine("index version 4"); reader.BaseStream.Position += 4; for (uint i = 0; i < filecount; i++) { Subfile newSubfile = new Subfile(); newSubfile.typeID = reader.ReadUInt32(); newSubfile.hash = (ulong)(reader.ReadUInt32()) << 32; newSubfile.hash |= (ulong)(reader.ReadUInt32()); newSubfile.fileoffset = reader.ReadUInt32(); newSubfile.filesize = reader.ReadUInt32(); newSubfile.uncompressedsize = reader.ReadUInt32(); if (newSubfile.filesize == newSubfile.uncompressedsize) { newSubfile.uncompressedsize = 0; } else { newSubfile.should_be_compressed_when_in_package = true; } subfiles.Add(newSubfile); } } else if (MSKindexversion == 7) { Console.WriteLine("index version 7"); for (uint i = 0; i < filecount; i++) { Subfile newSubfile = new Subfile(); newSubfile.typeID = reader.ReadUInt32(); newSubfile.groupID = reader.ReadUInt32(); newSubfile.hash = (ulong)(reader.ReadUInt32()) << 32; newSubfile.hash |= (ulong)(reader.ReadUInt32()); newSubfile.fileoffset = reader.ReadUInt32(); newSubfile.filesize = reader.ReadUInt32() & 0x7FFFFFFF; newSubfile.uncompressedsize = reader.ReadUInt32(); reader.BaseStream.Position += 0x04; //flags if (newSubfile.filesize == newSubfile.uncompressedsize) { newSubfile.uncompressedsize = 0; } else { newSubfile.should_be_compressed_when_in_package = true; } subfiles.Add(newSubfile); } } else { MessageBox.Show("Unknown index version: " + MSKindexversion); return; } } //extract files Console.WriteLine("filecount " + filecount); int filesprocessed = 0; List <string> luaFilenamesForDict = new List <string>(); bool containslua = false; for (int i = 0; i < filecount; i++) { if (subfiles[i].should_be_compressed_when_in_package) { subfiles[i].has_been_decompressed = false; //set default state of a compressed file to compressed } { string fileextension = null; switch ((global.TypeID)subfiles[i].typeID) { case global.TypeID.RMDL_MSA: //RMDL MSA fileextension = ".rmdl"; //TYPE ID 29 54 E7 34 break; case global.TypeID.RMDL_MSK: //RMDL MSK fileextension = ".rmdl"; // "ModelData" break; case global.TypeID.MATD_MSA: //MATD MSA fileextension = ".matd"; //TYPE ID E6 64 05 42 break; case global.TypeID.MATD_MSK: //MATD MSK "MaterialData" fileextension = ".matd"; break; case global.TypeID.TPL_MSA: //altered TPL MSA fileextension = ".tpl"; //TYPE ID 92 AA 4D 6A break; case global.TypeID.TPL_MSK: //altered TPL MSK "TextureData" fileextension = ".tpl"; break; case global.TypeID.MTST_MSK: //MTST Material Set MSK "MaterialSetData" fileextension = ".mtst"; break; case global.TypeID.MTST_MSA: //MTST Material Set MSA fileextension = ".mtst"; //TYPE ID 78 7E 84 2A break; case global.TypeID.FPST_MSK: //FPST MySims Kingdom footprint set. "FootprintData" contains a model footprint (ftpt) which is possibly documented at http://simswiki.info/wiki.php?title=Sims_3:PackedFileTypes case global.TypeID.FPST_MSA: //FPST MySims Agents footprint set. contains a model footprint (ftpt) which is possibly documented at http://simswiki.info/wiki.php?title=Sims_3:PackedFileTypes fileextension = ".fpst"; //It's like a heatmap of where sims can walk? Or perhaps which surfaces should generate which kind of footprint (but wouldn't that mean that overhangs wouldn't work?) break; case global.TypeID.BNK_MSA: //BNKb big endian BNK MSA vgmstream can decode these. https://github.com/losnoco/vgmstream/blob/master/src/meta/ea_schl.c fileextension = ".bnk"; //TYPE ID 21 99 BB 60 break; case global.TypeID.BNK_MSK: //BNKb BNK MSK (idk which endian, not tested) "AudioData" vgmstream can decode these. https://github.com/losnoco/vgmstream/blob/master/src/meta/ea_schl.c fileextension = ".bnk"; break; case global.TypeID.BIG_MSK: //big fileextension = ".big"; //"AptData" (but it is, of course, a familiar EA .BIG archive) break; case global.TypeID.BIG_MSA: //BIGF fileextension = ".big"; break; case global.TypeID.COLLISION_MSA: //00 00 00 02 There's another, separate filetype that also begins with the 2 magic, but that one doesn't appear as frequently, so this one here is probably the collision type ID //TYPE ID 1A 8F EB 14. fileextension = ".collision"; //mesh collision break; //there are mentions of 'rwphysics' in MSA's main.dol; could this mean Renderware Physics, which was indeed a product available at the time? case global.TypeID.FX: //FX fileextension = ".fx"; break; case global.TypeID.LUAC_MSA: //LUAC MSA fileextension = ".luac"; containslua = true; break; case global.TypeID.LUAC_MSK: //LUAC MSK "LuaObjectData" fileextension = ".luac"; containslua = true; break; case global.TypeID.SLOT_MSA: //SLOT MSA fileextension = ".slot"; break; case global.TypeID.SLOT_MSK: //SLOT MSK fileextension = ".slot"; //"SlotData" break; case global.TypeID.PARTICLES_MSA: //particles file fileextension = ".particles"; //TYPE ID 28 70 78 64 break; case global.TypeID.BUILDABLEREGION_MSA: //00 00 00 03 buildable region MSA fileextension = ".buildableregion"; break; case global.TypeID.BUILDABLEREGION_MSK: //buildable region MSK "BuildableRegionData" fileextension = ".buildableregion"; break; case global.TypeID.LLMF_MSA: //LLMF level bin MSA fileextension = ".llmf"; break; case global.TypeID.LLMF_MSK: //LLMF level bin MSK "LevelData" fileextension = ".llmf"; break; case global.TypeID.RIG_MSA: //RIG MSA //Interesting granny struct info at 0x49CFDD in MSA's main.dol fileextension = ".grannyrig"; //TYPE ID 46 72 E5 BD break; case global.TypeID.RIG_MSK: //RIG MSK fileextension = ".grannyrig"; // "RigData" break; case global.TypeID.ANIMCLIP_MSA: //ANIMATION MSA fileextension = ".clip"; //TYPE ID D6 BE DA 43 break; case global.TypeID.ANIMCLIP_MSK: //ANIMATION MSK "ClipData" fileextension = ".clip"; break; case global.TypeID.LTST_MSA: fileextension = ".ltst"; //possibly lighting set? break; case global.TypeID.TTF_MSK: //TrueType font MySims Kingdom case global.TypeID.TTF_MSA: //TrueType font MySims Agents fileextension = ".ttf"; //TYPE ID 27 6C A4 B9 break; case global.TypeID.HKX_MSK: //MSK HKX havok collision file fileextension = ".hkx"; // "PhysicsData" break; case global.TypeID.OGVD_MSK: fileextension = ".objectGridVolumeData"; //MSK "ObjectGridVolumeData" break; case global.TypeID.OGVD_MSA: //00 00 00 02 MSA ObjectGridVolumeData bounding box collision (for very simple objects) fileextension = ".objectGridVolumeData"; break; case global.TypeID.SPD_MSK: fileextension = ".snapPointData"; //MSK "SnapPointData" break; case global.TypeID.SPD_MSA: //00 00 00 03 MSA SnapPointData, most likely fileextension = ".snapPointData"; break; case global.TypeID.VGD_MSK: fileextension = ".voxelGridData"; //MSK "VoxelGridData" break; case global.TypeID.VGD_MSA: //00 00 00 01 MSA VoxelGridData, most likely, most likely fileextension = ".voxelGridData"; break; case global.TypeID.MODEL_MS: //model used by MySims, not the same as rmdl fileextension = ".model"; break; case global.TypeID.KEYNAMEMAP_MS: fileextension = ".KeyNameMap"; break; case global.TypeID.GEOMETRY_MS: fileextension = ".geometry"; break; case global.TypeID.OLDSPEEDTREE_MS: fileextension = ".oldSpeedTree"; break; case global.TypeID.SPEEDTREE_MS: fileextension = ".speedTree"; break; case global.TypeID.COMPOSITETEXTURE_MS: fileextension = ".compositeTexture"; break; case global.TypeID.SIMOUTFIT_MS: fileextension = ".simOutfit"; break; case global.TypeID.LEVELXML_MS: fileextension = ".levelXml"; break; case global.TypeID.LUA_MSK: //uncompiled lua script "LuaTextData" fileextension = ".lua"; break; case global.TypeID.LIGHTSETXML_MS: //Light set XML MySims fileextension = ".lightSetXml"; break; case global.TypeID.LIGHTSETBIN_MSK: //Light set bin MySims fileextension = ".lightSetBin"; //Named "LightSetData" in MSK's executable break; case global.TypeID.XML_MS: //xml fileextension = ".xml"; break; case global.TypeID.FPST_MS: //footprint set MySims fileextension = ".footprintSet"; break; case global.TypeID.OBJECTCONSTRUCTIONXML_MS: //object construction xml fileextension = ".objectConstructionXml"; break; case global.TypeID.OBJECTCONSTRUCTIONBIN_MS: //object construction bin fileextension = ".objectConstructionBin"; break; case global.TypeID.SLOTXML_MS: //slot xml fileextension = ".slotXml"; break; case global.TypeID.SWARM_MSK: //swm fileextension = ".swm"; //MSK "SwarmData" break; case global.TypeID.SWARM_MS: //SwarmBin fileextension = ".SwarmBin"; break; case global.TypeID.XMLBIN_MS: //XmlBin fileextension = ".XmlBin"; break; case global.TypeID.CABXML_MS: //CABXml fileextension = ".CABXml"; break; case global.TypeID.CABBIN_MS: //CABBin fileextension = ".CABBin"; break; case global.TypeID.LIGHTBOXXML_MS: //LightBoxXml fileextension = ".lightBoxXml"; break; case global.TypeID.LIGHTBOXBIN_MS: //LightBoxBin fileextension = ".lightBoxBin"; //MSK LightBoxData (Named in MSK's executable, though I think I found it in MySims?) break; case global.TypeID.XMB_MS: //xmb fileextension = ".xmb"; break; default: Console.WriteLine("Unknown type ID " + subfiles[i].typeID); Console.WriteLine("and this type ID appears " + GetNumOccurrencesOfTypeID(subfiles[i].typeID) + " times in total."); Console.WriteLine("index of file was " + filesprocessed); fileextension = "." + subfiles[i].typeID.ToString(); break; } subfiles[i].fileextension = fileextension; Vault.luaString typeIDRealString = global.activeVault.GetLuaStringWithHash(subfiles[i].typeID); //if (typeIDRealString != null) // { // fileextension = "." + typeIDRealString.name; // } byte[] newfilenameasbytes = BitConverter.GetBytes(Utility.ReverseEndianULong(subfiles[i].hash)); subfiles[i].filename = "0x"; ulong newfilenameasulong = Utility.ReverseEndianULong(subfiles[i].hash); subfiles[i].hashString = "0x" + BitConverter.ToString(newfilenameasbytes).Replace("-", ""); if (global.activeVault.VaultHashesAndFileNames.Keys.Contains(subfiles[i].hash)) { subfiles[i].filename = global.activeVault.VaultHashesAndFileNames[subfiles[i].hash]; } else { subfiles[i].filename = subfiles[i].hashString; } subfiles[i].filename += fileextension; /* * if (fileextension == ".lua") //temp * { * string luaName = ""; * * int currentOffset = 0x10; * * while (newfile[currentOffset] != 0x00) * { * currentOffset++; * } * * while (newfile[currentOffset] != 0x2F) * { * currentOffset--; * } * * currentOffset++; * * while (newfile[currentOffset] != 0x2E) * { * luaName += ((char)newfile[currentOffset]) + ""; * currentOffset++; * } * * luaFilenamesForDict.Add("VaultHashesAndFileNames.Add("+newfilename+",\""+luaName+"\");"); * }*/ filesprocessed++; } } form1.MakeFileTree(); if (containslua && global.activeVault.luaStrings.Count > 0) { string[] luaStringsForExport = new string[global.activeVault.luaStrings.Count]; for (int i = 0; i < global.activeVault.luaStrings.Count; i++) { luaStringsForExport[i] = BitConverter.ToString(BitConverter.GetBytes(Utility.ReverseEndian(global.activeVault.luaStrings[i].hash))).Replace("-", "");; luaStringsForExport[i] += " " + global.activeVault.luaStrings[i].name; } File.WriteAllLines("global.activeVault.luaStrings.lua", luaStringsForExport); } File.WriteAllLines("dict.txt", luaFilenamesForDict); MessageBox.Show("Processed " + filesprocessed + " files (out of a total " + filecount + ").", "Task complete", MessageBoxButtons.OK, MessageBoxIcon.Information); } }
public hkxLocalFixupEntry(Subfile basis, int pos) { destOffset = Utility.ReverseEndianSigned(BitConverter.ToInt32(basis.filebytes, pos)); pos += 4; srcOffset = Utility.ReverseEndianSigned(BitConverter.ToInt32(basis.filebytes, pos)); }
public hkArray(Subfile basis, int objectOffset) { System.Windows.Forms.MessageBox.Show("hkArray not implemented yet"); }
public RevoModel(Subfile basis) { int pos = 0; magic = Utility.ReadUInt32BigEndian(basis.filebytes, pos); pos += 4; flags = Utility.ReadUInt32BigEndian(basis.filebytes, pos); pos += 4; pos += 0x18; int meshcount = Utility.ReadInt32BigEndian(basis.filebytes, pos); pos += 4; int meshtableoffset = Utility.ReadInt32BigEndian(basis.filebytes, pos); pos += 4; for (int m = 0; m < meshcount; m++) { Mesh newMesh = new Mesh(); pos = meshtableoffset + (m * 4); int meshInfoTableOffset = Utility.ReadInt32BigEndian(basis.filebytes, pos); pos += 4; pos = meshInfoTableOffset; int displayListSize = Utility.ReadInt32BigEndian(basis.filebytes, pos); pos += 4; int displayListOffset = Utility.ReadInt32BigEndian(basis.filebytes, pos); pos += 4; int numVertAttributes = Utility.ReadInt32BigEndian(basis.filebytes, pos); pos += 4; int vertDataOffset = Utility.ReadInt32BigEndian(basis.filebytes, pos); pos += 4; newMesh.hash_of_material = Utility.ReadUInt64BigEndian(basis.filebytes, pos); pos += 8; uint typeID_of_material = Utility.ReadUInt32BigEndian(basis.filebytes, pos); pos += 4; uint unk2 = Utility.ReadUInt32BigEndian(basis.filebytes, pos); pos += 4; float boundsMinX = Utility.ReadSingleBigEndian(basis.filebytes, pos); pos += 4; float boundsMinY = Utility.ReadSingleBigEndian(basis.filebytes, pos); pos += 4; float boundsMinZ = Utility.ReadSingleBigEndian(basis.filebytes, pos); pos += 4; float boundsMaxX = Utility.ReadSingleBigEndian(basis.filebytes, pos); pos += 4; float boundsMaxY = Utility.ReadSingleBigEndian(basis.filebytes, pos); pos += 4; float boundsMaxZ = Utility.ReadSingleBigEndian(basis.filebytes, pos); pos += 4; uint unk3 = Utility.ReadUInt32BigEndian(basis.filebytes, pos); pos += 4; uint unk4 = Utility.ReadUInt32BigEndian(basis.filebytes, pos); pos += 4; uint unk5 = Utility.ReadUInt32BigEndian(basis.filebytes, pos); pos += 4; //FNV-1 hash of "none" in MySims Kingdom. Different in Agents. pos += 12; int boneToMatrixBindingInfoOffset = Utility.ReadInt32BigEndian(basis.filebytes, pos); pos += 4; int boneNamesOffset = Utility.ReadInt32BigEndian(basis.filebytes, pos); pos += 4; int boneInfoOffset = Utility.ReadInt32BigEndian(basis.filebytes, pos); pos += 4; //now read vertex data for (int v = 0; v < numVertAttributes; v++) { pos = vertDataOffset + (v * 8); VertexAttributeArrayType vertexArrayType = (VertexAttributeArrayType)basis.filebytes[pos]; pos++; //what kind of data is stored in the array: positions? lights? etc. newMesh.presentAttributes.Add(vertexArrayType); VertexAttributeComponentType vertexComponentType = (VertexAttributeComponentType)(basis.filebytes[pos] >> 3); //how many components are there to the data, e.g. is it XYZ, or just XY? etc. VertexAttributeComponentSize vertexComponentSize = (VertexAttributeComponentSize)(basis.filebytes[pos] & 0x07); pos++; //what format is the data in, e.g. 32 bit float, u8, RGB565, etc... pos += 2; //skip padding pos = Utility.ReadInt32BigEndian(basis.filebytes, pos); //now go to the start of the actual entries int stopPoint = boneToMatrixBindingInfoOffset; //absolute endpoint is the beginning of the next section if (stopPoint == 0) { if (m == meshcount - 1) { stopPoint = basis.filebytes.Length; } else { stopPoint = Utility.ReadInt32BigEndian(basis.filebytes, meshtableoffset + ((m + 1) * 4)); } } if (v < numVertAttributes - 1) //but if we're not at the last vertex array yet, then the endpoint is the start of the next array { stopPoint = Utility.ReadInt32BigEndian(basis.filebytes, vertDataOffset + ((v + 1) * 8) + 4); } int checkAheadAmount = 12; if (vertexArrayType == VertexAttributeArrayType.GX_LIGHTARRAY) //it will probably ask for RGB values etc { switch (vertexComponentSize) { case VertexAttributeComponentSize.GX_RGBA4: checkAheadAmount = 1; break; case VertexAttributeComponentSize.GX_RGB565: checkAheadAmount = 2; break; case VertexAttributeComponentSize.GX_RGB8: //more accurately described as RGB24 case VertexAttributeComponentSize.GX_RGBA6: //more accurately described as RGBA24 checkAheadAmount = 3; break; case VertexAttributeComponentSize.GX_RGBA8: case VertexAttributeComponentSize.GX_RGBX8: checkAheadAmount = 4; break; } } else { switch (vertexComponentSize) { case VertexAttributeComponentSize.GX_S8: case VertexAttributeComponentSize.GX_U8: checkAheadAmount = 1; break; case VertexAttributeComponentSize.GX_S16: case VertexAttributeComponentSize.GX_U16: checkAheadAmount = 2; break; case VertexAttributeComponentSize.GX_F32: checkAheadAmount = 4; break; } } switch (vertexComponentType) { case VertexAttributeComponentType.GX_POS_XY: checkAheadAmount *= 2; break; case VertexAttributeComponentType.GX_POS_XYZ: checkAheadAmount *= 3; break; default: System.Windows.Forms.MessageBox.Show("Unhandled component type, and therefore couldn't find an appopriate multiplier: " + vertexComponentType); break; } //now actually go and read those vertices List <Vertex> vertexAttributes = new List <Vertex>(); do { Vertex newVertex = new Vertex(); Console.WriteLine("Loading attribute " + vertexArrayType); if (vertexArrayType == VertexAttributeArrayType.GX_VA_POS) { switch (vertexComponentType) { case VertexAttributeComponentType.GX_POS_XY: if (vertexComponentSize == VertexAttributeComponentSize.GX_F32) { checkAheadAmount = 8; newVertex.position.x = Utility.ReadSingleBigEndian(basis.filebytes, pos); pos += 4; newVertex.position.y = Utility.ReadSingleBigEndian(basis.filebytes, pos); pos += 4; } else { System.Windows.Forms.MessageBox.Show("Argh! The vertex coordinates are not floats! How annoying."); } break; case VertexAttributeComponentType.GX_POS_XYZ: if (vertexComponentSize == VertexAttributeComponentSize.GX_F32) { checkAheadAmount = 12; newVertex.position.x = Utility.ReadSingleBigEndian(basis.filebytes, pos); pos += 4; newVertex.position.y = Utility.ReadSingleBigEndian(basis.filebytes, pos); pos += 4; newVertex.position.z = Utility.ReadSingleBigEndian(basis.filebytes, pos); pos += 4; } else { System.Windows.Forms.MessageBox.Show("Argh! The vertex coordinates are not floats! How annoying."); } break; default: Console.WriteLine("Unhandled vertex component type: " + vertexComponentType); break; } } else if (vertexArrayType == VertexAttributeArrayType.GX_VA_NRM || vertexArrayType == VertexAttributeArrayType.GX_VA_TEX0 || vertexArrayType == VertexAttributeArrayType.GX_VA_TEX1 || vertexArrayType == VertexAttributeArrayType.GX_VA_TEX2 || vertexArrayType == VertexAttributeArrayType.GX_VA_TEX3 || vertexArrayType == VertexAttributeArrayType.GX_VA_TEX4 || vertexArrayType == VertexAttributeArrayType.GX_VA_TEX5 || vertexArrayType == VertexAttributeArrayType.GX_VA_TEX6 || vertexArrayType == VertexAttributeArrayType.GX_VA_TEX7) { switch (vertexComponentType) { case VertexAttributeComponentType.GX_NRM_XYZ: if (vertexComponentSize == VertexAttributeComponentSize.GX_F32) { checkAheadAmount = 12; newVertex.normal.x = Utility.ReadSingleBigEndian(basis.filebytes, pos); pos += 4; newVertex.normal.y = Utility.ReadSingleBigEndian(basis.filebytes, pos); pos += 4; newVertex.normal.z = Utility.ReadSingleBigEndian(basis.filebytes, pos); pos += 4; } else { System.Windows.Forms.MessageBox.Show("Argh! The normal coordinates are not floats! How annoying."); } break; case VertexAttributeComponentType.GX_NRM_NBT3: if (vertexComponentSize == VertexAttributeComponentSize.GX_F32) { checkAheadAmount = 36; newVertex.normal.x = Utility.ReadSingleBigEndian(basis.filebytes, pos); pos += 4; newVertex.normal.y = Utility.ReadSingleBigEndian(basis.filebytes, pos); pos += 4; newVertex.normal.z = Utility.ReadSingleBigEndian(basis.filebytes, pos); pos += 4; newVertex.binormal.x = Utility.ReadSingleBigEndian(basis.filebytes, pos); pos += 4; newVertex.binormal.y = Utility.ReadSingleBigEndian(basis.filebytes, pos); pos += 4; newVertex.binormal.z = Utility.ReadSingleBigEndian(basis.filebytes, pos); pos += 4; newVertex.tangent.x = Utility.ReadSingleBigEndian(basis.filebytes, pos); pos += 4; newVertex.tangent.y = Utility.ReadSingleBigEndian(basis.filebytes, pos); pos += 4; newVertex.tangent.z = Utility.ReadSingleBigEndian(basis.filebytes, pos); pos += 4; } else { System.Windows.Forms.MessageBox.Show("Argh! The normal coordinates are not floats! How annoying."); } break; case VertexAttributeComponentType.GX_NRM_NBT: if (vertexComponentSize == VertexAttributeComponentSize.GX_F32) { checkAheadAmount = 12; newVertex.normal.x = Utility.ReadSingleBigEndian(basis.filebytes, pos); pos += 4; newVertex.normal.y = Utility.ReadSingleBigEndian(basis.filebytes, pos); pos += 4; newVertex.normal.z = Utility.ReadSingleBigEndian(basis.filebytes, pos); pos += 4; } else if (vertexComponentSize == VertexAttributeComponentSize.GX_U8) { checkAheadAmount = 8; //APPARENTLY IT IGNORES THE U8 AND READS FLOATS INSTEAD. float U = Utility.ReadSingleBigEndian(basis.filebytes, pos); pos += 4; float V = Utility.ReadSingleBigEndian(basis.filebytes, pos); pos += 4; newVertex.UVchannels[(int)vertexArrayType - 13] = new MorcuMath.Vector2(U, V); } else { System.Windows.Forms.MessageBox.Show("Argh! The normal coordinates are not floats! How annoying."); } break; default: Console.WriteLine("Unhandled tex or normal component type: " + vertexComponentType); break; } } else if (vertexArrayType == VertexAttributeArrayType.GX_VA_CLR0 || vertexArrayType == VertexAttributeArrayType.GX_VA_CLR1) { switch (vertexComponentType) { case VertexAttributeComponentType.GX_CLR_RGB: if (vertexComponentSize == VertexAttributeComponentSize.GX_RGBA6) //RGBA24 basically { checkAheadAmount = 3; newVertex.color = imageTools.ReadRGBA24(basis.filebytes, pos); pos += 3; } else { System.Windows.Forms.MessageBox.Show("Argh! The rgb component size was unhandled! It was: " + vertexComponentSize); } break; default: Console.WriteLine("Unhandled colour component type: " + vertexComponentType); break; } } else { Console.WriteLine("Don't know how to read a vertex array of type: " + vertexArrayType + "! Component type is: " + vertexComponentType); break; } vertexAttributes.Add(newVertex); } while (pos + checkAheadAmount <= stopPoint); switch (vertexArrayType) { case VertexAttributeArrayType.GX_VA_POS: newMesh.vertices = vertexAttributes; break; case VertexAttributeArrayType.GX_VA_NRM: foreach (Vertex vn in vertexAttributes) { newMesh.normals.Add(vn); } break; case VertexAttributeArrayType.GX_VA_TEX0: newMesh.texCoords = vertexAttributes; break; } } //now read display list data pos = displayListOffset; while (pos < displayListOffset + displayListSize) { byte cmd = basis.filebytes[pos]; pos++; switch ((DisplayListCommand)cmd) { case DisplayListCommand.kGXCmdNOP: break; case DisplayListCommand.kGXCmdLoadIndxA: case DisplayListCommand.kGXCmdLoadIndxB: case DisplayListCommand.kGXCmdLoadIndxC: case DisplayListCommand.kGXCmdLoadIndxD: pos += 4; break; default: { byte drawCmd = (byte)((cmd & 0x78) >> 3); byte vat = (byte)(cmd & 0x07); if (drawCmd > 7) { Console.WriteLine("Invalid Display List command " + cmd + " at offset " + pos); } else { DrawType drawType = (DrawType)drawCmd; //Console.WriteLine("Found drawtype at "+pos+": " + drawType); // Read element amount int elemCount = Utility.ReadUInt16BigEndian(basis.filebytes, pos); pos += 2; switch (drawType) { case DrawType.quads: case DrawType.quads2: case DrawType.triangles: int vertsMax = 4; if (drawType == DrawType.triangles) { vertsMax = 3; } for (int f = 0; f < elemCount / vertsMax; f++) { Face newFace = new Face(); for (int i = 0; i < vertsMax; i++) { if (boneNamesOffset != 0) { switch (i) { case 0: newFace.v1BoneIndex = basis.filebytes[pos]; break; case 1: newFace.v2BoneIndex = basis.filebytes[pos]; break; case 2: newFace.v3BoneIndex = basis.filebytes[pos]; break; case 3: newFace.v4BoneIndex = basis.filebytes[pos]; newFace.is_quad = true; break; } pos++; } for (int attr = 0; attr < 20; attr++) { //for each possible vertex attribute, if it exists, process it in order if (newMesh.presentAttributes.Contains((VertexAttributeArrayType)attr)) { newFace = AddAttrToFace(basis.filebytes, pos, (VertexAttributeArrayType)attr, newFace, i); pos = newFace.temp; } } newMesh.faces.Add(newFace); } } break; case DrawType.triangleStrip: { Face newFace = new Face(); int currentV = 0; int backupPos = 0; bool winding = false; for (int i = 0; i < elemCount; i++) { if (currentV == 1) { //if we're reading the penultimate vertex in a triangle, remember our pos because we'll need to come back to it when we process the next triangle (which overlaps slightly) backupPos = pos; } if (boneNamesOffset != 0) { switch (currentV) { case 0: newFace.v1BoneIndex = basis.filebytes[pos]; break; case 1: newFace.v2BoneIndex = basis.filebytes[pos]; break; case 2: newFace.v3BoneIndex = basis.filebytes[pos]; break; case 3: newFace.v4BoneIndex = basis.filebytes[pos]; newFace.is_quad = true; break; //this won't get triggered by a triangle, but might as well keep it here so I don't forget for quads } pos++; } if (winding && currentV == 0) { currentV = 1; } else if (winding && currentV == 1) { currentV = 0; } for (int attr = 0; attr < 20; attr++) { //for each possible vertex attribute, if it exists, process it in order if (newMesh.presentAttributes.Contains((VertexAttributeArrayType)attr)) { newFace = AddAttrToFace(basis.filebytes, pos, (VertexAttributeArrayType)attr, newFace, currentV); pos = newFace.temp; } } if (winding && currentV == 1) { currentV = 0; } else if (winding && currentV == 0) { currentV = 1; } currentV++; if (currentV == 3) { //if we just finished making a triangle newMesh.faces.Add(newFace); newFace = new Face(); currentV = 0; if (i < elemCount - 1) { //as long as we're not on the very last one, there's still work to do so back up a bit and keep reading the strip i -= 2; pos = backupPos; winding = !winding; } } } } break; default: Console.WriteLine("Unhandled draw type at offset " + pos + ": " + drawType); break; } } } break; } } //now get the materials ready newMesh.materials = new List <MaterialData>(); if (newMesh.hash_of_material != 0) { has_materials = true; } foreach (Subfile s in global.activePackage.subfiles) { if (s.hash == newMesh.hash_of_material && s.typeID == typeID_of_material) { switch ((global.TypeID)s.typeID) { case global.TypeID.MATD_MSK: case global.TypeID.MATD_MSA: if (s.filebytes == null || s.filebytes.Length == 0) { s.Load(); } newMesh.materials.Add(s.matd); break; case global.TypeID.MTST_MSK: case global.TypeID.MTST_MSA: if (s.filebytes == null || s.filebytes.Length == 0) { s.Load(); } foreach (MaterialData mat in s.mtst.mats) { newMesh.materials.Add(mat); } break; default: System.Windows.Forms.MessageBox.Show("RMDL tried to use a material of unexpected type ID: " + (global.TypeID)s.typeID); break; } break; } } meshes.Add(newMesh); } }
public TPLtexture(Subfile basis) { uint imagesize = 0; ushort width = 0; ushort height = 0; version = basis.filebytes[5]; int imageoffset = 0; int pos = 0; if (global.activePackage.packageType == Package.PackageType.SkyHeroes && Utility.ReadUInt32BigEndian(basis.filebytes, pos) != 0) //skip those annoying extra headers from MySims SkyHeroes { startoffile = 0x50; } pos = startoffile; if (global.activePackage.packageType == Package.PackageType.Agents || global.activePackage.packageType == Package.PackageType.Kingdom) { magic = Utility.ReadUInt32BigEndian(basis.filebytes, pos); pos += 4; flags = Utility.ReadUInt32BigEndian(basis.filebytes, pos); pos += 4; if (version == 1) //MYSIMS { pos = 0x1C; width = Utility.ReadUInt16BigEndian(basis.filebytes, pos); pos += 2; height = Utility.ReadUInt16BigEndian(basis.filebytes, pos); pos += 5; imageformat = (ImageFormat)basis.filebytes[pos]; pos++; imagecount = Utility.ReadUInt32BigEndian(basis.filebytes, pos); pos += 4; pos += 0x14; imageoffset = 0x4C; pos += 4; imagesize = (uint)(height * width * 4); } else if (version == 2) //MYSIMS { pos = 0x1C; width = Utility.ReadUInt16BigEndian(basis.filebytes, pos); pos += 2; height = Utility.ReadUInt16BigEndian(basis.filebytes, pos); pos += 5; imageformat = (ImageFormat)basis.filebytes[pos]; pos++; imagecount = Utility.ReadUInt32BigEndian(basis.filebytes, pos); pos += 4; pos += 0x14; imageoffset = 0x4C; pos += 4; imagesize = (uint)(height * width * 4); } else if (version == 3) //MYSIMS KINGDOM AND AGENTS { imagesize = Utility.ReadUInt32BigEndian(basis.filebytes, pos); pos += 4; pos += 0x0C; width = Utility.ReadUInt16BigEndian(basis.filebytes, pos); pos += 2; height = Utility.ReadUInt16BigEndian(basis.filebytes, pos); pos += 5; imageformat = (ImageFormat)basis.filebytes[pos]; pos++; imagecount = Utility.ReadUInt32BigEndian(basis.filebytes, pos); pos += 4; pos += 0x14; imageoffset = Utility.ReadInt32BigEndian(basis.filebytes, pos); pos += 4; } } else if (global.activePackage.packageType == Package.PackageType.SkyHeroes) { magic = Utility.ReadUInt32BigEndian(basis.filebytes, pos); pos += 4; flags = Utility.ReadUInt32BigEndian(basis.filebytes, pos); pos += 4; pos += 6; width = Utility.ReadUInt16BigEndian(basis.filebytes, pos); pos += 2; pos += 2; height = Utility.ReadUInt16BigEndian(basis.filebytes, pos); pos += 0x2D; imageformat = (ImageFormat)basis.filebytes[pos]; pos += 1; imageoffset = (pos + 0x20) - startoffile; } if (imagecount == 0) { imagecount = 1; } pos = startoffile + imageoffset; if (global.activePackage.packageType == Package.PackageType.SkyHeroes) { imagesize = (uint)((basis.filebytes.Length - startoffile) - (pos - startoffile)); } while ((width % 8) != 0) { width++; } while ((height % 8) != 0) { height++; } for (int img = 0; img < imagecount; img++) { Bitmap newImage = new Bitmap(width, height); switch (imageformat) { case ImageFormat.CMPR: for (int y = 0; y < height; y += 8) { for (int x = 0; x < width; x += 8) { for (int j = 0; j < 4; j++) { if ((j == 1 || j == 3) && ((x + 4) >= width)) { continue; } if ((j == 2 || j == 3) && ((y + 4) >= height)) { continue; } Color[] cols = new Color[4]; ushort col0ushort = Utility.ReadUInt16BigEndian(basis.filebytes, pos); pos += 2; ushort col1ushort = Utility.ReadUInt16BigEndian(basis.filebytes, pos); pos += 2; cols[0] = imageTools.ToRGB565(col0ushort); cols[1] = imageTools.ToRGB565(col1ushort); cols[2] = new Color(); cols[3] = new Color(); if (col0ushort > col1ushort) { cols[2] = Color.FromArgb(255, ((2 * cols[0].R) + (cols[1].R)) / 3, ((2 * cols[0].G) + (cols[1].G)) / 3, ((2 * cols[0].B) + (cols[1].B)) / 3); cols[3] = Color.FromArgb(255, ((2 * cols[1].R) + (cols[0].R)) / 3, ((2 * cols[1].G) + (cols[0].G)) / 3, ((2 * cols[1].B) + (cols[0].B)) / 3); } else { cols[2] = Color.FromArgb(255, (cols[0].R + cols[1].R) / 2, (cols[0].G + cols[1].G) / 2, (cols[0].B + cols[1].B) / 2); cols[3] = Color.FromArgb(0, 0, 0, 0); } uint indices = Utility.ReadUInt32BigEndian(basis.filebytes, pos); pos += 4; int xOffset = 0; int yOffset = 0; switch (j) { case 0: xOffset = 0; yOffset = 0; break; case 1: xOffset = 4; yOffset = 0; break; case 2: xOffset = 0; yOffset = 4; break; case 3: xOffset = 4; yOffset = 4; break; } int xOffsetLimit = xOffset + 3; for (int i = 30; i >= 0; i -= 2) { newImage.SetPixel(x + xOffset, y + yOffset, cols[(indices >> i) & 0x03]); xOffset++; if (xOffset > xOffsetLimit) { xOffset -= 4; yOffset++; } } } } } break; case ImageFormat.RGBA32: for (int y = 0; y < height; y += 4) { for (int x = 0; x < width; x += 4) { int xOffset = 0; int yOffset = 0; for (int j = 0; j < 16; j++) { newImage.SetPixel(x + xOffset, y + yOffset, imageTools.ReadNintendoRGBA32(basis.filebytes, pos)); pos += 2; xOffset++; if (xOffset > 3) { xOffset = 0; yOffset++; } } pos += 0x20; } } break; default: Console.WriteLine("That image format was not handled by the switch statement! Image format was: " + imageformat); break; } images.Add(newImage); } }
public MsaCollision(Subfile basis) { obj = new List <string>(); cumulativeVerts = 0; if (basis.filebytes.Length == 0) { Console.WriteLine("Couldn't process the MSA collision file because the input subfile was empty."); return; } int meshDataOffset = Utility.ReadInt32BigEndian(basis.filebytes, 0x14); int pos = meshDataOffset + 0x08; int offsetOfEndOfMeshData = meshDataOffset + Utility.ReadInt32BigEndian(basis.filebytes, pos); pos = meshDataOffset + 0x10; int sectionCount = Utility.ReadInt32BigEndian(basis.filebytes, pos); pos += 4; int[] sectionOffsets = new int[sectionCount]; for (int i = 0; i < sectionCount; i++) { sectionOffsets[i] = meshDataOffset + Utility.ReadInt32BigEndian(basis.filebytes, pos + (i * 4)); } //section 0 is some other data, the rest are vertex data seemingly pos = sectionOffsets[0]; //deal with section 0 here //now deal with other sections for (int i = 1; i < sectionCount; i++) { //seems to be triangle coordinates where each one overlaps at the end, then the section is terminated with 0x00000001 obj.Add("o object" + i); pos = sectionOffsets[i]; float UnkMultiplier = Utility.ReadSingleBigEndian(basis.filebytes, pos); pos += 4; float xUnk = Utility.ReadSingleBigEndian(basis.filebytes, pos); pos += 4; float yUnk = Utility.ReadSingleBigEndian(basis.filebytes, pos); pos += 4; float zUnk = Utility.ReadSingleBigEndian(basis.filebytes, pos); pos += 4; float Unk2Multiplier = Utility.ReadSingleBigEndian(basis.filebytes, pos); pos += 4; float xUnk2 = Utility.ReadSingleBigEndian(basis.filebytes, pos); pos += 4; float yUnk2 = Utility.ReadSingleBigEndian(basis.filebytes, pos); pos += 4; float zUnk2 = Utility.ReadSingleBigEndian(basis.filebytes, pos); pos += 4; float nudgeMultiplier = Utility.ReadSingleBigEndian(basis.filebytes, pos); pos += 4; float xNudge = Utility.ReadSingleBigEndian(basis.filebytes, pos); pos += 4; float yNudge = Utility.ReadSingleBigEndian(basis.filebytes, pos); pos += 4; float zNudge = Utility.ReadSingleBigEndian(basis.filebytes, pos); pos += 4; pos += 12; while (pos < basis.filebytes.Length && Utility.ReadInt32BigEndian(basis.filebytes, pos) != 1) { int chunkType = Utility.ReadInt32BigEndian(basis.filebytes, pos); pos += 4; Console.WriteLine("MSA collision chunk " + chunkType + " at " + pos); switch (chunkType) { case 0x04: //extents X Y Z of collision box vertices.Add(new Vertex(Utility.ReadSingleBigEndian(basis.filebytes, pos), Utility.ReadSingleBigEndian(basis.filebytes, pos + 4), Utility.ReadSingleBigEndian(basis.filebytes, pos + 8))); pos += 12; pos += 12; //seems to be a hash of a lua attribute or two? Possibly giving a name to this particular bit of collision. No obvious effects when nulled out (although there might be if the object is required by a lua script) break; default: Console.WriteLine("Unhandled MSA collision chunk type: " + chunkType); obj.Add("#dodgy"); break; } } foreach (Vertex v in vertices) { obj.Add("v " + v.position.x + " " + v.position.y + " " + v.position.z); } vertices.Clear(); cumulativeVerts += vertices.Count; } }
public void backtrackSubfile(Subfile s) { Console.WriteLine("Backtracking " + s.filename); switch ((global.TypeID)s.typeID) { case global.TypeID.TPL_MSK: case global.TypeID.TPL_MSA: foreach (Subfile superior in global.activePackage.subfiles) { //look for MATDs that reference this texture if ((global.TypeID)superior.typeID == global.TypeID.MATD_MSK || (global.TypeID)superior.typeID == global.TypeID.MATD_MSA) { superior.Load(); foreach (MaterialData.Param param in superior.matd.parameters) { if (param.paramType == MaterialData.MaterialParameter.diffuseMap && param.diffuse_texture == s) { backtrackSubfile(superior); return; } } superior.Unload(); } } break; case global.TypeID.MTST_MSK: case global.TypeID.MTST_MSA: foreach (Subfile superior in global.activePackage.subfiles) { //look for RMDLs that reference this materialset if ((global.TypeID)superior.typeID == global.TypeID.RMDL_MSK || (global.TypeID)superior.typeID == global.TypeID.RMDL_MSA) { superior.Load(); foreach (RevoModel.Mesh m in superior.rmdl.meshes) { if (m.hash_of_material == s.hash) { MessageBox.Show("Succesfully backtracked to model: " + superior.filename); foreach (TreeNode node in FileTree.Nodes[0].Nodes) { if (treeNodesAndSubfiles[node] == superior) { FileTree.SelectedNode = node; break; } } FileTree.SelectedNode.EnsureVisible(); return; } } superior.Unload(); } } break; case global.TypeID.MATD_MSK: case global.TypeID.MATD_MSA: foreach (Subfile superior in global.activePackage.subfiles) { //look for RMDLs and MTSTs that reference this material if ((global.TypeID)superior.typeID == global.TypeID.RMDL_MSK || (global.TypeID)superior.typeID == global.TypeID.RMDL_MSA || (global.TypeID)superior.typeID == global.TypeID.MTST_MSK || (global.TypeID)superior.typeID == global.TypeID.MTST_MSA) { superior.Load(); if (superior.rmdl != null) { foreach (RevoModel.Mesh m in superior.rmdl.meshes) { if (m.materials.Contains(s.matd)) { MessageBox.Show("Succesfully backtracked to model: " + superior.filename); foreach (TreeNode node in FileTree.Nodes[0].Nodes) { if (treeNodesAndSubfiles[node] == superior) { FileTree.SelectedNode = node; break; } } FileTree.SelectedNode.EnsureVisible(); return; } } } else if (superior.mtst != null) { if (superior.mtst.mats.Contains(s.matd)) { backtrackSubfile(superior); return; } } superior.Unload(); } } break; default: MessageBox.Show("Sorry, that type of file is not applicable for backtracking."); break; } }