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; }
private void button1_Click(object sender, EventArgs e) { GC.Collect(); OpenFileDialog openFileDialog1 = new OpenFileDialog(); openFileDialog1.Filter = "ROM File|*.z64"; openFileDialog1.Title = "Select a ROM"; if (openFileDialog1.ShowDialog() != System.Windows.Forms.DialogResult.OK) { return; } string path = openFileDialog1.FileName; path = Path.GetFullPath(path); ROM rom = new ROM(File.ReadAllBytes(path)); int offset = Convert.ToInt32(addressTextBox.Text, 16); if (offset == 0) { MessageBox.Show("Failed to parse address, input correct address", "Level Split", MessageBoxButtons.OK, MessageBoxIcon.Error); return; } LevelScript.PerformHeaderParse(rom, offset); SegmentDescriptor segmentDescriptor0E = rom.GetSegmentDescriptor(0x0E); SegmentDescriptor segmentDescriptor19 = rom.GetSegmentDescriptor(0x19); DataBuilder segment0E = new DataBuilder(segmentDescriptor0E.start, segmentDescriptor0E.length); DataBuilder segment19 = new DataBuilder(segmentDescriptor19.start, segmentDescriptor19.length); string dirname = splittedPathTextBox.Text; // Fill in area relocation table + geolayouts KeyRelocationTable areaGraphicsDescrRelocationTable = new KeyRelocationTable(); KeyRelocationTable areaGraphicsDataRelocationTable = new KeyRelocationTable(); KeyRelocationTable areaCollisionRelocationTable = new KeyRelocationTable(); for (int area = 1; area <= 8; area++) { if (!PathComposer.IsRegionFileExists(dirname, RegionState.GraphicsData, area: area)) { continue; } // Area graphics ParseGraphics(dirname, segment0E, segment19, rom, out RelocationUnit descrUnit, out RelocationUnit dataUnit, area: area); areaGraphicsDescrRelocationTable.AddUnit(area, descrUnit); areaGraphicsDataRelocationTable.AddUnit(area, dataUnit); // Area collision DynamicRegion collision = new DynamicRegion(dirname, RegionState.Collision, area: area); // No relocation needed segment0E.AddRegion(collision); segment0E.RoundOffset(); RelocationUnit collisionRelocationUnit = new RelocationUnit(collision, rom, isFromStatic: true); areaCollisionRelocationTable.AddUnit(area, collisionRelocationUnit); } // Fill in model relocation table + geolayouts KeyRelocationTable modelRelocationTable = new KeyRelocationTable(); for (int model = 0x00; model < 0xFF; model++) { if (!PathComposer.IsRegionFileExists(dirname, RegionState.GraphicsData, model: model)) { continue; } ParseGraphics(dirname, segment0E, segment19, rom, out RelocationUnit descrUnit, out RelocationUnit graphicsUnit, model: model); modelRelocationTable.AddUnit(model, descrUnit); } // As everything is prepared, we can finally start building level! LevelScriptRegion levelHeader = new LevelScriptRegion(dirname, RegionState.LevelHeader); LevelScript.FixLoadAddresses(rom, levelHeader); segment19.AddRegion(levelHeader); int levelScriptSegmentedAddressStart = rom.GetSegmentedAddress(levelHeader.romStart); LevelScriptRegion modelsLoader = new LevelScriptRegion(dirname, RegionState.ModelsLoader); modelsLoader.Relocate(modelRelocationTable); segment19.AddRegion(modelsLoader); for (sbyte area = 0; area < 8; area++) { if (!PathComposer.IsRegionFileExists(dirname, RegionState.GraphicsData, area: area)) { continue; } // Area header initializes graphics LevelScriptRegion areaHeader = new LevelScriptRegion(dirname, RegionState.AreaHeader, area: area); LevelScript.PerformRegionRelocation(areaHeader, areaGraphicsDescrRelocationTable, area); segment19.AddRegion(areaHeader); LevelScriptRegion areaData = new LevelScriptRegion(dirname, RegionState.AreaData, area: area); // No relocation needed segment19.AddRegion(areaData); if (PathComposer.IsRegionFileExists(dirname, RegionState.AreaScrolls, area: area)) { LevelScriptRegion scrollsData = new LevelScriptRegion(dirname, RegionState.AreaScrolls, area: area); LevelScript.PerformRegionRelocation(scrollsData, areaGraphicsDataRelocationTable, area); segment19.AddRegion(scrollsData); } // Area footer initializes collision LevelScriptRegion areaFooter = new LevelScriptRegion(dirname, RegionState.AreaFooter, area: area); LevelScript.PerformRegionRelocation(areaFooter, areaCollisionRelocationTable, area); segment19.AddRegion(areaFooter); } LevelScriptRegion levelFooter = new LevelScriptRegion(dirname, RegionState.LevelFooter); // no relocation needed segment19.AddRegion(levelFooter); // At this point we know that all data fit in rom // So just write all that in rom using (Stream stream = new FileStream(path, FileMode.Open)) { stream.Seek(segmentDescriptor0E.start, SeekOrigin.Begin); stream.Write(segment0E.Data, 0, segment0E.Offset); stream.Seek(segmentDescriptor19.start, SeekOrigin.Begin); stream.Write(segment19.Data, 0, segment19.Offset); // Also start of level script moved so write that thing too int endianData = IPAddress.HostToNetworkOrder(levelScriptSegmentedAddressStart); byte[] convertedData = BitConverter.GetBytes(endianData); stream.Seek(rom.levelScriptEntryOffset, SeekOrigin.Begin); stream.Write(convertedData, 0, 4); } MessageBox.Show(String.Format("ROM was build successfully from {0}", dirname), "Level Split", MessageBoxButtons.OK, MessageBoxIcon.Information); }