private void splitROM_Click(object sender, EventArgs e) { GC.Collect(); dataGridView1.Rows.Clear(); //object[] row = { "aaaa", true, false, "old", "new", false }; //dataGridView1.Rows.Add(row); //return; OpenFileDialog openFileDialog1 = new OpenFileDialog(); openFileDialog1.Filter = "ROM File|*.z64"; openFileDialog1.Title = "Select a ROM"; if (openFileDialog1.ShowDialog() != System.Windows.Forms.DialogResult.OK) { return; } path = openFileDialog1.FileName; path = Path.GetFullPath(path); rom = new ROM(File.ReadAllBytes(path)); for (int i = 0; i <= 30; i++) { try { int offset = LevelInfo.GetLevelScriptEntryPoint(i); List <Region> regions = new List <Region>(); // 1st pass : find out where regions are LevelScript.PerformRegionParse(rom, regions, offset, out Dictionary <int, List <ScrollObject> > scrolls); // Fill in data from rom foreach (Region region in regions) { region.data = new byte[region.length]; rom.ReadData(region.romStart, region.length, region.data); } foreach (Region region in regions) { if (region.state == RegionState.DisplayList) { DisplayListRegion dlRegion = (DisplayListRegion)region; scrolls.TryGetValue(1, out List <ScrollObject> areaScrolls); if (areaScrolls == null) { areaScrolls = new List <ScrollObject>(); } object[] row = { dlRegion, true, region.romStart.ToString("X"), i, dlRegion.isFogEnabled, dlRegion.isEnvcolorEnabled, new CombinerCommand(dlRegion.FCcmdfirst), CombinerCommand.GetNewCombiner(dlRegion), rom.segments.Clone(), areaScrolls }; dataGridView1.Rows.Add(row); } } } catch (Exception) { } } dataGridView1.Sort(new RowComparer(SortOrder.Ascending)); }
private static void FixParse_cmdBC(ROM rom, DisplayListRegion region, RegionFixState state) { if (state.config.disableFog) { rom.Write64(0); return; } if (state.config.nerfFog) { float A = rom.Read16(4); float B = rom.Read16(6); float min = 500 * (1 - B / A); float max = 128000 / A + min; // nerf fog min += 5; A = 128000 / (max - min); B = (500 - min) * 256 / (max - min); int Aint = (int)A; int Bint = (int)B; rom.Write16(Aint, 4); rom.Write16(Bint, 6); } }
private static void FixParse_cmdF8(ROM rom, DisplayListRegion region, RegionFixState state) { if (state.config.disableFog) { rom.Write64(0); } }
private static void OptimizeParse_cmdB7(ROM rom, DisplayListRegion region, RegionOptimizeState state) { // Very failsafe approach state.lastB6Cmd = 0; Int64 cmd = rom.Read64(); // Basically NOP if ((UInt64)cmd == 0xB700000000000000) { rom.Write64(0); return; } // initial state if (state.lastB7Cmd == 0) { state.lastB7Cmd = cmd; return; } // if current cmd loads the same, remove current cmd if (cmd == state.lastB7Cmd) { rom.Write64(0); return; } }
private static void RelocationParse_cmdB9(ROM rom, RelocationTable table, DisplayListRegion region) { if (!region.DLFixesNeeded) { return; } if (!region.isFogEnabled) { return; } if ((ulong)rom.Read64(-8) != 0xBA00140200000000) { return; } if (region.layer == 1) { rom.Write64(0xB900031D00552078); } if (region.layer == 4) { rom.Write64(0xB900031D00443078); } if (region.layer == 5) { rom.Write64(0xB900031D005049D8); } }
private static void FixParse_cmdB9(ROM rom, DisplayListRegion region, RegionFixState state) { if (state.config.disableFog) { rom.Write64(0); return; } if (!state.config.fixOtherMode) { return; } if (!region.isFogEnabled) { return; } if ((ulong)rom.Read64(-8) != 0xBA00140200000000 || (ulong)rom.Read64(8) != 0xB600000000010000) { return; } UInt64 B9Cmd = OtherMode.GetB9Cmd(region.layer); if (B9Cmd != 0) { rom.Write64(B9Cmd); } }
private static void OptimizeParse_cmd04(ROM rom, DisplayListRegion region, RegionOptimizeState state) { // initial state if (state.last04Cmd == 0) { state.last04Cmd = rom.Read64(); return; } Int64 cmd = rom.Read64(); // if current 04 loads the same vertices, remove current cmd if (cmd == state.last04Cmd) { rom.Write64(0); return; } // new vertices are being loaded, update the thing state.last04Cmd = cmd; // if previous cmd is 0x04, it will be overriden by current 04 anyways if (rom.Read8(-8) == 0x04) { rom.Write64(0, -8); } }
public static void PerformVisualMapRebuild(ROM realRom, Region region, int maxDLLength) { // This is fake rom but it works anyways, just more convenient // Want to be safe with overwriting the whole display list ROM fakeRom = new ROM(region.data); VisualMapParseState state = new VisualMapParseState(); DisplayListRegion dlRegion = (DisplayListRegion)region; VisualMap map = new VisualMap(); byte curCmdIndex; do { curCmdIndex = fakeRom.Read8(); VisualMapParserCmd func = visualMapParser[curCmdIndex]; func(fakeRom, map, state); fakeRom.AddOffset(8); }while (fakeRom.offset < region.length); ROM visualMapROM = new ROM(new byte[maxDLLength]); int visualMapLength = map.MakeF3D(visualMapROM); // Now write data to real rom + trimming // bzero fakeRom.offset = 0; realRom.PushOffset(region.romStart); { do { realRom.Write64(0x0101010101010101); realRom.AddOffset(8); fakeRom.AddOffset(8); } while (fakeRom.offset < region.length); } realRom.PopOffset(); visualMapROM.offset = 0; realRom.PushOffset(region.romStart); { int start = region.romStart; do { Int64 cmd = visualMapROM.Read64(); visualMapROM.AddOffset(8); realRom.Write64((ulong)cmd); realRom.AddOffset(8); } while (visualMapROM.offset < visualMapLength); region.length = realRom.offset - start; region.data = new byte[region.length]; realRom.ReadData(region.romStart, region.length, region.data); } realRom.PopOffset(); }
public static void PerformRegionOptimize(ROM realRom, Region region, FixConfig config) { // This is fake rom but it works anyways, just more convenient // Want to be safe with overwriting the whole display list ROM fakeRom = new ROM(region.data); RegionOptimizeState state = new RegionOptimizeState(config); DisplayListRegion dlRegion = (DisplayListRegion)region; byte curCmdIndex; do { curCmdIndex = fakeRom.Read8(); OptimizeParserCmd func = optimizeParser[curCmdIndex]; func(fakeRom, dlRegion, state); fakeRom.AddOffset(8); }while (fakeRom.offset < region.length); // Now write data to real rom + trimming // bzero fakeRom.offset = 0; realRom.PushOffset(region.romStart); { do { realRom.Write64(0x0101010101010101); realRom.AddOffset(8); fakeRom.AddOffset(8); } while (fakeRom.offset < region.length); } realRom.PopOffset(); fakeRom.offset = 0; realRom.PushOffset(region.romStart); { int start = region.romStart; do { Int64 cmd = fakeRom.Read64(); fakeRom.AddOffset(8); if (config.trimNOPs && cmd == 0 && dlRegion.isUnusedTrimmingAllowed) { continue; } realRom.Write64((ulong)cmd); realRom.AddOffset(8); } while (fakeRom.offset < region.length); region.length = realRom.offset - start; region.data = new byte[region.length]; realRom.ReadData(region.romStart, region.length, region.data); } realRom.PopOffset(); }
private static void OptimizeParse_cmdFD(ROM rom, DisplayListRegion region, RegionOptimizeState state) { if (state.prevFDcmdAddr != 0) { rom.PushOffset(state.prevFDcmdAddr); rom.Write64(0); rom.PopOffset(); } state.prevFDcmdAddr = rom.offset; }
public static CombinerCommand GetNewCombiner(DisplayListRegion dlRegion) { CombinerCommand oldCmd = new CombinerCommand(dlRegion.FCcmdfirst); if (dlRegion.isEnvcolorEnabled) { return(new CombinerCommand(fog: dlRegion.isFogEnabled, alpha: true)); } if (oldCmd.opaque) { return(new CombinerCommand(fog: dlRegion.isFogEnabled, opaque: true)); } return(new CombinerCommand(fog: dlRegion.isFogEnabled, solid: true)); }
private static void OptimizeParse_cmdFB(ROM rom, DisplayListRegion region, RegionOptimizeState state) { /* * Int64 cmd = rom.Read64(); * if (state.lastFBCmd == 0) * { * state.lastFBCmd = cmd; * return; * } * * if (cmd == state.lastFBCmd) * { * rom.Write64(0); * return; * } */ }
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 static void PerformRegionFix(ROM rom, Region region, FixConfig config) { RegionFixState state = new RegionFixState(config); DisplayListRegion dlRegion = (DisplayListRegion)region; rom.PushOffset(region.romStart); byte curCmdIndex; do { curCmdIndex = rom.Read8(); FixParseCmd func = fixParser[curCmdIndex]; func(rom, dlRegion, state); rom.AddOffset(8); }while (rom.offset < region.romStart + region.length); rom.PopOffset(); rom.ReadData(region.romStart, region.length, region.data); }
private static void OptimizeParse_cmdBC(ROM rom, DisplayListRegion region, RegionOptimizeState state) { Int64 cmd = rom.Read64(); // initial state if (state.lastBCCmd == 0) { state.lastBCCmd = cmd; return; } // if current cmd loads the same, remove current cmd if (cmd == state.lastBCCmd) { rom.Write64(0); return; } }
private static void FixParse_cmdFC(ROM rom, DisplayListRegion region, RegionFixState state) { if (!state.config.fixCombiners) { return; } if (state.FCCountFixed != 0) { return; } CombinerCommand cmd = CombinerCommand.GetNewCombiner(region); UInt64 FCCmd = cmd.GetFCcmd(); if (FCCmd != 0) { state.FCCountFixed++; rom.Write64(FCCmd); } }
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 static void OptimizeParse_common(ROM rom, DisplayListRegion region, RegionOptimizeState state) { }
private static void OptimizeParse_cmdBF(ROM rom, DisplayListRegion region, RegionOptimizeState state) { state.prevF2cmdAddr = 0; state.prevFDcmdAddr = 0; }
private static void FixParse_common(ROM rom, DisplayListRegion region, RegionFixState state) { }
private static void RelocationParse_cmdFD(ROM rom, RelocationTable table, DisplayListRegion region) { table.RelocateOffset(rom, 4); }
private static void RelocationParse_common(ROM rom, RelocationTable table, DisplayListRegion region) { }
private void button2_Click(object sender, EventArgs e) { List <Region> regions = new List <Region>(); if (!Int32.TryParse(textBoxF3DPtr.Text, NumberStyles.HexNumber, CultureInfo.InvariantCulture, out int offset)) { MessageBox.Show("Custom DL", "Invalid ptr", MessageBoxButtons.OK, MessageBoxIcon.Error); return; } if (!Int32.TryParse(textBoxSegNum.Text, NumberStyles.HexNumber, CultureInfo.InvariantCulture, out int segment)) { MessageBox.Show("Custom DL", "Invalid segment", MessageBoxButtons.OK, MessageBoxIcon.Error); return; } if (!Int32.TryParse(textBoxROMAddr.Text, NumberStyles.HexNumber, CultureInfo.InvariantCulture, out int addr)) { MessageBox.Show("Custom DL", "Invalid rom addr", MessageBoxButtons.OK, MessageBoxIcon.Error); return; } rom.SetSegment(segment, new SegmentDescriptor(addr, 0x00400000)); DisplayList.FixConfig config = new DisplayList.FixConfig(checkBoxNerfFog.Checked, checkBoxOptimizeVertex.Checked, checkBoxTrimNops.Checked, checkBoxCombiners.Checked, checkBoxOtherMode.Checked, checkBoxNoFog.Checked); DisplayList.PerformRegionParse(rom, regions, offset, int.Parse(textBoxLayer.Text)); foreach (Region region in regions) { if (region.state != RegionState.DisplayList) { continue; } DisplayListRegion dlRegion = (DisplayListRegion)region; region.data = new byte[region.length]; rom.ReadData(region.romStart, region.length, region.data); int maxDLLength = dlRegion.length; DisplayList.PerformRegionFix(rom, dlRegion, config); if (checkBoxOptimizeVertex.Checked) { DisplayList.PerformRegionOptimize(rom, dlRegion, config); } if (checkBoxGroupByTexture.Checked) { if (checkBoxRebuildVertices.Checked) { DisplayList.PerformTriangleMapRebuild(rom, dlRegion, maxDLLength, new List <ScrollObject>()); } else { DisplayList.PerformVisualMapRebuild(rom, dlRegion, maxDLLength); } } DisplayList.PerformRegionOptimize(rom, dlRegion, config); } File.WriteAllBytes(path, rom.rom); rom.SetSegment(segment, null); MessageBox.Show(String.Format("Ptr was fixed successfully"), "f3d fix", MessageBoxButtons.OK, MessageBoxIcon.Information); }
private static void RelocationParse_cmdFC(ROM rom, RelocationTable table, DisplayListRegion region) { if (!region.DLFixesNeeded) { return; } UInt64 FCcmd = 0; if (region.FCCountFixed == 0) { if (region.isFogEnabled) { if ((ulong)region.B9cmdfirst == 0xB900031DC8112078) { FCcmd = 0xFC127FFFFFFFF838; } if ((ulong)region.B9cmdfirst == 0xB900031DC8113078) { FCcmd = 0xFCFFFFFFFFFCF238; } if (region.isEnvcolorEnabled) { FCcmd = 0xFC127FFFFFFFFA38; } } else { if (region.FCCount == 1) { FCcmd = 0xFC127E24FFFFF9FC; } else { if (!region.isEnvcolorEnabled) { FCcmd = 0xFC121824FF33FFFF; } else { FCcmd = 0xFC122E24FFFFFBFD; } } } } else { FCcmd = 0xFC127E24FFFFF9FC; } if (FCcmd == 0) { MessageBox.Show("Parser could not choose the right combiner! Combiners won't be fixed", "Display List", MessageBoxButtons.OK, MessageBoxIcon.Warning); } else { region.FCCountFixed++; rom.Write64(FCcmd); } }
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); }
static RegionParseState PerformRegionParseInternal(ROM rom, List <Region> regions, int offset, int layer) { RegionParseState state = new RegionParseState(); rom.PushOffset(offset); try { int cmd = 0; do { cmd = rom.Read8(); parser[cmd](rom, regions, state); rom.AddOffset(8); }while (cmd != 0xB8); Region region; SortedRegionList graphicsData = new SortedRegionList(); foreach (KeyValuePair <int, int> lightRegion in state.lightData.RegionList) { //region = new Region(lightRegion.Key, lightRegion.Value, RegionState.LightData); graphicsData.AddRegion(lightRegion.Key, lightRegion.Value); //regions.Add(region); } // kostul if (state.lightData.RegionList.Count == 0) { graphicsData.AddRegion(rom.GetROMAddress(0x0E000000), 0x10); } foreach (KeyValuePair <int, int> textureRegion in state.textureData.RegionList) { //region = new Region(textureRegion.Key, textureRegion.Value, RegionState.TextureInfo); graphicsData.AddRegion(textureRegion.Key, textureRegion.Value); //regions.Add(region); } foreach (KeyValuePair <int, int> vertexRegion in state.vertexData.RegionList) { //region = new Region(vertexRegion.Key, vertexRegion.Value, RegionState.VertexInfo); graphicsData.AddRegion(vertexRegion.Key, vertexRegion.Value); //regions.Add(region); } int count = 0; foreach (KeyValuePair <int, int> notFixedRegion in graphicsData.RegionList) { region = new DynamicRegion(notFixedRegion.Key, notFixedRegion.Value, RegionState.GraphicsData); region.number = count++; regions.Add(region); } region = new DisplayListRegion(offset, rom.offset - offset, state.isFogEnabled, state.isEnvColorEnabled, state.FCCount, state.B9cmdfirst, layer); regions.Add(region); } finally { rom.PopOffset(); } return(state); }
private void ParseGraphics(string dirname, DataBuilder segment0E, DataBuilder segmentGeoLayouts, ROM rom, out RelocationUnit unit, out RelocationUnit graphicsDataUnit, int area = -1, int model = -1) { RelocationUnit retValue = null; RelocationUnit graphicsUnit = null; segmentGeoLayouts.Backup(); segment0E.Backup(); try { DynamicRegion graphicsDataRegion = new DynamicRegion(dirname, RegionState.GraphicsData, area, model); // no relocation needed for dynamic region segment0E.AddRegion(graphicsDataRegion); segment0E.RoundOffset(); // Display lists needs to be relocated with static graphics relocation table StaticRelocationTable graphicsRelocationTable = new StaticRelocationTable(); graphicsUnit = new RelocationUnit(graphicsDataRegion, rom, isFromStatic: true); graphicsRelocationTable.AddUnit(graphicsUnit); // Geolayouts needs to be relocated with queued display lists, will be filled during relocation with graphicsRelocationTable QueueRelocationTable dispRelocationTable = new QueueRelocationTable(); RelocationUnit dispRelocationUnit = null; for (int dispNumber = 0; dispNumber < 0xFF; dispNumber++) { if (!PathComposer.IsRegionFileExists(dirname, RegionState.DisplayList, area, model, dispNumber)) { break; } DisplayListRegion dispRegion = new DisplayListRegion(dirname, area, model, dispNumber); DisplayList.PerformRegionRelocation(dispRegion, graphicsRelocationTable); segment0E.AddRegion(dispRegion); segment0E.RoundOffset(); dispRelocationUnit = new RelocationUnit(dispRegion, rom, isFromStatic: true); dispRelocationTable.AddUnit(dispRelocationUnit); } // Not even one disp relocation unit, sounds like a bug if (dispRelocationUnit == null) { throw new IOException("No display lists found!"); } // Geolayout might or might not exist for model, check if it exists and if needed, relocate it if (PathComposer.IsRegionFileExists(dirname, RegionState.GeoLayout, area, model)) { // Load geolayout and relocate it with display lists GeoLayoutRegion modelGeoLayoutRegion = new GeoLayoutRegion(dirname, area, model); GeoLayout.PerformRegionRelocation(modelGeoLayoutRegion, dispRelocationTable); segmentGeoLayouts.AddRegion(modelGeoLayoutRegion); segmentGeoLayouts.RoundOffset(); // Finalize with returning geolayout for model retValue = new RelocationUnit(modelGeoLayoutRegion, rom, isFromStatic: true); } else { // Return display list only, there should be only one, if more, it is undefinied behaviour :3 retValue = dispRelocationUnit; } } catch (Exception ex) { MessageBox.Show(String.Format("Failed to load model {0}, reason : '{1}'", model, ex.Message), "Level Combiner", MessageBoxButtons.OK, MessageBoxIcon.Error); segment0E.Restore(); segmentGeoLayouts.Restore(); } unit = retValue; graphicsDataUnit = graphicsUnit; }