private static void TriangleMapParse_cmd04(ROM rom, TriangleMap map, TriangleMapParseState state) { state.vertexLoadCmd = (UInt64)rom.Read64(); byte vertexDesc = rom.Read8(1); byte vertexCount = (byte)(((vertexDesc & 0xF0) >> 4) + 1); byte vertexOffset = (byte)((vertexDesc & 0x0F)); Int32 vertexSegmentedAddress = rom.Read32(4); state.segmentedVertexBufferAddress = vertexSegmentedAddress; Int32 romPtr = rom.GetROMAddress(vertexSegmentedAddress); if (romPtr == -1) { throw new ArgumentException("Invalid segmented address!"); } rom.PushOffset(romPtr); for (int vertex = vertexOffset; vertex < vertexCount; vertex++) { Int64 lo = rom.Read64(); Int64 hi = rom.Read64(8); state.vbuf[vertex] = new Vertex((UInt64)lo, (UInt64)hi); state.vbufRomStart[vertex] = rom.offset; state.scrollBuf[vertex] = FindMatchingScroll(state.scrolls, vertexSegmentedAddress, state.td); rom.AddOffset(0x10); vertexSegmentedAddress += 0x10; } rom.PopOffset(); }
private static void TriangleMapParse_cmdBB(ROM rom, TriangleMap map, TriangleMapParseState state) { if ((UInt64)rom.Read64() == 0xBB000000FFFFFFFF) { state.state = VisualMapParseStateCmd.Footer; } TriangleMapParse_common(rom, map, state); }
private static void TriangleMapParse_cmdFD(ROM rom, TriangleMap map, TriangleMapParseState state) { UInt64 fdCmd = state.td.GetTextureCMD(); if (state.state != VisualMapParseStateCmd.Texture) { state.state = VisualMapParseStateCmd.Texture; state.td = new ScrollingTextureDescription(); if (state.envColorCmd != null) { state.td.Add(state.envColorCmd.GetValueOrDefault(), 0 /*FIXME*/); } } TriangleMapParse_common(rom, map, state); }
private static void TriangleMapParse_cmdFB(ROM rom, TriangleMap map, TriangleMapParseState state) { // Some importers have the only EnvColor func for everything lmfao if (rom.Read8(8) != 0xFD) { goto fini; } state.state = VisualMapParseStateCmd.Texture; state.envColorCmd = (ulong)rom.Read64(); state.td = new ScrollingTextureDescription(); fini: TriangleMapParse_common(rom, map, state); }
private static void TriangleMapParse_cmdBF(ROM rom, TriangleMap map, TriangleMapParseState state) { state.isHeader = false; state.state = VisualMapParseStateCmd.Footer; byte v0index = (byte)(rom.Read8(5) / 0xA); byte v1index = (byte)(rom.Read8(6) / 0xA); byte v2index = (byte)(rom.Read8(7) / 0xA); state.vertexBytes.AddRegion(state.vbufRomStart[v0index], 0x10); state.vertexBytes.AddRegion(state.vbufRomStart[v1index], 0x10); state.vertexBytes.AddRegion(state.vbufRomStart[v2index], 0x10); // This assumes all scrolls are scrolling at the same speed which is usually true :3 if (state.scrollBuf[v0index] != state.scrollBuf[v1index] || state.scrollBuf[v0index] != state.scrollBuf[v2index] || state.scrollBuf[v1index] != state.scrollBuf[v2index]) { throw new Exception("Vertices are scrolling at different scrolls"); } Scroll scroll = state.scrollBuf[v0index]; if ((scroll == null && state.td.scroll != null) || (scroll != null && !scroll.Equals(state.td.scroll))) { ScrollingTextureDescription oldTd = state.td; state.td = new ScrollingTextureDescription(); state.td.AddRange(oldTd); state.td.scroll = scroll; if (scroll is EditorScroll editorScroll) { state.td.RegisterScroll(editorScroll); } if (scroll is TextureScroll) { state.td.omitScrollCheck = true; } } state.td.RegisterVertex(state.segmentedVertexBufferAddress + v0index * 0x10); state.td.RegisterVertex(state.segmentedVertexBufferAddress + v1index * 0x10); state.td.RegisterVertex(state.segmentedVertexBufferAddress + v2index * 0x10); map.AddTriangle(state.td, state.vbuf[v0index], state.vbuf[v1index], state.vbuf[v2index]); }
private static void TriangleMapParse_common(ROM rom, TriangleMap map, TriangleMapParseState state) { switch (state.state) { case VisualMapParseStateCmd.Header: map.AddHeaderCmd((UInt64)rom.Read64()); break; case VisualMapParseStateCmd.Texture: state.td.Add((UInt64)rom.Read64(), rom.GetSegmentedAddress(rom.offset)); break; case VisualMapParseStateCmd.Footer: map.AddFooterCmd((UInt64)rom.Read64()); break; } }
public static void PerformTriangleMapRebuild(ROM realRom, Region region, int maxDLLength, List <ScrollObject> scrolls) { TriangleMapParseState state = new TriangleMapParseState(scrolls); DisplayListRegion dlRegion = (DisplayListRegion)region; TriangleMap map = new TriangleMap(); realRom.PushOffset(region.romStart); byte curCmdIndex; do { curCmdIndex = realRom.Read8(); TriangleMapParserCmd func = triangleMapParser[curCmdIndex]; func(realRom, map, state); realRom.AddOffset(8); }while (realRom.offset < region.romStart + region.length); realRom.PopOffset(); ROM fakeRom = (ROM)realRom.Clone(); // bzero fakeRom.PushOffset(region.romStart); { do { fakeRom.Write64(0x0101010101010101); fakeRom.AddOffset(8); } while (fakeRom.offset < region.romStart + region.length); } fakeRom.PopOffset(); fakeRom.offset = region.romStart; int triangleMapLength = map.MakeF3D(fakeRom, state.vertexBytes, new ScrollFactory(scrolls)); if (triangleMapLength > maxDLLength) { throw new OutOfMemoryException("No memory for DL available :("); } realRom.TransferFrom(fakeRom); realRom.offset = fakeRom.offset; region.length = realRom.offset - region.romStart; region.data = new byte[region.length]; realRom.ReadData(region.romStart, region.length, region.data); }
public Mesh Create(int X, int Y, Map Map_, TerrainTileSet TileSet) { Map map = Map_; // Create vertex array Vector3[] array_vec = new Vector3[PARTITION_WIDTH * PARTITION_HEIGHT * 4]; // Create triangles array int[] array_tri = new int[PARTITION_WIDTH * PARTITION_HEIGHT * 6]; // Create UV arrays Vector2[] uv1 = new Vector2[array_vec.Length]; Vector2[] uv2 = new Vector2[array_vec.Length]; Vector4[] uv3 = new Vector4[array_vec.Length]; // Triangle map m_TerrainMap = new TriangleMap[PARTITION_WIDTH * PARTITION_HEIGHT * 2]; // Go through all tiles int partition_begin_x = 0; int partition_begin_y = 0; int partition_height = PARTITION_HEIGHT; int partition_width = PARTITION_WIDTH; for (int i = partition_begin_y, last_vertex = 0; i < partition_height; ++i) { for (int j = partition_begin_x; j < partition_width; ++j) { // Get Tile ja2.TerrainTile tile = map.GetTile(j + X * PARTITION_WIDTH, i + Y * PARTITION_HEIGHT); // Create vertices array_vec[GetVertexIndex(j, i)] = TileVertex(j, i, 0); array_vec[GetVertexIndex(j, i) + 1] = TileVertex(j, i, 1); array_vec[GetVertexIndex(j, i) + 2] = TileVertex(j, i, 2); array_vec[GetVertexIndex(j, i) + 3] = TileVertex(j, i, 3); last_vertex += 4; // Get the tile material types byte mat_v0 = tile.GetTerrainType(ja2.TerrainTile.Vertex.NORTH), mat_v1 = tile.GetTerrainType(ja2.TerrainTile.Vertex.WEST), mat_v2 = tile.GetTerrainType(ja2.TerrainTile.Vertex.SOUTH), mat_v3 = tile.GetTerrainType(ja2.TerrainTile.Vertex.EAST); // Get 1. and 2. material byte mat_1 = mat_v0; byte mat_2 = mat_1; if (mat_v0 != mat_v1) { mat_2 = mat_v1; } else if (mat_v0 != mat_v2) { mat_2 = mat_v2; } else if (mat_v0 != mat_v3) { mat_2 = mat_v3; } // Get alpha splat index byte alpha_index = 1; alpha_index |= (byte)((mat_v1 == mat_1) ? 2 : 0); alpha_index |= (byte)((mat_v2 == mat_1) ? 4 : 0); alpha_index |= (byte)((mat_v3 == mat_1) ? 8 : 0); // If materials need to be inverted if (alpha_index > 7) { byte mat_helper = mat_1; mat_1 = mat_2; mat_2 = mat_helper; alpha_index = (byte)(~alpha_index & 15); } // Get the primary tile type information TextureAtlasInfo primary_mat_info = TileSet.GetTileType(mat_1, tile.variant); // Get secondary tile type information TextureAtlasInfo secondary_mat_info = TileSet.GetTileType(mat_2, tile.variant); // Get the alpha splat info for tile TextureAtlasInfo alpha_splat_mat_info = TileSet.splatUsed.GetSplat(alpha_index); // Texture coordinates uv1[last_vertex - 4] = new Vector2(primary_mat_info.uvOffsetW + primary_mat_info.uvWidth / 2, primary_mat_info.uvOffsetH); uv1[last_vertex - 3] = new Vector2(primary_mat_info.uvOffsetW, primary_mat_info.uvOffsetH - primary_mat_info.uvHeight / 2); uv1[last_vertex - 2] = new Vector2(primary_mat_info.uvOffsetW + primary_mat_info.uvWidth / 2, primary_mat_info.uvOffsetH - primary_mat_info.uvHeight); uv1[last_vertex - 1] = new Vector2(primary_mat_info.uvOffsetW + primary_mat_info.uvWidth, primary_mat_info.uvOffsetH - primary_mat_info.uvHeight / 2); uv2[last_vertex - 4] = new Vector2(secondary_mat_info.uvOffsetW + secondary_mat_info.uvWidth / 2, secondary_mat_info.uvOffsetH); uv2[last_vertex - 3] = new Vector2(secondary_mat_info.uvOffsetW, secondary_mat_info.uvOffsetH - secondary_mat_info.uvHeight / 2); uv2[last_vertex - 2] = new Vector2(secondary_mat_info.uvOffsetW + secondary_mat_info.uvWidth / 2, secondary_mat_info.uvOffsetH - secondary_mat_info.uvHeight); uv2[last_vertex - 1] = new Vector2(secondary_mat_info.uvOffsetW + secondary_mat_info.uvWidth, secondary_mat_info.uvOffsetH - secondary_mat_info.uvHeight / 2); uv3[last_vertex - 4] = new Vector4(alpha_splat_mat_info.uvOffsetW + alpha_splat_mat_info.uvWidth / 2, alpha_splat_mat_info.uvOffsetH, 0); uv3[last_vertex - 3] = new Vector4(alpha_splat_mat_info.uvOffsetW, alpha_splat_mat_info.uvOffsetH - alpha_splat_mat_info.uvHeight / 2, 0, 0); uv3[last_vertex - 2] = new Vector4(alpha_splat_mat_info.uvOffsetW + alpha_splat_mat_info.uvWidth / 2, alpha_splat_mat_info.uvOffsetH - alpha_splat_mat_info.uvHeight, 0, 0); uv3[last_vertex - 1] = new Vector4(alpha_splat_mat_info.uvOffsetW + alpha_splat_mat_info.uvWidth, alpha_splat_mat_info.uvOffsetH - alpha_splat_mat_info.uvHeight / 2, 0, 0); // Create triangles int triangle_index = GetTriIndex(j, i); array_tri[triangle_index] = last_vertex - 4; array_tri[triangle_index + 1] = last_vertex - 1; array_tri[triangle_index + 2] = last_vertex - 3; array_tri[triangle_index + 3] = last_vertex - 3; array_tri[triangle_index + 4] = last_vertex - 1; array_tri[triangle_index + 5] = last_vertex - 2; // Save triangles int triangle_index_raw = j * 2 + i * PARTITION_WIDTH * 2; m_TerrainMap[triangle_index_raw] = new TriangleMap(tile.x, tile.y); m_TerrainMap[triangle_index_raw + 1] = new TriangleMap(tile.x, tile.y); } } Mesh mesh = new Mesh(); mesh.vertices = array_vec; mesh.triangles = array_tri; mesh.uv = uv1; mesh.uv2 = uv2; mesh.tangents = uv3; mesh.RecalculateNormals(); return(mesh); }
public Mesh Create(int X, int Y, Map Map_, TerrainTileSet TileSet) { Map map = Map_; // Create vertex array Vector3[] array_vec = new Vector3[PARTITION_WIDTH * PARTITION_HEIGHT * 4]; // Create triangles array int[] array_tri = new int[PARTITION_WIDTH * PARTITION_HEIGHT * 6]; // Create UV arrays Vector2[] uv1 = new Vector2[array_vec.Length]; Vector2[] uv2 = new Vector2[array_vec.Length]; Vector4[] uv3 = new Vector4[array_vec.Length]; // Triangle map m_TerrainMap = new TriangleMap[PARTITION_WIDTH * PARTITION_HEIGHT * 2]; // Go through all tiles int partition_begin_x = 0; int partition_begin_y = 0; int partition_height = PARTITION_HEIGHT; int partition_width = PARTITION_WIDTH; for (int i = partition_begin_y, last_vertex = 0; i < partition_height; ++i) { for (int j = partition_begin_x; j < partition_width; ++j) { // Get Tile ja2.TerrainTile tile = map.GetTile(j + X * PARTITION_WIDTH, i + Y * PARTITION_HEIGHT); // Create vertices array_vec[GetVertexIndex(j, i)] = TileVertex(j, i, 0); array_vec[GetVertexIndex(j, i) + 1] = TileVertex(j, i, 1); array_vec[GetVertexIndex(j, i) + 2] = TileVertex(j, i, 2); array_vec[GetVertexIndex(j, i) + 3] = TileVertex(j, i, 3); last_vertex += 4; // Get the tile material types byte mat_v0 = tile.GetTerrainType(ja2.TerrainTile.Vertex.NORTH), mat_v1 = tile.GetTerrainType(ja2.TerrainTile.Vertex.WEST), mat_v2 = tile.GetTerrainType(ja2.TerrainTile.Vertex.SOUTH), mat_v3 = tile.GetTerrainType(ja2.TerrainTile.Vertex.EAST); // Get 1. and 2. material byte mat_1 = mat_v0; byte mat_2 = mat_1; if (mat_v0 != mat_v1) mat_2 = mat_v1; else if (mat_v0 != mat_v2) mat_2 = mat_v2; else if (mat_v0 != mat_v3) mat_2 = mat_v3; // Get alpha splat index byte alpha_index = 1; alpha_index |= (byte)((mat_v1 == mat_1) ? 2 : 0); alpha_index |= (byte)((mat_v2 == mat_1) ? 4 : 0); alpha_index |= (byte)((mat_v3 == mat_1) ? 8 : 0); // If materials need to be inverted if (alpha_index > 7) { byte mat_helper = mat_1; mat_1 = mat_2; mat_2 = mat_helper; alpha_index = (byte)(~alpha_index & 15); } // Get the primary tile type information TextureAtlasInfo primary_mat_info = TileSet.GetTileType(mat_1, tile.variant); // Get secondary tile type information TextureAtlasInfo secondary_mat_info = TileSet.GetTileType(mat_2, tile.variant); // Get the alpha splat info for tile TextureAtlasInfo alpha_splat_mat_info = TileSet.splatUsed.GetSplat(alpha_index); // Texture coordinates uv1[last_vertex - 4] = new Vector2(primary_mat_info.uvOffsetW + primary_mat_info.uvWidth / 2, primary_mat_info.uvOffsetH); uv1[last_vertex - 3] = new Vector2(primary_mat_info.uvOffsetW, primary_mat_info.uvOffsetH - primary_mat_info.uvHeight / 2); uv1[last_vertex - 2] = new Vector2(primary_mat_info.uvOffsetW + primary_mat_info.uvWidth / 2, primary_mat_info.uvOffsetH - primary_mat_info.uvHeight); uv1[last_vertex - 1] = new Vector2(primary_mat_info.uvOffsetW + primary_mat_info.uvWidth, primary_mat_info.uvOffsetH - primary_mat_info.uvHeight / 2); uv2[last_vertex - 4] = new Vector2(secondary_mat_info.uvOffsetW + secondary_mat_info.uvWidth / 2, secondary_mat_info.uvOffsetH); uv2[last_vertex - 3] = new Vector2(secondary_mat_info.uvOffsetW, secondary_mat_info.uvOffsetH - secondary_mat_info.uvHeight / 2); uv2[last_vertex - 2] = new Vector2(secondary_mat_info.uvOffsetW + secondary_mat_info.uvWidth / 2, secondary_mat_info.uvOffsetH - secondary_mat_info.uvHeight); uv2[last_vertex - 1] = new Vector2(secondary_mat_info.uvOffsetW + secondary_mat_info.uvWidth, secondary_mat_info.uvOffsetH - secondary_mat_info.uvHeight / 2); uv3[last_vertex - 4] = new Vector4(alpha_splat_mat_info.uvOffsetW + alpha_splat_mat_info.uvWidth / 2, alpha_splat_mat_info.uvOffsetH, 0); uv3[last_vertex - 3] = new Vector4(alpha_splat_mat_info.uvOffsetW, alpha_splat_mat_info.uvOffsetH - alpha_splat_mat_info.uvHeight / 2, 0, 0); uv3[last_vertex - 2] = new Vector4(alpha_splat_mat_info.uvOffsetW + alpha_splat_mat_info.uvWidth / 2, alpha_splat_mat_info.uvOffsetH - alpha_splat_mat_info.uvHeight, 0, 0); uv3[last_vertex - 1] = new Vector4(alpha_splat_mat_info.uvOffsetW + alpha_splat_mat_info.uvWidth, alpha_splat_mat_info.uvOffsetH - alpha_splat_mat_info.uvHeight / 2, 0, 0); // Create triangles int triangle_index = GetTriIndex(j, i); array_tri[triangle_index] = last_vertex - 4; array_tri[triangle_index + 1] = last_vertex - 1; array_tri[triangle_index + 2] = last_vertex - 3; array_tri[triangle_index + 3] = last_vertex - 3; array_tri[triangle_index + 4] = last_vertex - 1; array_tri[triangle_index + 5] = last_vertex - 2; // Save triangles int triangle_index_raw = j * 2 + i * PARTITION_WIDTH * 2; m_TerrainMap[triangle_index_raw] = new TriangleMap(tile.x, tile.y); m_TerrainMap[triangle_index_raw + 1] = new TriangleMap(tile.x, tile.y); } } Mesh mesh = new Mesh(); mesh.vertices = array_vec; mesh.triangles = array_tri; mesh.uv = uv1; mesh.uv2 = uv2; mesh.tangents = uv3; mesh.RecalculateNormals(); return mesh; }
private static void TriangleMapParse_cmdF2(ROM rom, TriangleMap map, TriangleMapParseState state) { TriangleMapParse_common(rom, map, state); state.state = state.isHeader ? VisualMapParseStateCmd.Header : VisualMapParseStateCmd.Footer; // Case for fog }
public static void RebuildTriangleMap(ROM realRom, Region region, int maxDLLength, TriangleMap map, SortedRegionList vertexData, ScrollFactory factory) { ROM fakeRom = (ROM)realRom.Clone(); // bzero fakeRom.PushOffset(region.romStart); { do { fakeRom.Write64(0x0101010101010101); fakeRom.AddOffset(8); } while (fakeRom.offset < region.romStart + region.length); } fakeRom.PopOffset(); fakeRom.offset = region.romStart; int triangleMapLength = map.MakeF3D(fakeRom, vertexData, factory); if (triangleMapLength > maxDLLength) { throw new OutOfMemoryException("No memory for DL available :("); } realRom.TransferFrom(fakeRom); realRom.offset = fakeRom.offset; region.length = realRom.offset - region.romStart; region.data = new byte[region.length]; realRom.ReadData(region.romStart, region.length, region.data); }
public static void GetTriangleMap(ROM realRom, Region region, int maxDLLength, List <ScrollObject> scrolls, out TriangleMap map, out SortedRegionList vertexData) { TriangleMapParseState state = new TriangleMapParseState(scrolls); DisplayListRegion dlRegion = (DisplayListRegion)region; map = new TriangleMap(); realRom.PushOffset(region.romStart); byte curCmdIndex; do { curCmdIndex = realRom.Read8(); TriangleMapParserCmd func = triangleMapParser[curCmdIndex]; func(realRom, map, state); realRom.AddOffset(8); }while (realRom.offset < region.romStart + region.length); realRom.PopOffset(); // Check map validity // There are 2 possible ways to mess up scroll // 'Too much' - scroll is performing too much scrolling, 1st warn detect, 2nd falsing, 3rd can fix such scroll if scrolls are done properly // 'Not enough' - scroll is not scrolling the whole texture, 2nd warn may be able to detect that, no fix yet but 'stretch' the scroll should work // I assume there is no scrolls that do not correspond to no texture, such case will leave weird things :) // Currently ScrollingTextures cannot be longed so it is impossible to fix 'Not enough' :( List <ScrollingTextureDescription> brokenTextures = new List <ScrollingTextureDescription>(); { // Not enough HashSet <TextureDescription> scrollingTds = new HashSet <TextureDescription>(map.map.Keys.Where(k => k.scroll != null)); foreach (TextureDescription td in scrollingTds) { var stds = map.map.Keys.Where(k => k.Equals(td)).ToList(); if (stds.Count() > 1) { int a = 0; } } // Check if scroll 'fits' foreach (ScrollingTextureDescription std in map.map.Keys) { if (std.scroll == null) { continue; } if (!std.vertexRegions.Equals(std.scrollRegions)) { brokenTextures.Add(std); } } } foreach (ScrollingTextureDescription brokenTd in brokenTextures) { if (brokenTd.omitScrollCheck) { continue; } // Figure out the way to "heal", either drop scroll or extend it // If scroll does not start at the same place, just drop it, such solution may backfire if 2 scrolls intersect bool shouldDrop = brokenTd.scrollRegions.RegionList.First().Key != brokenTd.vertexRegions.RegionList.First().Key; if (shouldDrop) { // Find if texture without scroll exists, if it does, merge tris in it, otherwise drop the scroll List <ScrollingTextureDescription> similarTextures = map.map.Keys.Where(k => k.scroll == null).Where(k => TextureDescription.Equals(brokenTd, k)).ToList(); if (similarTextures.Count() != 0) { ScrollingTextureDescription stdNoScroll = similarTextures[0]; List <Triangle> tris = map.map[brokenTd]; map.map.Remove(brokenTd); map.map[stdNoScroll].AddRange(tris); } else { state.td.scroll = null; } } else { // Find if texture without scroll exists, if it does, merge tris from it (make it scroll) List <ScrollingTextureDescription> similarTextures = map.map.Keys.Where(k => k.scroll == null).Where(k => TextureDescription.Equals(brokenTd, k)).ToList(); foreach (ScrollingTextureDescription similarStd in similarTextures) { List <Triangle> tris = map.map[similarStd]; map.map.Remove(similarStd); map.map[brokenTd].AddRange(tris); } } } vertexData = state.vertexBytes; }
private void button1_Click(object sender, EventArgs e) { GC.Collect(); foreach (DataGridViewRow row in dataGridView1.Rows) { rom.segments = (SegmentDescriptor[])row.Cells[8].Value; List <ScrollObject> scrolls = (List <ScrollObject>)row.Cells[9].Value; DisplayListRegion dlRegion = (DisplayListRegion)row.Cells[0].Value; Boolean fixingCheckBox = (Boolean)row.Cells[1].Value; DisplayList.FixConfig config = new DisplayList.FixConfig(checkBoxNerfFog.Checked, checkBoxOptimizeVertex.Checked, checkBoxTrimNops.Checked, checkBoxCombiners.Checked, checkBoxOtherMode.Checked, checkBoxNoFog.Checked); if (fixingCheckBox) { if (checkBoxNoFog.Checked) { dlRegion.isFogEnabled = false; } int maxDlLength = dlRegion.length; DisplayList.PerformRegionFix(rom, dlRegion, config); if (checkBoxOptimizeVertex.Checked) { DisplayList.PerformRegionOptimize(rom, dlRegion, config); } try { if (checkBoxGroupByTexture.Checked && !checkBoxRebuildVertices.Checked) { DisplayList.PerformVisualMapRebuild(rom, dlRegion, maxDlLength); } //DisplayList.PerformRegionOptimize(rom, dlRegion, config); } catch (Exception) { } } } if (checkBoxGroupByTexture.Checked && checkBoxRebuildVertices.Checked) { Dictionary <int, List <DataGridViewRow> > levelDatas = new Dictionary <int, List <DataGridViewRow> >(); foreach (DataGridViewRow row in dataGridView1.Rows) { Boolean fixingCheckBox = (Boolean)row.Cells[1].Value; if (!fixingCheckBox) { continue; } int level = (int)row.Cells[3].Value; if (!levelDatas.Keys.Contains(level)) { levelDatas[level] = new List <DataGridViewRow>(); } levelDatas[level].Add(row); } foreach (int level in levelDatas.Keys) { ROM romCopy = (ROM)rom.Clone(); try { List <DataGridViewRow> rows = levelDatas[level]; rom.segments = (SegmentDescriptor[])rows[0].Cells[8].Value; List <ScrollObject> scrolls = (List <ScrollObject>)rows[0].Cells[9].Value; foreach (ScrollObject scr in scrolls) { scr.Disable(rom); } ScrollFactory factory = new ScrollFactory(scrolls); SortedRegionList vertexData = new SortedRegionList(); List <KeyValuePair <DataGridViewRow, TriangleMap> > rowMaps = new List <KeyValuePair <DataGridViewRow, TriangleMap> >(); foreach (DataGridViewRow row in rows) { DisplayListRegion dlRegion = (DisplayListRegion)row.Cells[0].Value; int maxDlLength = dlRegion.length; DisplayList.GetTriangleMap(rom, dlRegion, maxDlLength, scrolls, out TriangleMap map, out SortedRegionList levelVertexData); rowMaps.Add(new KeyValuePair <DataGridViewRow, TriangleMap>(row, map)); vertexData.AddRegions(levelVertexData); } foreach (KeyValuePair <DataGridViewRow, TriangleMap> kvp in rowMaps) { DataGridViewRow row = kvp.Key; TriangleMap map = kvp.Value; DisplayListRegion dlRegion = (DisplayListRegion)row.Cells[0].Value; int maxDlLength = dlRegion.length; DisplayList.RebuildTriangleMap(rom, dlRegion, maxDlLength, map, vertexData, factory); } } catch (Exception) { rom = romCopy; } } } File.WriteAllBytes(path, rom.rom); MessageBox.Show(String.Format("ROM was patched successfully"), "f3d fix", MessageBoxButtons.OK, MessageBoxIcon.Information); }