// Dump the display list of the currently selected gfx node (if applicable) // This can contain vertices and triangles, but also draw settings like lighting and fog private void DumpButton_Click(object sender, EventArgs e) { if (SelectedNode != null && (SelectedNode is GfxDisplayList || SelectedNode is GfxAnimationNode || SelectedNode is GfxTranslatedModel || SelectedNode is GfxRotationNode)) { uint address = Config.Stream.GetUInt32(SelectedNode.Address + 0x14); richTextBoxGfx.Text = Fast3DDecoder.DecodeList(SegmentationUtilities.DecodeSegmentedAddress(address)); } else { MessageBox.Show("Select a display list node first"); } }
// Gives a string decoding the display list starting at a given address public static string DecodeList(uint address, int recursionDepth = 0) { if (recursionDepth > MaxRecursionDepth) { return("Recursion too deep"); } var res = new StringBuilder(); res.AppendLine(Indent(recursionDepth) + $"Decoding list at 0x{address:X8}"); for (int i = 0; i < MaxDisplayListLength; i++) { var firstWord = Config.Stream.GetUInt32(address); var secondWord = Config.Stream.GetUInt32(address + 4); var opcode = (F3DOpcode)((firstWord >> 24) & 0xFF); var name = Enum.GetName(typeof(F3DOpcode), opcode); res.Append(Indent(recursionDepth) + $"{firstWord:X8} {secondWord:X8} " + name + " "); // todo: interpret the other commands switch (opcode) { case F3DOpcode.G_LOADBLOCK: res.AppendLine(); break; case F3DOpcode.G_MOVEMEM: case F3DOpcode.G_SETTIMG: res.AppendLine($"0x{SegmentationUtilities.DecodeSegmentedAddress(secondWord):X8}"); break; case F3DOpcode.G_MTX: res.Append($"0x{SegmentationUtilities.DecodeSegmentedAddress(secondWord):X8} "); var p = (firstWord >> 16) & 0xFF; res.Append(((p & 0x01) != 0) ? "projection: " : "model view: "); res.Append(((p & 0x02) != 0) ? "load" : "multiply"); res.AppendLine(((p & 0x04) != 0) ? "and push" : "and don't push"); break; case F3DOpcode.G_SETTILESIZE: var h = ((secondWord & 0xFFF) + 4) / 4; var w = (((secondWord >> 12) & 0xFFF) + 4) / 4; res.AppendLine($"{w} * {h}"); break; case F3DOpcode.G_SETTILE: var colorFormat = (firstWord >> 21) & 0x7; res.Append(colorFormat < ColorModes.Length ? ColorModes[colorFormat] : "Invalid color mode"); int pixelBits = ((int)firstWord >> 19) & 0x3; res.AppendLine(" " + (4 << pixelBits) + "-bit"); break; case F3DOpcode.G_CLEARGEOMETRYMODE: case F3DOpcode.G_SETGEOMETRYMODE: res.AppendLine(GetGeometryFlags(secondWord)); break; case F3DOpcode.G_VTX: var vertexAmount = ((firstWord >> 20) & 0xF) + 1; var startIndex = ((firstWord >> 16) & 0xF); var vertexAddress = SegmentationUtilities.DecodeSegmentedAddress(secondWord); res.AppendLine($"{vertexAmount} vertices at 0x{vertexAddress:X8}, start index {startIndex}"); res.AppendLine(Indent(recursionDepth) + "(pos) flags (tex) (normal/color)"); for (byte j = 0; j < vertexAmount; j++) { uint add = (uint)(vertexAddress + (j * 0x10)); var x = Config.Stream.GetInt16(add + 0x00); var y = Config.Stream.GetInt16(add + 0x02); var z = Config.Stream.GetInt16(add + 0x04); var flags = Config.Stream.GetUInt16(add + 0x06); var texX = Config.Stream.GetInt16(add + 0x08); var texY = Config.Stream.GetInt16(add + 0x0A); var r = Config.Stream.GetByte(add + 0x0C); var g = Config.Stream.GetByte(add + 0x0D); var b = Config.Stream.GetByte(add + 0x0E); var a = Config.Stream.GetByte(add + 0x0F); res.AppendLine(Indent(recursionDepth) + $"({x}, {y}, {z}) 0x{flags:X4} ({texX}, {texY}) ({r}, {g}, {b}, {a})"); } break; case F3DOpcode.G_TRI1: var v1 = ((secondWord >> 16) & 0xFF) / 0x0A; var v2 = ((secondWord >> 8) & 0xFF) / 0x0A; var v3 = ((secondWord >> 0) & 0xFF) / 0x0A; res.AppendLine($"indices ({v1}, {v2}, {v3})"); break; case F3DOpcode.G_DL: res.AppendLine(); res.AppendLine(DecodeList(SegmentationUtilities.DecodeSegmentedAddress(secondWord), recursionDepth + 1)); break; default: res.AppendLine(); break; } if (opcode == F3DOpcode.G_ENDDL) { break; } address += 8; //Fast 3D instructions are always 8 bytes long } return(res.ToString()); }