public static void DumpTextures(byte[] romBytes, string outputDir) { Directory.CreateDirectory(outputDir + "Converted"); string fullOutputPath = outputDir + "Converted/texture/"; Directory.CreateDirectory(fullOutputPath); // ok this is dumb i need to fix this Filesystem.Filesystem filesystem = new Filesystem.Filesystem(romBytes); foreach (Filesystem.Filesystem.File file in filesystem.AllFiles.Where(file => file.fileTypeFromFileHeader == "UVTX")) { // TODO string outputFileName = $"[0x{file.formLocationInROM:x6}]"; UVTXFile uvtx = new UVTXFile(file.Sections.Single().Item2); StringBuilder fileStringBuilder = new StringBuilder(); fileStringBuilder.AppendLine("========================================"); fileStringBuilder.AppendLine($"{outputFileName} ({uvtx.texelData.Length} data | {uvtx.displayListCommands.Length} cmds | {uvtx.palettes.Length} palettes)"); fileStringBuilder.AppendLine($"(footer) size <{uvtx.imageWidth}, {uvtx.imageHeight}> | {uvtx.mipMapCount} mm |"); string?cmdString = SaveAsPNG(uvtx, fullOutputPath + outputFileName); if (cmdString == null) { continue; } fileStringBuilder.Append(cmdString); fileStringBuilder.AppendLine("Unknown: \n" + String.Join(" ", uvtx.unknownData.Select(b => b.ToString("X2")))); fileStringBuilder.AppendLine("Floats: " + String.Join(", ", uvtx.unknownFloats.Select(f => f.ToString("F8")))); Console.Write(fileStringBuilder.ToString()); } }
public static void DisplayModels(byte[] romBytes) { Filesystem.Filesystem filesystem = new Filesystem.Filesystem(romBytes); foreach (Filesystem.Filesystem.File file in filesystem.AllFiles.Where(file => file.fileTypeFromFileHeader == "UVMD")) { UVMDFile uvmd = new UVMDFile(new PowerByteArray(file.Sections.Single().Item2), filesystem); } }
public static void ParseUVCTs(byte[] romBytes) { // ok this is dumb i need to fix this Filesystem.Filesystem filesystem = new Filesystem.Filesystem(romBytes); foreach (Filesystem.Filesystem.File file in filesystem.AllFiles.Where(file => file.fileTypeFromFileHeader == "UVCT")) { PowerByteArray data = new PowerByteArray(file.Section("COMM")); new UVCTFile(data, filesystem); } }
public static void ParseUVTRs(byte[] romBytes) { //Directory.CreateDirectory(outputDir + "Converted"); //string fullOutputPath = outputDir + "Converted/UVTR/"; //Directory.CreateDirectory(fullOutputPath); // ok this is dumb i need to fix this Filesystem.Filesystem filesystem = new Filesystem.Filesystem(romBytes); foreach (Filesystem.Filesystem.File file in filesystem.AllFiles.Where(file => file.fileTypeFromFileHeader == "UVTR")) { PowerByteArray data = new PowerByteArray(file.Section("COMM")); UVTR uvtr = new UVTR(); uvtr.h_6float = data.NextSubArray(24).AsFloats(); uvtr.h_b1 = data.NextU8(); uvtr.h_b2 = data.NextU8(); uvtr.h_f1 = data.NextFloat(); uvtr.h_f2 = data.NextFloat(); uvtr.h_f3 = data.NextFloat(); using (ThreeDDisplayWindow window = new ThreeDDisplayWindow()) { Console.WriteLine(uvtr.h_b1 * uvtr.h_b2); uvtr.pB1xB2Mem = new UVTR140Struct[uvtr.h_b1 * uvtr.h_b2]; for (int i = 0; i < uvtr.pB1xB2Mem.Length; i++) { UVTR140Struct oneFourty = new UVTR140Struct(); if (data.NextU8() == 0) { continue; } oneFourty.m1 = new Matrix(data.NextSubArray(64).AsFloats()); byte unkByte = data.NextU8(); // m2 = identity matrix oneFourty.m2 = new Matrix(new float[] { 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1 }); // Copy translation from m1 to m2 oneFourty.m2[12] = oneFourty.m1[12]; oneFourty.m2[13] = oneFourty.m1[13]; oneFourty.m2[14] = oneFourty.m1[14]; /* FUN_801360cc(m2, m2) */ Console.WriteLine(oneFourty.m1); oneFourty.uvctFileIndex = data.NextU16(); var uvct = filesystem.GetFile("UVCT", oneFourty.uvctFileIndex); Console.WriteLine($"{uvct.formLocationInROM:x6}"); var uvctFile = new UVCTFile(new PowerByteArray(uvct.Section("COMM")), filesystem); uvctFile.AddToWindow(window, oneFourty.m1); /* oneFourty.pUVCT = processed filesystem.GetFile("UVCT", oneFourty.uvctFileIndex); */ /* if uvct does not exist, set oneFourty.unkFloat to 0 and move to next loop*/ /* at this point some stuff happens with the UVCT, idk what exactly */ uvtr.pB1xB2Mem[i] = oneFourty; } window.Run(); } uvtr.ushort1 = data.NextU16(); uvtr.uvrwFileIndex = data.NextU16(); /* * if (DAT_8002d1a8 == 0) { * uvtrObj->ushort1 = 0; * uVar1 = uvtrObj->ushort1; * } * else { * uVar1 = uvtrObj->ushort1; * } */ if (uvtr.ushort1 != 0) { /* Load UVRW COMM section */ uvtr.unk4Bytes = data.NextU32(); data.NextU16(); data.NextU16(); data.NextU16(); /* a whole bunch of nonsense */ /* includes reading a few more shorts (maybe) */ } } }
public static void DumpFonts(byte[] romBytes, string outputDir) { Directory.CreateDirectory(outputDir + "Converted"); string fullOutputPath = outputDir + "Converted/font/"; Directory.CreateDirectory(fullOutputPath); // ok this is dumb i need to fix this Filesystem.Filesystem filesystem = new Filesystem.Filesystem(romBytes); foreach (Filesystem.Filesystem.File file in filesystem.AllFiles.Where(file => file.fileTypeFromFileHeader == "UVFT")) { // TODO string outputSubfolder = $"{fullOutputPath}[0x{file.formLocationInROM:x6}]/"; Directory.CreateDirectory(outputSubfolder); List <byte[]> imageDatas = file.SectionsOfType("IMAG"); byte[] charIndexToASCII = file.Section("STRG"); byte[] frmtSec = file.Section("FRMT"); if (frmtSec.Length != 8) { throw new Exception(); } Texels.ColorFormat colorFormat = (Texels.ColorFormat)frmtSec.ReadUInt32(0); Texels.BitSize bitSize = (Texels.BitSize)frmtSec.ReadUInt32(4); CharacterDescriptor[] characterDescriptors = file.Section("BITM") .InGroupsOf(16) .Select((charDescData, index) => new CharacterDescriptor(charDescData, index)) .ToArray(); // First, save off the main images //for (int i = 0; i < imageDatas.Count; i++) //{ // byte[] imageData = imageDatas[i]; // IEnumerable<CharacterDescriptor> relevantChars = characterDescriptors.Where(c => c.sourceIMAGIndex == i); // int texelsPerLine = relevantChars.First().sourceIMAGTexelsPerLine; // if(relevantChars.Any(c => c.sourceIMAGTexelsPerLine != texelsPerLine)) // { // throw new Exception("Inconsistent texels per line in BITM section!"); // } // int imagHeightTexels = relevantChars.Max(c => c.heightTexels); // int bytesPerLine = Texels.GetNumBytes(texelsPerLine, bitSize); // Bitmap bitmap = Texels.ConvertToBitmap(imageData, colorFormat, bitSize, texelsPerLine, imagHeightTexels, bytesPerLine, false, false, null); // bitmap.Save($"{outputSubfolder}{i+1}.png"); //} // Now save each of the characters //string individualCharsDirectory = $"{outputSubfolder}Individual Characters/"; //Directory.CreateDirectory(individualCharsDirectory); foreach (CharacterDescriptor charDesc in characterDescriptors) { byte[] sourceIMAG = imageDatas[charDesc.sourceIMAGIndex]; byte[] texelData; if (bitSize == Texels.BitSize._4) { texelData = GetByteArrayStartingFromFourBitIndex(sourceIMAG, charDesc.startTexel).ToArray(); } else { texelData = sourceIMAG.Skip(Texels.GetNumBytes(charDesc.startTexel, bitSize)).ToArray(); } int bytesPerLine = Texels.GetNumBytes(charDesc.sourceIMAGTexelsPerLine, bitSize); Bitmap bitmap = Texels.ConvertToBitmap(texelData, colorFormat, bitSize, charDesc.widthTexels, charDesc.heightTexels, bytesPerLine, false, false, null); string filename = $"{charDesc.indexInBITM:D2}"; char ascii = Convert.ToChar(charIndexToASCII[charDesc.indexInBITM]); if (!Path.GetInvalidFileNameChars().Contains(ascii)) { filename += $" ({ascii})"; } bitmap.Save($"{outputSubfolder}{filename}.png"); } Console.WriteLine($"Converted font at {file.formLocationInROM:x6}"); } }
public static void DumpBlits(byte[] romBytes, string outputDir) { Directory.CreateDirectory(outputDir + "Converted"); string fullOutputPath = outputDir + "Converted/blit/"; Directory.CreateDirectory(fullOutputPath); // ok this is dumb i need to fix this Filesystem.Filesystem filesystem = new Filesystem.Filesystem(romBytes); foreach (Filesystem.Filesystem.File file in filesystem.AllFiles.Where(file => file.fileTypeFromFileHeader == "UVBT")) { string outputFileName = $"[0x{file.formLocationInROM:x6}]"; byte[] bytes = file.Sections.Single().Item2; ColorFormat colorFormat = (ColorFormat)bytes.ReadUInt16(0); BitSize bitSize = Texels.NumBitsToBitSize(bytes.ReadUInt16(2)); ushort width = bytes.ReadUInt16(4); ushort texelsPerLine = bytes.ReadUInt16(6); ushort height = bytes.ReadUInt16(8); ushort tileWidth = bytes.ReadUInt16(10); ushort tileHeight = bytes.ReadUInt16(12); byte[] colorData = bytes.Subsection(14, bytes.Length - 14); Console.WriteLine(bytes.Subsection(0, 14).PrettyPrint()); Console.WriteLine($"{outputFileName} {bitSize}-bit {colorFormat}\t <{width} ({texelsPerLine}), {height}> [{tileWidth} {tileHeight}]"); int bytesPerTile = (int)(tileWidth * tileHeight * Texels.BitSizeToNumBytesFloat(bitSize)); Bitmap outBitmap = new Bitmap(width, height); int p = 0; int curTopLeftX = 0; int curTopLeftY = 0; while (p < colorData.Length) { if (curTopLeftY + tileHeight > height) { tileHeight = (ushort)(height - curTopLeftY); bytesPerTile = (int)(tileWidth * tileHeight * Texels.BitSizeToNumBytesFloat(bitSize)); } byte[] tileColorData; if (p + bytesPerTile >= colorData.Length) { tileColorData = new byte[bytesPerTile]; Array.Copy(colorData, p, tileColorData, 0, colorData.Length - p); } else { tileColorData = colorData.Subsection(p, bytesPerTile); } // TODO: why arent 32-bit textures deinterleaved? Bitmap tile = Texels.ConvertToBitmap(tileColorData, colorFormat, bitSize, tileWidth, tileHeight, (int)(tileWidth * Texels.BitSizeToNumBytesFloat(bitSize)), false, bitSize != BitSize._32, null); CopyToMainBitmap(tile, curTopLeftX, curTopLeftY, outBitmap); p += bytesPerTile; curTopLeftX += tileWidth; if (curTopLeftX >= width) { curTopLeftX = 0; curTopLeftY += tileHeight; if (curTopLeftY >= height) { break; } } } outBitmap.Save(fullOutputPath + outputFileName + ".png"); } }
public UVMDFile(PowerByteArray fileBytes, Filesystem.Filesystem filesystem) { Console.WriteLine("NEW UVMD"); { lodCount = fileBytes.NextU8(); partsPerLOD = fileBytes.NextU8(); b3 = fileBytes.NextU8(); byte alwaysZero = fileBytes.NextU8(); if (alwaysZero != 0) { throw new Exception(); } vertexCount = fileBytes.NextU16(); // These are here for memory allocation, but we don't care! ushort totalMaterials = fileBytes.NextU16(); //24 structs ushort totalCommands = fileBytes.NextU16(); if ((b3 & 0x80) != 0) { // appears to be 10 floats, 3 signed shorts, 1 byte // TODO UnknownOptionalHeaderData unknownOptionalData = new UnknownOptionalHeaderData(fileBytes.NextSubArray(0x2F)); } float1 = fileBytes.NextFloat(); float2 = fileBytes.NextFloat(); float3 = fileBytes.NextFloat(); } // Lets allocate all memory here since this is where it happens anyway // 36 bytes: already done, that's the response struct // b1 * 12: subsections pLODs = new LOD[lodCount]; // b2 * 64: matrices pMatrices = new Matrix[partsPerLOD]; // b1 * b2 * 44: parts // this is initialized in the loop // short1 * 16 // this is initialized in the loop // short2 * 24 // this is initialized in the loop // short3 * 8 // this is initialized in the loop //////////////////////////////// // Overall structure here is correct, but the processing is not implemented yet for (int i = 0; i < lodCount; i++) { Console.WriteLine(" LOD " + i); LOD lodInfo; lodInfo.partCount = fileBytes.NextU8(); lodInfo.b2 = fileBytes.NextU8(); lodInfo.f = fileBytes.NextFloat(); lodInfo.f = lodInfo.f * lodInfo.f; lodInfo.pModelParts = new ModelPart[lodInfo.partCount]; for (int j = 0; j < lodInfo.partCount; j++) { Console.WriteLine(" PART " + j); ModelPart modelPart; modelPart.b5 = fileBytes.NextU8(); modelPart.b6 = fileBytes.NextU8(); modelPart.b7 = fileBytes.NextU8(); modelPart.sixFloats = fileBytes.NextSubArray(24).AsFloats(); modelPart.sixFloats = modelPart.sixFloats.Select(fl => fl * float2).ToArray(); byte stackByte1 = fileBytes.NextU8(); byte stackByte2 = fileBytes.NextU8(); modelPart.materialCount = fileBytes.NextU8(); modelPart.pMaterials = new Material[modelPart.materialCount]; byte firstWordEverHadHighBitSet = 0; for (int k = 0; k < modelPart.materialCount; k++) { Console.WriteLine(" MATERIAL " + k); Material material = new Material(); material.unk4 = fileBytes.NextU32(); float unk2 = fileBytes.NextFloat(); float unk3 = fileBytes.NextFloat(); float unk4 = fileBytes.NextFloat(); material.vertCount = fileBytes.NextU16(); material.unksh18 = fileBytes.NextU16(); material.unksh16 = fileBytes.NextU16(); material.unksh20 = fileBytes.NextU16(); ushort shortsCount = fileBytes.NextU16(); ushort commandCount = fileBytes.NextU16(); uint textureRef = material.unk4 & 0xFFF; Console.WriteLine($" {material.unk4:X8}"); if (textureRef != 0xFFF) { //Filesystem.Filesystem.File textureFile = filesystem.GetFile("UVTX", (int)textureRef); // TODO: load texture Console.WriteLine(" Textured!"); } if ((int)(material.unk4 << 13) < 0) { //Console.WriteLine($"{twentyFourStruct.unk4:X8} {twentyFourStruct.unk4 << 13:X8}"); // TODO: lighting information? // twentyFourStruct.unksh12 = response from light function Console.WriteLine(" Lit!"); } else { material.unk_someLightingThing = -1; } if ((material.unk4 & 0x08_000000) != 0) { firstWordEverHadHighBitSet = 1; } material.pVertices = fileBytes.NextSubArray(material.vertCount * 16).InGroupsOf(16).Select(d => new Vertex(d)).ToArray(); if (textureRef != 0xFFF) { foreach (var x in material.pVertices) { Console.WriteLine(x); } } RSPCommand[] commands = UnpackTriangleCommands(fileBytes, shortsCount, commandCount); material.pCommandsInRDRAM = commands; modelPart.pMaterials[k] = material; } modelPart.unk1 = firstWordEverHadHighBitSet; lodInfo.pModelParts[j] = modelPart; } pLODs[i] = lodInfo; } for (int m = 0; m < partsPerLOD; m++) { pMatrices[m] = new Matrix(fileBytes.NextSubArray(64).AsFloats()); } if (fileBytes.Length != (int)Math.Ceiling(fileBytes.Position / 8f) * 8) { throw new Exception(); } //////////////////////////////// using (ThreeDDisplayWindow window = new ThreeDDisplayWindow()) { foreach (LOD lod in pLODs) { bool hadTextured = false; for (int i = 0; i < lod.partCount; i++) { ModelPart part = lod.pModelParts[i]; Matrix m = pMatrices[i]; foreach (Material material in part.pMaterials) { List <Vertex> verts = MaterialToVertexData(material.pVertices, material.pCommandsInRDRAM, m); uint textureRef = material.unk4 & 0xFFF; if (textureRef != 0xFFF) { Filesystem.Filesystem.File textureFile = filesystem.GetFile("UVTX", (int)textureRef) !; Console.WriteLine($"{textureFile.formLocationInROM:x6}"); window.AddTexturedVertices(verts, new UVTX.UVTXFile(textureFile.Section("COMM"))); hadTextured = true; } else { window.AddVertices(verts); } } //lodData.AddRange(part.pMaterials.SelectMany(material => MaterialToVertexData(material.pVertices, material.pCommandsInRDRAM, m))); } if (hadTextured) { window.Run(); } } } }