diff --git a/src/app/editor/overworld_editor.cc b/src/app/editor/overworld_editor.cc index 06868ecf..07bf8916 100644 --- a/src/app/editor/overworld_editor.cc +++ b/src/app/editor/overworld_editor.cc @@ -200,7 +200,7 @@ void OverworldEditor::DrawOverworldEdits() { auto canvas_size = ow_map_canvas_.GetCanvasSize(); int x = mouse_position.x / canvas_size.x; int y = mouse_position.y / canvas_size.y; - auto index = x + (y * 64); + auto index = x + (y * 8); // Determine which overworld map the user is currently editing. DetermineActiveMap(mouse_position); @@ -213,14 +213,26 @@ void OverworldEditor::DrawOverworldEdits() { QueueROMChanges(index, current_tile16_); } +void OverworldEditor::DetermineActiveMap(const ImVec2 &mouse_position) { + // Assuming each small map is 256x256 pixels (adjust as needed) + constexpr int small_map_size = 512; + + // Calculate which small map the mouse is currently over + int map_x = mouse_position.x / small_map_size; + int map_y = mouse_position.y / small_map_size; + + // Calculate the index of the map in the `maps_bmp_` vector + current_map_ = map_x + map_y * 8; +} + void OverworldEditor::RenderUpdatedMapBitmap(const ImVec2 &click_position, const Bytes &tile_data) { // Calculate the tile position relative to the current active map constexpr int tile_size = 16; // Tile size is 16x16 pixels // Calculate the tile index for x and y based on the click_position - int tile_index_x = static_cast(click_position.x) / tile_size; - int tile_index_y = static_cast(click_position.y) / tile_size; + int tile_index_x = (static_cast(click_position.x) % 512) / tile_size; + int tile_index_y = (static_cast(click_position.y) % 512) / tile_size; // Calculate the pixel start position based on tile index and tile size ImVec2 start_position; @@ -256,20 +268,22 @@ void OverworldEditor::QueueROMChanges(int index, ushort new_tile16) { }); } -void OverworldEditor::DetermineActiveMap(const ImVec2 &mouse_position) { - // Assuming each small map is 256x256 pixels (adjust as needed) - constexpr int small_map_size = 512; - - // Calculate which small map the mouse is currently over - int map_x = mouse_position.x / small_map_size; - int map_y = mouse_position.y / small_map_size; - - // Calculate the index of the map in the `maps_bmp_` vector - current_map_ = map_x + map_y * 8; -} - // ---------------------------------------------------------------------------- +void OverworldEditor::CheckForOverworldEdits() { + if (!blockset_canvas_.Points().empty()) { + // User has selected a tile they want to draw from the blockset. + int x = blockset_canvas_.Points().front().x / 32; + int y = blockset_canvas_.Points().front().y / 32; + current_tile16_ = x + (y * 8); + if (ow_map_canvas_.DrawTilePainter(tile16_individual_[current_tile16_], + 16)) { + // Update the overworld map. + DrawOverworldEdits(); + } + } +} + // Overworld Editor canvas // Allows the user to make changes to the overworld map. void OverworldEditor::DrawOverworldCanvas() { @@ -285,17 +299,7 @@ void OverworldEditor::DrawOverworldCanvas() { DrawOverworldMaps(); DrawOverworldEntrances(); // DrawOverworldSprites(); - // User has selected a tile they want to draw from the blockset. - if (!blockset_canvas_.Points().empty()) { - int x = blockset_canvas_.Points().front().x / 32; - int y = blockset_canvas_.Points().front().y / 32; - current_tile16_ = x + (y * 8); - if (ow_map_canvas_.DrawTilePainter(tile16_individual_[current_tile16_], - 16)) { - // Update the overworld map. - DrawOverworldEdits(); - } - } + CheckForOverworldEdits(); } ow_map_canvas_.DrawGrid(64.0f); ow_map_canvas_.DrawOverlay(); diff --git a/src/app/editor/overworld_editor.h b/src/app/editor/overworld_editor.h index 27c9d566..c2dea7df 100644 --- a/src/app/editor/overworld_editor.h +++ b/src/app/editor/overworld_editor.h @@ -63,6 +63,21 @@ class OverworldEditor : public Editor, public SharedROM { absl::Status Copy() { return absl::UnimplementedError("Copy"); } absl::Status Paste() { return absl::UnimplementedError("Paste"); } + void Shutdown() { + for (auto &bmp : tile16_individual_) { + bmp.Cleanup(); + } + for (auto &[i, bmp] : maps_bmp_) { + bmp.Cleanup(); + } + for (auto &[i, bmp] : graphics_bin_) { + bmp.Cleanup(); + } + for (auto &[i, bmp] : current_graphics_set_) { + bmp.Cleanup(); + } + } + private: absl::Status DrawToolset(); void DrawOverworldMapSettings(); @@ -77,6 +92,7 @@ class OverworldEditor : public Editor, public SharedROM { void QueueROMChanges(int index, ushort new_tile16); void DetermineActiveMap(const ImVec2 &mouse_position); + void CheckForOverworldEdits(); void DrawOverworldCanvas(); void DrawTile8Selector(); diff --git a/src/app/zelda3/overworld.cc b/src/app/zelda3/overworld.cc index 98bd0422..2e477678 100644 --- a/src/app/zelda3/overworld.cc +++ b/src/app/zelda3/overworld.cc @@ -286,7 +286,8 @@ absl::Status Overworld::SaveLargeMaps() { std::unordered_map checkedMap; // Always write the map parent since it should not matter - rom()->Write(overworldMapParentId + i, overworld_maps_[i].Parent()); + RETURN_IF_ERROR( + rom()->Write(overworldMapParentId + i, overworld_maps_[i].Parent())) if (checkedMap.count(overworld_maps_[i].Parent()) > 0) { continue; @@ -295,149 +296,140 @@ absl::Status Overworld::SaveLargeMaps() { // If it's large then save parent pos * // 0x200 otherwise pos * 0x200 if (overworld_maps_[i].IsLargeMap()) { - // Check 1 - rom()->Write(overworldMapSize + i, 0x20); - rom()->Write(overworldMapSize + i + 1, 0x20); - rom()->Write(overworldMapSize + i + 8, 0x20); - rom()->Write(overworldMapSize + i + 9, 0x20); + RETURN_IF_ERROR(rom()->RunTransaction( + // Check 1 + WriteAction{overworldMapSize + i, 0x20}, + WriteAction{overworldMapSize + i + 1, 0x20}, + WriteAction{overworldMapSize + i + 8, 0x20}, + WriteAction{overworldMapSize + i + 9, 0x20}, - // Check 2 - rom()->Write(overworldMapSizeHighByte + i, 0x03); - rom()->Write(overworldMapSizeHighByte + i + 1, 0x03); - rom()->Write(overworldMapSizeHighByte + i + 8, 0x03); - rom()->Write(overworldMapSizeHighByte + i + 9, 0x03); + // Check 2 + WriteAction{overworldMapSizeHighByte + i, 0x03}, + WriteAction{overworldMapSizeHighByte + i + 1, 0x03}, + WriteAction{overworldMapSizeHighByte + i + 8, 0x03}, + WriteAction{overworldMapSizeHighByte + i + 9, 0x03}, - // Check 3 - rom()->Write(overworldScreenSize + i, 0x00); - rom()->Write(overworldScreenSize + i + 64, 0x00); + // Check 3 + WriteAction{overworldScreenSize + i, 0x00}, + WriteAction{overworldScreenSize + i + 64, 0x00}, - rom()->Write(overworldScreenSize + i + 1, 0x00); - rom()->Write(overworldScreenSize + i + 1 + 64, 0x00); + WriteAction{overworldScreenSize + i + 1, 0x00}, + WriteAction{overworldScreenSize + i + 1 + 64, 0x00}, - rom()->Write(overworldScreenSize + i + 8, 0x00); - rom()->Write(overworldScreenSize + i + 8 + 64, 0x00); + WriteAction{overworldScreenSize + i + 8, 0x00}, + WriteAction{overworldScreenSize + i + 8 + 64, 0x00}, - rom()->Write(overworldScreenSize + i + 9, 0x00); - rom()->Write(overworldScreenSize + i + 9 + 64, 0x00); + WriteAction{overworldScreenSize + i + 9, 0x00}, + WriteAction{overworldScreenSize + i + 9 + 64, 0x00}, - // Check 4 - rom()->Write(OverworldScreenSizeForLoading + i, 0x04); - rom()->Write(OverworldScreenSizeForLoading + i + 64, 0x04); - rom()->Write(OverworldScreenSizeForLoading + i + 128, 0x04); + // Check 4 + WriteAction{OverworldScreenSizeForLoading + i, 0x04}, + WriteAction{OverworldScreenSizeForLoading + i + 64, 0x04}, + WriteAction{OverworldScreenSizeForLoading + i + 128, 0x04}, - rom()->Write(OverworldScreenSizeForLoading + i + 1, 0x04); - rom()->Write(OverworldScreenSizeForLoading + i + 1 + 64, 0x04); - rom()->Write(OverworldScreenSizeForLoading + i + 1 + 128, 0x04); + WriteAction{OverworldScreenSizeForLoading + i + 1, 0x04}, + WriteAction{OverworldScreenSizeForLoading + i + 1 + 64, 0x04}, + WriteAction{OverworldScreenSizeForLoading + i + 1 + 128, 0x04}, - rom()->Write(OverworldScreenSizeForLoading + i + 8, 0x04); - rom()->Write(OverworldScreenSizeForLoading + i + 8 + 64, 0x04); - rom()->Write(OverworldScreenSizeForLoading + i + 8 + 128, 0x04); + WriteAction{OverworldScreenSizeForLoading + i + 8, 0x04}, + WriteAction{OverworldScreenSizeForLoading + i + 8 + 64, 0x04}, + WriteAction{OverworldScreenSizeForLoading + i + 8 + 128, 0x04}, - rom()->Write(OverworldScreenSizeForLoading + i + 9, 0x04); - rom()->Write(OverworldScreenSizeForLoading + i + 9 + 64, 0x04); - rom()->Write(OverworldScreenSizeForLoading + i + 9 + 128, 0x04); + WriteAction{OverworldScreenSizeForLoading + i + 9, 0x04}, + WriteAction{OverworldScreenSizeForLoading + i + 9 + 64, 0x04}, + WriteAction{OverworldScreenSizeForLoading + i + 9 + 128, 0x04}, - // Check 5 and 6 - rom()->WriteShort( - transition_target_north + (i * 2) + 2, - (short)((parentyPos * 0x200) - - 0xE0)); // (short) is placed to reduce the int to 2 bytes. - rom()->WriteShort(transition_target_west + (i * 2) + 2, - (short)((parentxPos * 0x200) - 0x100)); + // Check 5 and 6 + WriteAction{transition_target_north + (i * 2) + 2, + (short)((parentyPos * 0x200) - + 0xE0)}, // (short) is placed to reduce the int to + // 2 bytes. + WriteAction{transition_target_west + (i * 2) + 2, + (short)((parentxPos * 0x200) - 0x100)}, - rom()->WriteShort( - transition_target_north + (i * 2) + 16, - (short)((parentyPos * 0x200) - - 0xE0)); // (short) is placed to reduce the int to 2 bytes. - rom()->WriteShort(transition_target_west + (i * 2) + 16, - (short)((parentxPos * 0x200) - 0x100)); + // (short) is placed to reduce the int to 2 bytes. + WriteAction{transition_target_north + (i * 2) + 16, + (short)((parentyPos * 0x200) - 0xE0)}, + WriteAction{transition_target_west + (i * 2) + 16, + (short)((parentxPos * 0x200) - 0x100)}, - rom()->WriteShort( - transition_target_north + (i * 2) + 18, - (short)((parentyPos * 0x200) - - 0xE0)); // (short) is placed to reduce the int to 2 bytes. - rom()->WriteShort(transition_target_west + (i * 2) + 18, - (short)((parentxPos * 0x200) - 0x100)); + // (short) is placed to reduce the int to 2 bytes. + WriteAction{transition_target_north + (i * 2) + 18, + (short)((parentyPos * 0x200) - 0xE0)}, + WriteAction{transition_target_west + (i * 2) + 18, + (short)((parentxPos * 0x200) - 0x100)}, - // Check 7 and 8 - rom()->WriteShort(overworldTransitionPositionX + (i * 2), - (parentxPos * 0x200)); - rom()->WriteShort(overworldTransitionPositionY + (i * 2), - (parentyPos * 0x200)); + // Check 7 and 8 + WriteAction{overworldTransitionPositionX + (i * 2), + (parentxPos * 0x200)}, + WriteAction{overworldTransitionPositionY + (i * 2), + (parentyPos * 0x200)}, - rom()->WriteShort(overworldTransitionPositionX + (i * 2) + 2, - (parentxPos * 0x200)); - rom()->WriteShort(overworldTransitionPositionY + (i * 2) + 2, - (parentyPos * 0x200)); + WriteAction{overworldTransitionPositionX + (i * 2) + 2, + (parentxPos * 0x200)}, + WriteAction{overworldTransitionPositionY + (i * 2) + 2, + (parentyPos * 0x200)}, - rom()->WriteShort(overworldTransitionPositionX + (i * 2) + 16, - (parentxPos * 0x200)); - rom()->WriteShort(overworldTransitionPositionY + (i * 2) + 16, - (parentyPos * 0x200)); + WriteAction{overworldTransitionPositionX + (i * 2) + 16, + (parentxPos * 0x200)}, + WriteAction{overworldTransitionPositionY + (i * 2) + 16, + (parentyPos * 0x200)}, - rom()->WriteShort(overworldTransitionPositionX + (i * 2) + 18, - (parentxPos * 0x200)); - rom()->WriteShort(overworldTransitionPositionY + (i * 2) + 18, - (parentyPos * 0x200)); + WriteAction{overworldTransitionPositionX + (i * 2) + 18, + (parentxPos * 0x200)}, + WriteAction{overworldTransitionPositionY + (i * 2) + 18, + (parentyPos * 0x200)}, - // Check 9 - rom()->WriteShort(OverworldScreenTileMapChangeByScreen + (i * 2), - 0x0060); // Always 0x0060 - rom()->WriteShort(OverworldScreenTileMapChangeByScreen + (i * 2) + 2, - 0x0060); // Always 0x0060 + // Check 9 + // Always 0x0060 + WriteAction{OverworldScreenTileMapChangeByScreen + (i * 2), 0x0060}, + // Always 0x0060 + WriteAction{OverworldScreenTileMapChangeByScreen + (i * 2) + 2, + 0x0060})) + uint16_t lowerSubmaps; // If parentX == 0 then lower submaps == 0x0060 too if (parentxPos == 0) { - rom()->WriteShort(OverworldScreenTileMapChangeByScreen + (i * 2) + 16, - 0x0060); - rom()->WriteShort(OverworldScreenTileMapChangeByScreen + (i * 2) + 18, - 0x0060); + lowerSubmaps = 0x0060; } else { // Otherwise lower submaps == 0x1060 - rom()->WriteShort(OverworldScreenTileMapChangeByScreen + (i * 2) + 16, - 0x1060); - rom()->WriteShort(OverworldScreenTileMapChangeByScreen + (i * 2) + 18, - 0x1060); + lowerSubmaps = 0x1060; } - rom()->WriteShort(OverworldScreenTileMapChangeByScreen + (i * 2) + 128, - 0x0080); // Always 0x0080 - rom()->WriteShort( - OverworldScreenTileMapChangeByScreen + (i * 2) + 2 + 128, - 0x0080); // Always 0x0080 - // Lower are always 8010 - rom()->WriteShort( - OverworldScreenTileMapChangeByScreen + (i * 2) + 16 + 128, - 0x1080); // Always 0x1080 - rom()->WriteShort( - OverworldScreenTileMapChangeByScreen + (i * 2) + 18 + 128, - 0x1080); // Always 0x1080 + RETURN_IF_ERROR(rom()->RunTransaction( + WriteAction{OverworldScreenTileMapChangeByScreen + (i * 2) + 16, + uint16_t(lowerSubmaps)}, + WriteAction{OverworldScreenTileMapChangeByScreen + (i * 2) + 18, + uint16_t(lowerSubmaps)}, + WriteAction{OverworldScreenTileMapChangeByScreen + (i * 2) + 128, + uint16_t(0x0080)}, // Always 0x0080 + WriteAction{OverworldScreenTileMapChangeByScreen + (i * 2) + 2 + 128, + uint16_t(0x0080)}, // Always 0x0080 + // Lower are always 8010 + WriteAction{OverworldScreenTileMapChangeByScreen + (i * 2) + 16 + 128, + uint16_t(0x1080)}, // Always 0x1080 + WriteAction{OverworldScreenTileMapChangeByScreen + (i * 2) + 18 + 128, + uint16_t(0x1080)}, // Always 0x1080 - rom()->WriteShort(OverworldScreenTileMapChangeByScreen + (i * 2) + 256, - 0x1800); // Always 0x1800 - rom()->WriteShort( - OverworldScreenTileMapChangeByScreen + (i * 2) + 16 + 256, - 0x1800); // Always 0x1800 - // Right side is always 1840 - rom()->WriteShort( - OverworldScreenTileMapChangeByScreen + (i * 2) + 2 + 256, - 0x1840); // Always 0x1840 - rom()->WriteShort( - OverworldScreenTileMapChangeByScreen + (i * 2) + 18 + 256, - 0x1840); // Always 0x1840 + WriteAction{OverworldScreenTileMapChangeByScreen + (i * 2) + 256, + uint16_t(0x1800)}, // Always 0x1800 + WriteAction{OverworldScreenTileMapChangeByScreen + (i * 2) + 16 + 256, + uint16_t(0x1800)}, // Always 0x1800 + // Right side is always 1840 + WriteAction{OverworldScreenTileMapChangeByScreen + (i * 2) + 2 + 256, + uint16_t(0x1840)}, // Always 0x1840 + WriteAction{OverworldScreenTileMapChangeByScreen + (i * 2) + 18 + 256, + uint16_t(0x1840)}, // Always 0x1840 - rom()->WriteShort(OverworldScreenTileMapChangeByScreen + (i * 2) + 384, - 0x2000); // Always 0x2000 - rom()->WriteShort( - OverworldScreenTileMapChangeByScreen + (i * 2) + 16 + 384, - 0x2000); // Always 0x2000 - // Right side is always 0x2040 - rom()->WriteShort( - OverworldScreenTileMapChangeByScreen + (i * 2) + 2 + 384, - 0x2040); // Always 0x2000 - rom()->WriteShort( - OverworldScreenTileMapChangeByScreen + (i * 2) + 18 + 384, - 0x2040); // Always 0x2000 + WriteAction{OverworldScreenTileMapChangeByScreen + (i * 2) + 384, + uint16_t(0x2000)}, // Always 0x2000 + WriteAction{OverworldScreenTileMapChangeByScreen + (i * 2) + 16 + 384, + uint16_t(0x2000)}, // Always 0x2000 + // Right side is always 0x2040 + WriteAction{OverworldScreenTileMapChangeByScreen + (i * 2) + 2 + 384, + uint16_t(0x2040)}, // Always 0x2000 + WriteAction{OverworldScreenTileMapChangeByScreen + (i * 2) + 18 + 384, + uint16_t(0x2040)})) // Always 0x2000 checkedMap.emplace(i, 1); checkedMap.emplace((i + 1), 1); @@ -445,31 +437,31 @@ absl::Status Overworld::SaveLargeMaps() { checkedMap.emplace((i + 9), 1); } else { - rom()->Write(overworldMapSize + i, 0x00); - rom()->Write(overworldMapSizeHighByte + i, 0x01); + RETURN_IF_ERROR(rom()->RunTransaction( + WriteAction{overworldMapSize + i, 0x00}, + WriteAction{overworldMapSizeHighByte + i, 0x01}, + WriteAction{overworldScreenSize + i, 0x01}, + WriteAction{overworldScreenSize + i + 64, 0x01}, + WriteAction{OverworldScreenSizeForLoading + i, 0x02}, + WriteAction{OverworldScreenSizeForLoading + i + 64, 0x02}, + WriteAction{OverworldScreenSizeForLoading + i + 128, 0x02}, - rom()->Write(overworldScreenSize + i, 0x01); - rom()->Write(overworldScreenSize + i + 64, 0x01); - - rom()->Write(OverworldScreenSizeForLoading + i, 0x02); - rom()->Write(OverworldScreenSizeForLoading + i + 64, 0x02); - rom()->Write(OverworldScreenSizeForLoading + i + 128, 0x02); - - rom()->WriteShort(OverworldScreenTileMapChangeByScreen + (i * 2), 0x0060); - rom()->WriteShort(OverworldScreenTileMapChangeByScreen + (i * 2) + 128, - 0x0040); - rom()->WriteShort(OverworldScreenTileMapChangeByScreen + (i * 2) + 256, - 0x1800); - rom()->WriteShort(OverworldScreenTileMapChangeByScreen + (i * 2) + 384, - 0x1000); - - rom()->WriteShort(transition_target_north + (i * 2), - (short)((yPos * 0x200) - 0xE0)); - rom()->WriteShort(transition_target_west + (i * 2), - (short)((xPos * 0x200) - 0x100)); - - rom()->WriteShort(overworldTransitionPositionX + (i * 2), (xPos * 0x200)); - rom()->WriteShort(overworldTransitionPositionY + (i * 2), (yPos * 0x200)); + WriteAction{OverworldScreenTileMapChangeByScreen + (i * 2), + uint16_t(0x0060)}, + WriteAction{OverworldScreenTileMapChangeByScreen + (i * 2) + 128, + uint16_t(0x0040)}, + WriteAction{OverworldScreenTileMapChangeByScreen + (i * 2) + 256, + uint16_t(0x1800)}, + WriteAction{OverworldScreenTileMapChangeByScreen + (i * 2) + 384, + (0x1000)}, + WriteAction{transition_target_north + (i * 2), + uint16_t((yPos * 0x200) - 0xE0)}, + WriteAction{transition_target_west + (i * 2), + uint16_t((xPos * 0x200) - 0x100)}, + WriteAction{overworldTransitionPositionX + (i * 2), + uint16_t(xPos * 0x200)}, + WriteAction{overworldTransitionPositionY + (i * 2), + uint16_t(yPos * 0x200)})) checkedMap.emplace(i, 1); } @@ -553,60 +545,76 @@ absl::Status Overworld::SaveMap16Tiles() { int tpos = kMap16Tiles; // 3760 for (int i = 0; i < NumberOfMap16; i += 1) { - RETURN_IF_ERROR(rom()->RunTransaction( - WriteAction{tpos, uint16_t(TileInfoToShort(tiles16[i].tile0_))}, - WriteAction{tpos += 2, uint16_t(TileInfoToShort(tiles16[i].tile1_))}, - WriteAction{tpos += 2, uint16_t(TileInfoToShort(tiles16[i].tile2_))}, - WriteAction{tpos += 2, uint16_t(TileInfoToShort(tiles16[i].tile3_))})); - - // rom()->WriteShort(tpos, TileInfoToShort(tiles16[i].tile0_)); - // tpos += 2; - // rom()->WriteShort(tpos, TileInfoToShort(tiles16[i].tile1_)); - // tpos += 2; - // rom()->WriteShort(tpos, TileInfoToShort(tiles16[i].tile2_)); - // tpos += 2; - // rom()->WriteShort(tpos, TileInfoToShort(tiles16[i].tile3_)); - // tpos += 2; + RETURN_IF_ERROR(rom()->WriteShort(tpos, TileInfoToShort(tiles16[i].tile0_))) + tpos += 2; + RETURN_IF_ERROR(rom()->WriteShort(tpos, TileInfoToShort(tiles16[i].tile1_))) + tpos += 2; + RETURN_IF_ERROR(rom()->WriteShort(tpos, TileInfoToShort(tiles16[i].tile2_))) + tpos += 2; + RETURN_IF_ERROR(rom()->WriteShort(tpos, TileInfoToShort(tiles16[i].tile3_))) + tpos += 2; } + return absl::OkStatus(); } // ---------------------------------------------------------------------------- absl::Status Overworld::SaveMap32Tiles() { - int index = 0; - int c = tiles32_unique_.size(); + constexpr int kMaxUniqueTiles = 0x4540; + constexpr int kTilesPer32x32Tile = 6; + constexpr int kQuadrantsPer32x32Tile = 4; - for (int i = 0; i < c; i += 6) { - if (index >= 0x4540) { - return absl::AbortedError("Too many unique tile32 definitions."); - } + if (tiles32_unique_.size() % kTilesPer32x32Tile != 0) { + return absl::InvalidArgumentError("Invalid number of unique tiles."); + } - // Helper lambda to avoid code repetition - auto writeTilesToRom = [&](int base_addr, auto get_tile) { - for (int j = 0; j < 4; ++j) { - rom()->Write(base_addr + i + j, - get_tile(tiles32_unique_[index + j]) & 0xFF); + int unique_tile_index = 0; + int num_unique_tiles = tiles32_unique_.size(); + int num_32x32_tiles = num_unique_tiles / kTilesPer32x32Tile; + + if (num_32x32_tiles > kMaxUniqueTiles / kQuadrantsPer32x32Tile) { + return absl::AbortedError("Too many unique tile32 definitions."); + } + + for (int i = 0; i < num_32x32_tiles; ++i) { + int base_addr = + rom()->GetVersionConstants().kMap32TileTL + i * kQuadrantsPer32x32Tile; + + auto write_quadrant_to_rom = [&](int quadrant, + auto get_tile) -> absl::Status { + for (int j = 0; j < kQuadrantsPer32x32Tile; ++j) { + int tile_index = unique_tile_index + j; + const gfx::Tile32 &tile = tiles32_unique_[tile_index]; + RETURN_IF_ERROR( + rom()->Write(base_addr + quadrant + j, get_tile(tile) & 0xFF)); } - rom()->Write(base_addr + i + 4, - ((get_tile(tiles32_unique_[index]) >> 4) & 0xF0) | - ((get_tile(tiles32_unique_[index + 1]) >> 8) & 0x0F)); - rom()->Write(base_addr + i + 5, - ((get_tile(tiles32_unique_[index + 2]) >> 4) & 0xF0) | - ((get_tile(tiles32_unique_[index + 3]) >> 8) & 0x0F)); + + int tile0 = get_tile(tiles32_unique_[unique_tile_index]); + int tile1 = get_tile(tiles32_unique_[unique_tile_index + 1]); + int tile2 = get_tile(tiles32_unique_[unique_tile_index + 2]); + int tile3 = get_tile(tiles32_unique_[unique_tile_index + 3]); + + RETURN_IF_ERROR( + rom()->Write(base_addr + quadrant + 4, + ((tile0 >> 4) & 0xF0) | ((tile1 >> 8) & 0x0F))); + RETURN_IF_ERROR( + rom()->Write(base_addr + quadrant + 5, + ((tile2 >> 4) & 0xF0) | ((tile3 >> 8) & 0x0F))); + return absl::OkStatus(); }; - writeTilesToRom(rom()->GetVersionConstants().kMap32TileTL, - [](const gfx::Tile32 &t) { return t.tile0_; }); - writeTilesToRom(rom()->GetVersionConstants().kMap32TileTR, - [](const gfx::Tile32 &t) { return t.tile1_; }); - writeTilesToRom(rom()->GetVersionConstants().kMap32TileBL, - [](const gfx::Tile32 &t) { return t.tile2_; }); - writeTilesToRom(rom()->GetVersionConstants().kMap32TileBR, - [](const gfx::Tile32 &t) { return t.tile3_; }); + RETURN_IF_ERROR(write_quadrant_to_rom( + 0, [](const gfx::Tile32 &t) { return t.tile0_; })); + RETURN_IF_ERROR(write_quadrant_to_rom( + 1, [](const gfx::Tile32 &t) { return t.tile1_; })); + RETURN_IF_ERROR(write_quadrant_to_rom( + 2, [](const gfx::Tile32 &t) { return t.tile2_; })); + RETURN_IF_ERROR(write_quadrant_to_rom( + 3, [](const gfx::Tile32 &t) { return t.tile3_; })); - index += 4; - c += 2; + unique_tile_index += kTilesPer32x32Tile; } + return absl::OkStatus(); }