From d5e06e943fb3ab8cf97a963deae3769f3cf98599 Mon Sep 17 00:00:00 2001 From: scawful Date: Mon, 22 Dec 2025 14:55:59 -0500 Subject: [PATCH] fix(dungeon): align object drawing and tests --- src/zelda3/dungeon/dungeon_object_editor.cc | 30 +++--- src/zelda3/dungeon/object_drawer.cc | 31 +++--- src/zelda3/dungeon/object_drawer.h | 7 ++ src/zelda3/dungeon/object_parser.cc | 6 +- .../dungeon_editor_system_integration_test.cc | 30 ++++-- .../dungeon_graphics_transparency_test.cc | 67 ++++++++----- .../dungeon_object_rom_validation_test.cc | 4 +- .../zelda3/dungeon_palette_test.cc | 59 +++++------ test/integration/zelda3/dungeon_room_test.cc | 9 +- .../zelda3/room_integration_test.cc | 23 +++-- test/unit/zelda3/custom_object_test.cc | 6 +- .../dungeon/draw_routine_mapping_test.cc | 2 +- test/unit/zelda3/dungeon/dungeon_save_test.cc | 29 +++--- .../zelda3/dungeon/object_dimensions_test.cc | 24 ++--- .../object_drawing_comprehensive_test.cc | 99 +++++++++++-------- .../zelda3/dungeon/room_layer_manager_test.cc | 14 +-- test/unit/zelda3/object_parser_test.cc | 4 +- tools/test_helpers/dungeon_test_harness.cc | 1 + 18 files changed, 256 insertions(+), 189 deletions(-) diff --git a/src/zelda3/dungeon/dungeon_object_editor.cc b/src/zelda3/dungeon/dungeon_object_editor.cc index 54d84dbd..30f93ac7 100644 --- a/src/zelda3/dungeon/dungeon_object_editor.cc +++ b/src/zelda3/dungeon/dungeon_object_editor.cc @@ -1148,21 +1148,16 @@ std::optional DungeonObjectEditor::FindObjectAt(int room_x, bool DungeonObjectEditor::IsObjectAtPosition(const RoomObject& object, int x, int y) { - // Convert object position to pixel coordinates - int obj_x = object.x_ * 16; - int obj_y = object.y_ * 16; + // Coordinates are in room tiles. + int obj_x = object.x_; + int obj_y = object.y_; - // Check if point is within object bounds - // This is a simplified implementation - in practice, you'd check - // against the actual tile data - - int obj_width = 16; // Default object width - int obj_height = 16; // Default object height - - // Adjust size based on object size value + // Simplified bounds: default to 1x1 tile, grow to 2x2 for large objects. + int obj_width = 1; + int obj_height = 1; if (object.size_ > 0x80) { - obj_width *= 2; - obj_height *= 2; + obj_width = 2; + obj_height = 2; } return (x >= obj_x && x < obj_x + obj_width && y >= obj_y && @@ -1225,7 +1220,14 @@ int DungeonObjectEditor::SnapToGrid(int coordinate) { return coordinate; } - return (coordinate / config_.grid_size) * config_.grid_size; + int grid_size = config_.grid_size; + if (grid_size <= 0) { + return coordinate; + } + + // Coordinates are in room tiles; map pixel grid size to tile steps. + int tile_step = std::max(1, grid_size / 16); + return (coordinate / tile_step) * tile_step; } void DungeonObjectEditor::UpdatePreviewObject() { diff --git a/src/zelda3/dungeon/object_drawer.cc b/src/zelda3/dungeon/object_drawer.cc index b1afe8e3..cae45956 100644 --- a/src/zelda3/dungeon/object_drawer.cc +++ b/src/zelda3/dungeon/object_drawer.cc @@ -6,6 +6,7 @@ #include "absl/strings/str_format.h" #include "app/gfx/types/snes_tile.h" #include "rom/rom.h" +#include "core/features.h" #include "rom/snes.h" #include "util/log.h" #include "zelda3/dungeon/draw_routines/draw_routine_registry.h" @@ -340,10 +341,15 @@ void ObjectDrawer::InitializeDrawRoutines() { object_to_routine_map_[0x30] = 24; // RoomDraw_RightwardsBottomCorners1x2_1to16_plus13 // Custom Objects (0x31-0x32) - Oracle of Secrets minecart tracks and furniture - // These use external binary files instead of ROM tile data. - // Requires CustomObjectManager initialization and enable_custom_objects feature flag. - object_to_routine_map_[0x31] = DrawRoutineIds::kCustomObject; // Custom tracks - object_to_routine_map_[0x32] = DrawRoutineIds::kCustomObject; // Custom furniture + // USDASM marks these as RoomDraw_Nothing; only map to custom routines when enabled. + if (core::FeatureFlags::get().kEnableCustomObjects) { + // These use external binary files instead of ROM tile data. + object_to_routine_map_[0x31] = DrawRoutineIds::kCustomObject; // Custom tracks + object_to_routine_map_[0x32] = DrawRoutineIds::kCustomObject; // Custom furniture + } else { + object_to_routine_map_[0x31] = DrawRoutineIds::kNothing; + object_to_routine_map_[0x32] = DrawRoutineIds::kNothing; + } object_to_routine_map_[0x33] = 16; // 4x4 Block object_to_routine_map_[0x34] = 25; // Solid 1x1 object_to_routine_map_[0x35] = 26; // Door Switcher @@ -633,19 +639,10 @@ void ObjectDrawer::InitializeDrawRoutines() { object_to_routine_map_[id] = 39; // Chest draw routine } - // Subtype 2 Object Mappings (0x100-0x1FF) - // LAYOUT CORNERS: 0x100-0x103 are the concave corners used in room layouts - // These must use DrawCorner4x4 (routine 19) NOT DrawRightwards4x4 (routine 16) - // ASM Reference: bank_01.asm RoomDraw_4x4Corner routine - // 0x100 = Corner (top, concave) ▛ (upper-left) - // 0x101 = Corner (top, concave) ▙ (lower-left) - // 0x102 = Corner (top, concave) ▜ (upper-right) - // 0x103 = Corner (top, concave) ▟ (lower-right) - for (int id = 0x100; id <= 0x103; id++) { - object_to_routine_map_[id] = 19; // DrawCorner4x4 for layout corners - } - // 0x104-0x107: Other 4x4 patterns (non-corner) - for (int id = 0x104; id <= 0x107; id++) { + // Subtype 2 Object Mappings (0x100-0x13F) + // ASM Reference: bank_01.asm .type1_subtype_2_routine ($018470) + // 0x100-0x107: RoomDraw_4x4 + for (int id = 0x100; id <= 0x107; id++) { object_to_routine_map_[id] = 16; // Rightwards 4x4 } for (int id = 0x108; id <= 0x10F; id++) { diff --git a/src/zelda3/dungeon/object_drawer.h b/src/zelda3/dungeon/object_drawer.h index d86df29d..6674f311 100644 --- a/src/zelda3/dungeon/object_drawer.h +++ b/src/zelda3/dungeon/object_drawer.h @@ -112,6 +112,13 @@ class ObjectDrawer { */ int GetDrawRoutineId(int16_t object_id) const; + /** + * @brief Get the total number of registered draw routines + */ + int GetDrawRoutineCount() const { + return static_cast(draw_routines_.size()); + } + /** * @brief Initialize draw routine registry * Must be called before drawing objects diff --git a/src/zelda3/dungeon/object_parser.cc b/src/zelda3/dungeon/object_parser.cc index 376b22e7..3ba866c5 100644 --- a/src/zelda3/dungeon/object_parser.cc +++ b/src/zelda3/dungeon/object_parser.cc @@ -118,7 +118,7 @@ absl::StatusOr ObjectParser::GetObjectSubtype( info.subtype_ptr = kRoomObjectSubtype2 + (index * 2); // Routine table starts 128 bytes (64 entries * 2 bytes) after data table info.routine_ptr = kRoomObjectSubtype2 + 0x80 + (index * 2); - info.max_tile_count = 8; + info.max_tile_count = GetSubtype2TileCount(object_id); break; } case 3: { @@ -127,7 +127,7 @@ absl::StatusOr ObjectParser::GetObjectSubtype( int index = (object_id - 0xF80) & 0x7F; info.subtype_ptr = kRoomObjectSubtype3 + (index * 2); info.routine_ptr = kRoomObjectSubtype3 + 0x100 + (index * 2); - info.max_tile_count = 8; + info.max_tile_count = GetSubtype3TileCount(object_id); break; } default: @@ -653,4 +653,4 @@ ObjectDrawInfo ObjectParser::GetObjectDrawInfo(int16_t object_id) const { } } // namespace zelda3 -} // namespace yaze \ No newline at end of file +} // namespace yaze diff --git a/test/integration/zelda3/dungeon_editor_system_integration_test.cc b/test/integration/zelda3/dungeon_editor_system_integration_test.cc index 6d7f356d..4f285ab1 100644 --- a/test/integration/zelda3/dungeon_editor_system_integration_test.cc +++ b/test/integration/zelda3/dungeon_editor_system_integration_test.cc @@ -3,9 +3,11 @@ #include #include #include +#include #include #include "rom/rom.h" +#include "test/test_utils.h" #include "zelda3/dungeon/dungeon_editor_system.h" #include "zelda3/dungeon/dungeon_object_editor.h" #include "zelda3/dungeon/room.h" @@ -21,12 +23,22 @@ class DungeonEditorSystemIntegrationTest : public ::testing::Test { GTEST_SKIP() << "Dungeon editor tests require ROM file (unavailable on Linux CI)"; #endif - // Use the real ROM from build directory - rom_path_ = "build/bin/zelda3.sfc"; + yaze::test::TestRomManager::SkipIfRomMissing( + yaze::test::RomRole::kVanilla, + "DungeonEditorSystemIntegrationTest"); + rom_path_ = + yaze::test::TestRomManager::GetRomPath(yaze::test::RomRole::kVanilla); + ASSERT_FALSE(rom_path_.empty()) + << "ROM path not set for vanilla role. " + << yaze::test::TestRomManager::GetRomRoleHint( + yaze::test::RomRole::kVanilla); // Load ROM rom_ = std::make_unique(); - ASSERT_TRUE(rom_->LoadFromFile(rom_path_).ok()); + auto load_status = rom_->LoadFromFile(rom_path_); + ASSERT_TRUE(load_status.ok()) + << "Failed to load ROM from " << rom_path_ << ": " + << load_status.message(); // Initialize dungeon editor system dungeon_editor_system_ = std::make_unique(rom_.get()); @@ -99,8 +111,8 @@ TEST_F(DungeonEditorSystemIntegrationTest, ObjectEditorIntegration) { ASSERT_TRUE(dungeon_editor_system_->SetCurrentRoom(0x0000).ok()); // Test object insertion - ASSERT_TRUE(object_editor->InsertObject(5, 5, 0x10, 0x12, 0).ok()); - ASSERT_TRUE(object_editor->InsertObject(10, 10, 0x20, 0x22, 1).ok()); + ASSERT_TRUE(object_editor->InsertObject(5, 5, 0x10, 0x0F, 0).ok()); + ASSERT_TRUE(object_editor->InsertObject(10, 10, 0x20, 0x0F, 1).ok()); // Verify objects were added EXPECT_EQ(object_editor->GetObjectCount(), 2); @@ -125,8 +137,8 @@ TEST_F(DungeonEditorSystemIntegrationTest, UndoRedoFunctionality) { ASSERT_NE(object_editor, nullptr); // Add some objects - ASSERT_TRUE(object_editor->InsertObject(5, 5, 0x10, 0x12, 0).ok()); - ASSERT_TRUE(object_editor->InsertObject(10, 10, 0x20, 0x22, 1).ok()); + ASSERT_TRUE(object_editor->InsertObject(5, 5, 0x10, 0x0F, 0).ok()); + ASSERT_TRUE(object_editor->InsertObject(10, 10, 0x20, 0x0F, 1).ok()); // Verify objects were added EXPECT_EQ(object_editor->GetObjectCount(), 2); @@ -158,8 +170,8 @@ TEST_F(DungeonEditorSystemIntegrationTest, SaveLoadFunctionality) { auto object_editor = dungeon_editor_system_->GetObjectEditor(); ASSERT_NE(object_editor, nullptr); - ASSERT_TRUE(object_editor->InsertObject(5, 5, 0x10, 0x12, 0).ok()); - ASSERT_TRUE(object_editor->InsertObject(10, 10, 0x20, 0x22, 1).ok()); + ASSERT_TRUE(object_editor->InsertObject(5, 5, 0x10, 0x0F, 0).ok()); + ASSERT_TRUE(object_editor->InsertObject(10, 10, 0x20, 0x0F, 1).ok()); // Save room ASSERT_TRUE(dungeon_editor_system_->SaveRoom(0x0000).ok()); diff --git a/test/integration/zelda3/dungeon_graphics_transparency_test.cc b/test/integration/zelda3/dungeon_graphics_transparency_test.cc index a3924efb..7e6ab8e4 100644 --- a/test/integration/zelda3/dungeon_graphics_transparency_test.cc +++ b/test/integration/zelda3/dungeon_graphics_transparency_test.cc @@ -5,8 +5,10 @@ #include #include +#include #include "rom/rom.h" +#include "test/test_utils.h" #include "zelda3/dungeon/object_drawer.h" #include "zelda3/dungeon/room.h" #include "zelda3/game_data.h" @@ -20,11 +22,11 @@ class DungeonGraphicsTransparencyTest : public ::testing::Test { void SetUp() override { rom_ = std::make_unique(); - const char* rom_path = std::getenv("YAZE_TEST_ROM_PATH"); - if (!rom_path) { - rom_path = "zelda3.sfc"; - } - + yaze::test::TestRomManager::SkipIfRomMissing( + yaze::test::RomRole::kVanilla, + "DungeonGraphicsTransparencyTest"); + const std::string rom_path = + yaze::test::TestRomManager::GetRomPath(yaze::test::RomRole::kVanilla); auto status = rom_->LoadFromFile(rom_path); if (!status.ok()) { GTEST_SKIP() << "ROM file not available: " << status.message(); @@ -78,7 +80,8 @@ TEST_F(DungeonGraphicsTransparencyTest, GraphicsBufferHasTransparentPixels) { // Test 2: Verify room graphics buffer after CopyRoomGraphicsToBuffer TEST_F(DungeonGraphicsTransparencyTest, RoomGraphicsBufferHasTransparentPixels) { // Create room 0 (Ganon's room - known to have walls) - Room room(0x00, rom_.get()); + Room room = LoadRoomHeaderFromRom(rom_.get(), 0x00); + room.SetGameData(&game_data_); room.LoadRoomGraphics(0xFF); room.CopyRoomGraphicsToBuffer(); @@ -86,15 +89,22 @@ TEST_F(DungeonGraphicsTransparencyTest, RoomGraphicsBufferHasTransparentPixels) const auto& gfx16 = room.get_gfx_buffer(); ASSERT_GT(gfx16.size(), 0); - // Count zeros in the room's graphics buffer + // Count zeros in the room's graphics buffer (background blocks only) + constexpr int kBlockSize = 4096; + constexpr int kBgBlocks = 8; int zero_count = 0; - for (size_t i = 0; i < gfx16.size(); i++) { - if (gfx16[i] == 0) zero_count++; + int total_pixels = 0; + for (int block = 0; block < kBgBlocks; block++) { + int base = block * kBlockSize; + for (int i = 0; i < kBlockSize; i++) { + if (gfx16[base + i] == 0) zero_count++; + total_pixels++; + } } - float zero_percent = 100.0f * zero_count / gfx16.size(); - printf("[RoomGraphics] Room 0: Zeros: %d / %zu (%.1f%%)\n", zero_count, - gfx16.size(), zero_percent); + float zero_percent = 100.0f * zero_count / total_pixels; + printf("[RoomGraphics] Room 0: Zeros: %d / %d (%.1f%%)\n", zero_count, + total_pixels, zero_percent); // Log first 64 bytes (one tile's worth) to see actual values printf("[RoomGraphics] First 64 bytes:\n"); @@ -107,39 +117,44 @@ TEST_F(DungeonGraphicsTransparencyTest, RoomGraphicsBufferHasTransparentPixels) } // Print value distribution - int value_counts[8] = {0}; + int value_counts[16] = {0}; int other_count = 0; - for (size_t i = 0; i < gfx16.size(); i++) { - if (gfx16[i] < 8) { - value_counts[gfx16[i]]++; - } else { - other_count++; + for (int block = 0; block < kBgBlocks; block++) { + int base = block * kBlockSize; + for (int i = 0; i < kBlockSize; i++) { + uint8_t value = gfx16[base + i]; + if (value < 16) { + value_counts[value]++; + } else { + other_count++; + } } } printf("[RoomGraphics] Value distribution:\n"); - for (int v = 0; v < 8; v++) { + for (int v = 0; v < 16; v++) { printf(" Value %d: %d (%.1f%%)\n", v, value_counts[v], - 100.0f * value_counts[v] / gfx16.size()); + 100.0f * value_counts[v] / total_pixels); } if (other_count > 0) { - printf(" Values >7: %d (%.1f%%) - UNEXPECTED for 3BPP!\n", other_count, - 100.0f * other_count / gfx16.size()); + printf(" Values >15: %d (%.1f%%) - UNEXPECTED for 4BPP!\n", other_count, + 100.0f * other_count / total_pixels); } EXPECT_GT(zero_percent, 5.0f) << "Room graphics buffer should have transparent pixels. " << "Got " << zero_percent << "%. Check CopyRoomGraphicsToBuffer()."; - // All values should be 0-7 for 3BPP graphics + // Background blocks should not exceed 4BPP (0-15) values. EXPECT_EQ(other_count, 0) - << "Found " << other_count << " pixels with values > 7. " - << "3BPP graphics should only have values 0-7."; + << "Found " << other_count << " pixels with values > 15 in BG blocks. " + << "BG graphics should only have values 0-15."; } // Test 3: Verify specific tile has expected mix of transparent/opaque TEST_F(DungeonGraphicsTransparencyTest, SpecificTileTransparency) { - Room room(0x00, rom_.get()); + Room room = LoadRoomHeaderFromRom(rom_.get(), 0x00); + room.SetGameData(&game_data_); room.LoadRoomGraphics(0xFF); room.CopyRoomGraphicsToBuffer(); diff --git a/test/integration/zelda3/dungeon_object_rom_validation_test.cc b/test/integration/zelda3/dungeon_object_rom_validation_test.cc index 0614494d..15a1de3f 100644 --- a/test/integration/zelda3/dungeon_object_rom_validation_test.cc +++ b/test/integration/zelda3/dungeon_object_rom_validation_test.cc @@ -202,8 +202,8 @@ TEST_F(DungeonObjectRomValidationTest, DrawRoutineMapping_AllType1ObjectsHaveRou int routine = drawer.GetDrawRoutineId(id); EXPECT_GE(routine, 0) << "Object 0x" << std::hex << id << " should have a valid draw routine"; - EXPECT_LT(routine, 40) - << "Object 0x" << std::hex << id << " routine ID should be < 40"; + EXPECT_LT(routine, drawer.GetDrawRoutineCount()) + << "Object 0x" << std::hex << id << " routine ID should be < registry size"; } } diff --git a/test/integration/zelda3/dungeon_palette_test.cc b/test/integration/zelda3/dungeon_palette_test.cc index 644acbbe..c2f1398f 100644 --- a/test/integration/zelda3/dungeon_palette_test.cc +++ b/test/integration/zelda3/dungeon_palette_test.cc @@ -1,10 +1,12 @@ #include +#include #include #include "app/gfx/core/bitmap.h" #include "app/gfx/types/snes_tile.h" #include "zelda3/dungeon/object_drawer.h" #include "zelda3/game_data.h" #include "rom/rom.h" +#include "test/test_utils.h" namespace yaze { namespace zelda3 { @@ -44,10 +46,10 @@ TEST_F(DungeonPaletteTest, PaletteOffsetIsCorrectFor8BPP) { // Row 0, Col 1: Index 2 tiledata[1] = 2; - // Create TileInfo with palette index 1 + // Create TileInfo with palette index 2 (first dungeon bank) gfx::TileInfo tile_info; tile_info.id_ = 0; - tile_info.palette_ = 1; // Palette 1 + tile_info.palette_ = 2; // Palette 2 tile_info.horizontal_mirror_ = false; tile_info.vertical_mirror_ = false; tile_info.over_ = false; @@ -56,33 +58,30 @@ TEST_F(DungeonPaletteTest, PaletteOffsetIsCorrectFor8BPP) { drawer_->DrawTileToBitmap(bitmap, tile_info, 0, 0, tiledata.data()); // Check pixels - // Dungeon tiles use 15-color sub-palettes (not 8 like overworld). - // Formula: final_color = (pixel - 1) + (palette * 15) - // For palette 1, offset is 15. - // Pixel at (0,0) was 1. Result should be (1-1) + 15 = 15. - // Pixel at (1,0) was 2. Result should be (2-1) + 15 = 16. + // Dungeon tiles use 16-color CGRAM banks with index 0 as transparent. + // Formula: final_color = pixel + ((palette - 2) * 16) for palette 2-7. + // Palette 2 maps to the first dungeon bank (offset 0). + // Pixel at (0,0) was 1. Result should be 1. + // Pixel at (1,0) was 2. Result should be 2. const auto& data = bitmap.vector(); // Bitmap data is row-major. // (0,0) is index 0. - EXPECT_EQ(data[0], 15); // (1-1) + 15 = 15 - EXPECT_EQ(data[1], 16); // (2-1) + 15 = 16 + EXPECT_EQ(data[0], 1); + EXPECT_EQ(data[1], 2); - // Test with palette 0 - tile_info.palette_ = 0; + // Test with palette 3 (offset 16) + tile_info.palette_ = 3; drawer_->DrawTileToBitmap(bitmap, tile_info, 0, 0, tiledata.data()); - // Offset 0 * 15 = 0. - // Pixel 1 -> (1-1) + 0 = 0 - // Pixel 2 -> (2-1) + 0 = 1 - EXPECT_EQ(data[0], 0); - EXPECT_EQ(data[1], 1); + EXPECT_EQ(data[0], 17); // 1 + 16 + EXPECT_EQ(data[1], 18); // 2 + 16 - // Test with palette 7 (wraps to palette 1 due to 6 sub-palette limit) + // Test with palette 7 (last dungeon bank) tile_info.palette_ = 7; drawer_->DrawTileToBitmap(bitmap, tile_info, 0, 0, tiledata.data()); - // Palette 7 wraps to 7 % 6 = 1, offset 1 * 15 = 15. - EXPECT_EQ(data[0], 15); // (1-1) + 15 = 15 - EXPECT_EQ(data[1], 16); // (2-1) + 15 = 16 + // Palette 7 maps to bank 5 (offset 80). + EXPECT_EQ(data[0], 81); // 1 + 80 + EXPECT_EQ(data[1], 82); // 2 + 80 } TEST_F(DungeonPaletteTest, PaletteOffsetWorksWithConvertedData) { @@ -100,7 +99,7 @@ TEST_F(DungeonPaletteTest, PaletteOffsetWorksWithConvertedData) { gfx::TileInfo tile_info; tile_info.id_ = 0; - tile_info.palette_ = 2; // Palette 2 → offset 30 (2 * 15) + tile_info.palette_ = 4; // Palette 4 → offset 32 (2 * 16) tile_info.horizontal_mirror_ = false; tile_info.vertical_mirror_ = false; tile_info.over_ = false; @@ -108,17 +107,21 @@ TEST_F(DungeonPaletteTest, PaletteOffsetWorksWithConvertedData) { drawer_->DrawTileToBitmap(bitmap, tile_info, 0, 0, tiledata.data()); const auto& data = bitmap.vector(); - // Dungeon tiles use 15-color sub-palettes. - // Formula: final_color = (pixel - 1) + (palette * 15) - // Pixel 3: (3-1) + 30 = 32 - // Pixel 5: (5-1) + 30 = 34 - EXPECT_EQ(data[0], 32); - EXPECT_EQ(data[1], 34); + // Dungeon tiles use 16-color CGRAM banks. + // Formula: final_color = pixel + ((palette - 2) * 16) + // Pixel 3: 3 + 32 = 35 + // Pixel 5: 5 + 32 = 37 + EXPECT_EQ(data[0], 35); + EXPECT_EQ(data[1], 37); } TEST_F(DungeonPaletteTest, InspectActualPaletteColors) { // Load actual ROM file - auto load_result = rom_->LoadFromFile("zelda3.sfc"); + yaze::test::TestRomManager::SkipIfRomMissing( + yaze::test::RomRole::kVanilla, "DungeonPaletteTest.InspectActualPaletteColors"); + const std::string rom_path = + yaze::test::TestRomManager::GetRomPath(yaze::test::RomRole::kVanilla); + auto load_result = rom_->LoadFromFile(rom_path); if (!load_result.ok()) { GTEST_SKIP() << "ROM file not found, skipping"; } diff --git a/test/integration/zelda3/dungeon_room_test.cc b/test/integration/zelda3/dungeon_room_test.cc index f244511b..8a753a14 100644 --- a/test/integration/zelda3/dungeon_room_test.cc +++ b/test/integration/zelda3/dungeon_room_test.cc @@ -1,7 +1,10 @@ #include #include +#include + #include "rom/rom.h" +#include "test/test_utils.h" #include "zelda3/dungeon/room.h" namespace yaze { @@ -14,8 +17,10 @@ class DungeonRoomTest : public ::testing::Test { #if defined(__linux__) GTEST_SKIP() << "Dungeon room tests require ROM file (unavailable on Linux CI)"; #else - if (!rom_.LoadFromFile("./zelda3.sfc").ok()) { - GTEST_SKIP() << "Failed to load test ROM (zelda3.sfc)"; + TestRomManager::SkipIfRomMissing(RomRole::kVanilla, "DungeonRoomTest"); + const std::string rom_path = TestRomManager::GetRomPath(RomRole::kVanilla); + if (!rom_.LoadFromFile(rom_path).ok()) { + GTEST_SKIP() << "Failed to load test ROM (" << rom_path << ")"; } #endif } diff --git a/test/integration/zelda3/room_integration_test.cc b/test/integration/zelda3/room_integration_test.cc index e6957217..3c0ecedb 100644 --- a/test/integration/zelda3/room_integration_test.cc +++ b/test/integration/zelda3/room_integration_test.cc @@ -4,7 +4,10 @@ #include #include +#include + #include "rom/rom.h" +#include "test/test_utils.h" #include "zelda3/dungeon/room.h" #include "zelda3/dungeon/room_object.h" @@ -24,12 +27,10 @@ class RoomIntegrationTest : public ::testing::Test { // Load the ROM file rom_ = std::make_unique(); - // Check if ROM file exists - const char* rom_path = std::getenv("YAZE_TEST_ROM_PATH"); - if (!rom_path) { - rom_path = "zelda3.sfc"; - } - + yaze::test::TestRomManager::SkipIfRomMissing( + yaze::test::RomRole::kVanilla, "RoomIntegrationTest"); + const std::string rom_path = + yaze::test::TestRomManager::GetRomPath(yaze::test::RomRole::kVanilla); auto status = rom_->LoadFromFile(rom_path); if (!status.ok()) { GTEST_SKIP() << "ROM file not available: " << status.message(); @@ -59,6 +60,7 @@ class RoomIntegrationTest : public ::testing::Test { TEST_F(RoomIntegrationTest, BasicLoadSaveRoundTrip) { // Load room 0 (Hyrule Castle Entrance) Room room1(0x00, rom_.get()); + room1.LoadObjects(); // Get original object count size_t original_count = room1.GetTileObjects().size(); @@ -73,6 +75,7 @@ TEST_F(RoomIntegrationTest, BasicLoadSaveRoundTrip) { // Load the room again Room room2(0x00, rom_.get()); + room2.LoadObjects(); // Verify object count matches EXPECT_EQ(room2.GetTileObjects().size(), original_count); @@ -108,6 +111,7 @@ TEST_F(RoomIntegrationTest, MultiRoomLoadSaveRoundTrip) { // Load room Room room1(room_id, rom_.get()); + room1.LoadObjects(); auto original_objects = room1.GetTileObjects(); if (original_objects.empty()) { @@ -120,6 +124,7 @@ TEST_F(RoomIntegrationTest, MultiRoomLoadSaveRoundTrip) { // Reload and verify Room room2(room_id, rom_.get()); + room2.LoadObjects(); auto reloaded_objects = room2.GetTileObjects(); EXPECT_EQ(reloaded_objects.size(), original_objects.size()); @@ -146,6 +151,7 @@ TEST_F(RoomIntegrationTest, MultiRoomLoadSaveRoundTrip) { TEST_F(RoomIntegrationTest, LayerPreservation) { // Load a room known to have multiple layers Room room(0x01, rom_.get()); + room.LoadObjects(); auto objects = room.GetTileObjects(); ASSERT_GT(objects.size(), 0); @@ -170,6 +176,7 @@ TEST_F(RoomIntegrationTest, LayerPreservation) { ASSERT_TRUE(room.SaveObjects().ok()); Room room2(0x01, rom_.get()); + room2.LoadObjects(); auto reloaded = room2.GetTileObjects(); // Verify layer counts match @@ -199,6 +206,7 @@ TEST_F(RoomIntegrationTest, LayerPreservation) { TEST_F(RoomIntegrationTest, ObjectTypeDistribution) { Room room(0x00, rom_.get()); + room.LoadObjects(); auto objects = room.GetTileObjects(); ASSERT_GT(objects.size(), 0); @@ -222,6 +230,7 @@ TEST_F(RoomIntegrationTest, ObjectTypeDistribution) { ASSERT_TRUE(room.SaveObjects().ok()); Room room2(0x00, rom_.get()); + room2.LoadObjects(); auto reloaded = room2.GetTileObjects(); // Verify type distribution matches @@ -250,6 +259,7 @@ TEST_F(RoomIntegrationTest, BinaryDataExactMatch) { // when no modifications are made Room room(0x02, rom_.get()); + room.LoadObjects(); // Get the ROM location where objects are stored auto rom_data = rom_->vector(); @@ -309,6 +319,7 @@ TEST_F(RoomIntegrationTest, BinaryDataExactMatch) { TEST_F(RoomIntegrationTest, KnownRoomData) { // Room 0x00 (Hyrule Castle Entrance) - verify known objects exist Room room(0x00, rom_.get()); + room.LoadObjects(); auto objects = room.GetTileObjects(); ASSERT_GT(objects.size(), 0) << "Room 0x00 should have objects"; diff --git a/test/unit/zelda3/custom_object_test.cc b/test/unit/zelda3/custom_object_test.cc index 44fbb6e2..569035d5 100644 --- a/test/unit/zelda3/custom_object_test.cc +++ b/test/unit/zelda3/custom_object_test.cc @@ -78,14 +78,14 @@ TEST_F(CustomObjectManagerTest, LoadSimpleObject) { TEST_F(CustomObjectManagerTest, LoadComplexLayout) { // Two rows of 2 tiles - // Row 1: 0xAAAA, 0xBBBB. Jump to next row (stride 64 bytes - 4 bytes used = 60 bytes jump) - // Header 1: Count=2, Jump=60 (0x3C). 0x3C02 -> LE: 02 3C + // Row 1: 0xAAAA, 0xBBBB. Jump to next row (stride 128 bytes, jump 128) + // Header 1: Count=2, Jump=128 (0x80). 0x8002 -> LE: 02 80 // Row 2: 0xCCCC, 0xDDDD. // Header 2: Count=2, Jump=0. 0x0002 -> LE: 02 00 // Terminator std::vector data = { - 0x02, 0x3C, // Header 1 + 0x02, 0x80, // Header 1 0xAA, 0xAA, 0xBB, 0xBB, // Row 1 Tiles (LE: 0xAAAA, 0xBBBB) 0x02, 0x00, // Header 2 0xCC, 0xCC, 0xDD, 0xDD, // Row 2 Tiles (LE: 0xCCCC, 0xDDDD) diff --git a/test/unit/zelda3/dungeon/draw_routine_mapping_test.cc b/test/unit/zelda3/dungeon/draw_routine_mapping_test.cc index 1eefca79..c0a316e0 100644 --- a/test/unit/zelda3/dungeon/draw_routine_mapping_test.cc +++ b/test/unit/zelda3/dungeon/draw_routine_mapping_test.cc @@ -156,7 +156,7 @@ TEST_F(DrawRoutineMappingTest, VerifiesPhase4Step5SpecialMappings) { TEST_F(DrawRoutineMappingTest, VerifiesSubtype2Mappings) { ObjectDrawer drawer(rom_.get(), 0); - // 0x100-0x107 -> Routine 16 (4x4) + // 0x100-0x107 -> Routine 16 (RoomDraw_4x4) EXPECT_EQ(drawer.GetDrawRoutineId(0x100), 16); // 0x108 -> Routine 35 (4x4 Corner BothBG) diff --git a/test/unit/zelda3/dungeon/dungeon_save_test.cc b/test/unit/zelda3/dungeon/dungeon_save_test.cc index e91454e5..e8764b0c 100644 --- a/test/unit/zelda3/dungeon/dungeon_save_test.cc +++ b/test/unit/zelda3/dungeon/dungeon_save_test.cc @@ -81,32 +81,31 @@ class DungeonSaveTest : public ::testing::Test { void SetupSpritePointers() { // 1. Setup kRoomsSpritePointer (0x4C298) - // Points to table in Bank 04. Let's put table at 0x20000 (04:8000) + // Points to table in Bank 09. Let's put table at 0x48000 (09:8000) int ptr_loc = kRoomsSpritePointer; rom_->mutable_data()[ptr_loc] = 0x00; rom_->mutable_data()[ptr_loc + 1] = 0x80; - // Bank is hardcoded to 0x04 in code, so we only write low 2 bytes. + // Bank is hardcoded to 0x09 in code, so we only write low 2 bytes. - // 2. Setup Sprite Pointer Table at 0x20000 - // Room 0 pointer at 0x20000 - // Points to sprite list in Bank 09. Let's put sprites at 0x48000 (09:8000) - // Write 00 80 at 0x20000 - int table_loc = 0x20000; + // 2. Setup Sprite Pointer Table at 0x48000 (09:8000) + // Room 0 pointer -> sprite list at 0x49000 (09:9000) + // Write 00 90 at 0x48000 + int table_loc = 0x48000; rom_->mutable_data()[table_loc] = 0x00; - rom_->mutable_data()[table_loc + 1] = 0x80; + rom_->mutable_data()[table_loc + 1] = 0x90; - // Room 1 pointer at 0x20002 (for size calculation) + // Room 1 pointer at 0x48002 (for size calculation) // Let's give 0x50 bytes for sprites. - // Next room at 0x48050 (09:8050) - // Write 50 80 at 0x20002 + // Next room at 0x49050 (09:9050) + // Write 50 90 at 0x48002 rom_->mutable_data()[table_loc + 2] = 0x50; - rom_->mutable_data()[table_loc + 3] = 0x80; + rom_->mutable_data()[table_loc + 3] = 0x90; - // 3. Setup Sprite Data at 0x48000 + // 3. Setup Sprite Data at 0x49000 // Sortsprite byte (0 or 1) - rom_->mutable_data()[0x48000] = 0x00; + rom_->mutable_data()[0x49000] = 0x00; // End of sprites (0xFF) - rom_->mutable_data()[0x48001] = 0xFF; + rom_->mutable_data()[0x49001] = 0xFF; } std::unique_ptr rom_; diff --git a/test/unit/zelda3/dungeon/object_dimensions_test.cc b/test/unit/zelda3/dungeon/object_dimensions_test.cc index 360826de..bf0cc9fa 100644 --- a/test/unit/zelda3/dungeon/object_dimensions_test.cc +++ b/test/unit/zelda3/dungeon/object_dimensions_test.cc @@ -66,8 +66,8 @@ TEST_F(ObjectDimensionTableTest, GetDimensionsAccountsForSize) { auto [w0, h0] = table.GetDimensions(0x00, 0); auto [w5, h5] = table.GetDimensions(0x00, 5); - // Larger size should give larger width for horizontal walls - EXPECT_GE(w5, w0); + // Size 0 uses the 32-tile default, so width should be larger than size 5 + EXPECT_GT(w0, w5); } TEST_F(ObjectDimensionTableTest, GetHitTestBoundsReturnsObjectPosition) { @@ -143,20 +143,20 @@ TEST_F(ObjectDimensionsTest, CalculatesDimensionsForDiagonalWalls) { ObjectDrawer drawer(rom_.get(), 0); // Test object 0x10 (Diagonal Wall /) - // Routine 17: DrawDiagonalAcute_1to16_BothBG - // Logic: width = (size + 6) * 8 + // Routine 5: DrawDiagonalAcute_1to16 + // Logic: width = (size + 7) * 8 RoomObject obj10(0x10, 10, 10, 0, 0); // Size 0 - // width = (0 + 6) * 8 = 48 + // width = (0 + 7) * 8 = 56 auto dims = drawer.CalculateObjectDimensions(obj10); - EXPECT_EQ(dims.first, 48); - EXPECT_EQ(dims.second, 48); + EXPECT_EQ(dims.first, 56); + EXPECT_EQ(dims.second, 88); RoomObject obj10_size10(0x10, 10, 10, 10, 0); // Size 10 - // width = (10 + 6) * 8 = 128 + // width = (10 + 7) * 8 = 136 dims = drawer.CalculateObjectDimensions(obj10_size10); - EXPECT_EQ(dims.first, 128); - EXPECT_EQ(dims.second, 128); + EXPECT_EQ(dims.first, 136); + EXPECT_EQ(dims.second, 168); } TEST_F(ObjectDimensionsTest, CalculatesDimensionsForType2Corners) { @@ -164,10 +164,10 @@ TEST_F(ObjectDimensionsTest, CalculatesDimensionsForType2Corners) { // Test object 0x40 (Type 2 Corner) // Routine 22: Edge 1x1 - // Width 8, Height 8 + // Width 24, Height 8 (corner + middle + end) RoomObject obj40(0x40, 10, 10, 0, 0); auto dims = drawer.CalculateObjectDimensions(obj40); - EXPECT_EQ(dims.first, 8); + EXPECT_EQ(dims.first, 24); EXPECT_EQ(dims.second, 8); } diff --git a/test/unit/zelda3/dungeon/object_drawing_comprehensive_test.cc b/test/unit/zelda3/dungeon/object_drawing_comprehensive_test.cc index 2093e6d3..f04164eb 100644 --- a/test/unit/zelda3/dungeon/object_drawing_comprehensive_test.cc +++ b/test/unit/zelda3/dungeon/object_drawing_comprehensive_test.cc @@ -3,7 +3,7 @@ * @brief Comprehensive tests for object drawing, parsing, and routine mapping * * Tests the following areas: - * 1. Object type detection (Type 1: 0x00-0xFF, Type 2: 0x100-0x1FF, Type 3: 0xF80-0xFFF) + * 1. Object type detection (Type 1: 0x00-0xFF, Type 2: 0x100-0x13F, Type 3: 0xF80-0xFFF) * 2. Tile count lookup table (kSubtype1TileLengths) * 3. Draw routine mapping completeness * 4. Type 3 object index calculation @@ -71,12 +71,18 @@ TEST_F(ObjectDrawingComprehensiveTest, DetectsType1Objects) { TEST_F(ObjectDrawingComprehensiveTest, DetectsType2Objects) { ObjectParser parser(rom_.get()); - // Type 2: 0x100-0x1FF (64 fixed-size objects) - for (int id = 0x100; id <= 0x1FF; ++id) { + // Type 2: 0x100-0x13F (64 objects) + for (int id = 0x100; id <= 0x13F; ++id) { auto info = parser.GetObjectSubtype(id); ASSERT_TRUE(info.ok()) << "Failed for ID 0x" << std::hex << id; EXPECT_EQ(info->subtype, 2) << "ID 0x" << std::hex << id << " should be Type 2"; - EXPECT_EQ(info->max_tile_count, 8) << "Type 2 objects should have 8 tiles"; + if (id >= 0x100 && id <= 0x10F) { + EXPECT_EQ(info->max_tile_count, 16) << "Type 2 corners should have 16 tiles"; + } else if (id >= 0x110 && id <= 0x117) { + EXPECT_EQ(info->max_tile_count, 12) << "Type 2 weird corners should have 12 tiles"; + } else { + EXPECT_EQ(info->max_tile_count, 8) << "Type 2 objects should have 8 tiles"; + } } } @@ -88,7 +94,16 @@ TEST_F(ObjectDrawingComprehensiveTest, DetectsType3Objects) { auto info = parser.GetObjectSubtype(id); ASSERT_TRUE(info.ok()) << "Failed for ID 0x" << std::hex << id; EXPECT_EQ(info->subtype, 3) << "ID 0x" << std::hex << id << " should be Type 3"; - EXPECT_EQ(info->max_tile_count, 8) << "Type 3 objects should have 8 tiles"; + if (id == 0xFB1 || id == 0xFB2 || + id == 0xF94 || id == 0xFCE || + (id >= 0xFE7 && id <= 0xFE8) || + (id >= 0xFEC && id <= 0xFED)) { + EXPECT_EQ(info->max_tile_count, 12) << "Type 3 objects should have 12 tiles"; + } else if (id == 0xFC8 || id == 0xFE6 || id == 0xFEB || id == 0xFFA) { + EXPECT_EQ(info->max_tile_count, 16) << "Type 3 objects should have 16 tiles"; + } else { + EXPECT_EQ(info->max_tile_count, 8) << "Type 3 objects should have 8 tiles"; + } } } @@ -158,8 +173,8 @@ TEST_F(ObjectDrawingComprehensiveTest, Type3IndexCalculation_AllIndicesInRange) TEST_F(ObjectDrawingComprehensiveTest, Type2IndexCalculation_BoundaryValues) { ObjectParser parser(rom_.get()); - // Verify Type 2 index calculation: index = (object_id - 0x100) & 0xFF - // This maps 0x100-0x1FF to table indices 0-255 + // Verify Type 2 index calculation: index = (object_id - 0x100) & 0x3F + // This maps 0x100-0x13F to table indices 0-63 (64-entry table) struct TestCase { int object_id; @@ -170,8 +185,7 @@ TEST_F(ObjectDrawingComprehensiveTest, Type2IndexCalculation_BoundaryValues) { {0x100, 0}, // First Type 2 object -> index 0 {0x101, 1}, // Second Type 2 object -> index 1 {0x10F, 15}, // Index 15 - {0x13F, 63}, // Last commonly used Type 2 object - {0x1FF, 255}, // Last possible Type 2 object + {0x13F, 63}, // Last Type 2 object -> index 63 }; for (const auto& tc : test_cases) { @@ -253,15 +267,14 @@ TEST_F(ObjectDrawingComprehensiveTest, TileCountLookupTable_SpecialCases) { TEST_F(ObjectDrawingComprehensiveTest, DrawRoutineMapping_AllSubtype1ObjectsHaveRoutines) { ObjectDrawer drawer(rom_.get(), 0); + const int max_routine_id = drawer.GetDrawRoutineCount() - 1; // Verify all Type 1 objects (0x00-0xF7) have valid routine mappings for (int id = 0; id <= 0xF7; ++id) { int routine_id = drawer.GetDrawRoutineId(id); - // Should return valid routine (0-82) or -1 for unmapped - // Phase 4 added: SuperSquare routines 56-64, Step 2 variants 65-74, - // Step 3 diagonal ceilings 75-78, Step 5 special routines 79-82 + // Should return valid routine (0..max) or -1 for unmapped EXPECT_GE(routine_id, -1) << "ID 0x" << std::hex << id; - EXPECT_LE(routine_id, 82) << "ID 0x" << std::hex << id; + EXPECT_LE(routine_id, max_routine_id) << "ID 0x" << std::hex << id; } } @@ -360,11 +373,13 @@ TEST_F(ObjectDrawingComprehensiveTest, DrawRoutineMapping_Type3SpecialObjects) { // Type 3 objects (0xF80-0xFFF) - actual decoded IDs from ROM // Index = (object_id - 0xF80) & 0x7F - // Water Face (indices 0-2) - for (int id : {0xF80, 0xF81, 0xF82}) { - EXPECT_EQ(drawer.GetDrawRoutineId(id), 34) - << "ID 0x" << std::hex << id << " should use routine 34 (Water Face)"; - } + // Water Face variants (indices 0-2) + EXPECT_EQ(drawer.GetDrawRoutineId(0xF80), 94) + << "ID 0xF80 should use routine 94 (Empty Water Face)"; + EXPECT_EQ(drawer.GetDrawRoutineId(0xF81), 95) + << "ID 0xF81 should use routine 95 (Spitting Water Face)"; + EXPECT_EQ(drawer.GetDrawRoutineId(0xF82), 96) + << "ID 0xF82 should use routine 96 (Drenching Water Face)"; // Somaria Line (indices 3-9) for (int id : {0xF83, 0xF84, 0xF85, 0xF86, 0xF87, 0xF88, 0xF89}) { @@ -372,8 +387,12 @@ TEST_F(ObjectDrawingComprehensiveTest, DrawRoutineMapping_Type3SpecialObjects) { << "ID 0x" << std::hex << id << " should use routine 33 (Somaria Line)"; } - // Chests (indices 23-26 = 0x17-0x1A + 0xF80 = 0xF97-0xF9A) - for (int id : {0xF97, 0xF98, 0xF99, 0xF9A}) { + // Prison Cell + Big Key Lock + Chests (indices 0x17-0x1A -> 0xF97-0xF9A) + EXPECT_EQ(drawer.GetDrawRoutineId(0xF97), 97) + << "ID 0xF97 should use routine 97 (Prison Cell)"; + EXPECT_EQ(drawer.GetDrawRoutineId(0xF98), 92) + << "ID 0xF98 should use routine 92 (Big Key Lock)"; + for (int id : {0xF99, 0xF9A}) { EXPECT_EQ(drawer.GetDrawRoutineId(id), 39) << "ID 0x" << std::hex << id << " should use routine 39 (DrawChest)"; } @@ -503,14 +522,14 @@ TEST_F(ObjectDrawingComprehensiveTest, DimensionCalculation_DiagonalPatterns) { // Diagonal walls use (size + 6) or (size + 7) count RoomObject diagonal_size0(0x10, 0, 0, 0, 0); auto dims = drawer.CalculateObjectDimensions(diagonal_size0); - // Diagonal: (size + 6) * 8 pixels each direction - EXPECT_EQ(dims.first, 48); // 6 * 8 = 48 - EXPECT_EQ(dims.second, 48); + // Diagonal: count = size + 7, width = count * 8, height = (count + 4) * 8 + EXPECT_EQ(dims.first, 56); // 7 * 8 = 56 + EXPECT_EQ(dims.second, 88); // (7 + 4) * 8 = 88 RoomObject diagonal_size10(0x10, 0, 0, 10, 0); dims = drawer.CalculateObjectDimensions(diagonal_size10); - EXPECT_EQ(dims.first, 128); // 16 * 8 = 128 - EXPECT_EQ(dims.second, 128); + EXPECT_EQ(dims.first, 136); // 17 * 8 = 136 + EXPECT_EQ(dims.second, 168); // (17 + 4) * 8 = 168 } // ============================================================================ @@ -546,14 +565,10 @@ TEST_F(ObjectDrawingComprehensiveTest, EdgeCase_Size0HandledCorrectly) { auto dims00 = drawer.CalculateObjectDimensions(obj00_size0); EXPECT_EQ(dims00.first, 512); // 32 * 16 = 512 - // NOTE: Object 0x01 (routine 1) size=0 handling is NOT implemented in - // CalculateObjectDimensions. The draw routine uses size=26 when size=0, - // but CalculateObjectDimensions falls through to default case. - // This is a known limitation - see TODO in CalculateObjectDimensions. + // Object 0x01 (routine 1) size=0 uses GetSize_1to15or26 -> 26 repetitions. RoomObject obj01_size0(0x01, 0, 0, 0, 0); auto dims01 = drawer.CalculateObjectDimensions(obj01_size0); - // Current behavior: falls through to default, size_h=0, width=(0+1)*8=8 - EXPECT_EQ(dims01.first, 8); // Known limitation: should be 416 (26*16) + EXPECT_EQ(dims01.first, 416); // 26 * 16 } // ============================================================================ @@ -597,19 +612,19 @@ TEST_F(ObjectDrawingComprehensiveTest, TransparencyHandling_Pixel0IsSkipped) { const auto& data = bg.bitmap().vector(); EXPECT_EQ(data[0], 0xFF) << "Transparent pixel (0) should not overwrite bitmap"; - // Verify pixel (1,0) WAS written with value (1-1) + palette_offset = 0 - EXPECT_EQ(data[1], 0) << "Non-transparent pixel should be written"; + // Verify pixel (1,0) WAS written with value pixel + palette_offset = 1 + EXPECT_EQ(data[1], 1) << "Non-transparent pixel should be written"; - // Verify pixel (2,0) WAS written with value (2-1) + palette_offset = 1 - EXPECT_EQ(data[2], 1) << "Non-transparent pixel should be written"; + // Verify pixel (2,0) WAS written with value pixel + palette_offset = 2 + EXPECT_EQ(data[2], 2) << "Non-transparent pixel should be written"; - // Verify pixel (0,1) WAS written with value (3-1) + palette_offset = 2 - EXPECT_EQ(data[64], 2) << "Non-transparent pixel at row 1 should be written"; + // Verify pixel (0,1) WAS written with value pixel + palette_offset = 3 + EXPECT_EQ(data[64], 3) << "Non-transparent pixel at row 1 should be written"; } TEST_F(ObjectDrawingComprehensiveTest, TileInfo_PaletteIndexMappingVerify) { // Verify palette index calculation: - // final_color = (pixel - 1) + (palette_ * 15) + // final_color = pixel + (palette_bank * 16) // TileInfo with palette 0 gfx::TileInfo tile0; @@ -619,13 +634,13 @@ TEST_F(ObjectDrawingComprehensiveTest, TileInfo_PaletteIndexMappingVerify) { gfx::TileInfo tile1; tile1.palette_ = 1; - // Palette 0 should use offsets 0-14 - // Palette 1 should use offsets 15-29 + // Palette 0 should use offsets 0-15 + // Palette 1 should use offsets 16-31 // etc. // This is design verification - the actual color lookup happens in DrawTileToBitmap - EXPECT_EQ(tile0.palette_ * 15, 0); - EXPECT_EQ(tile1.palette_ * 15, 15); + EXPECT_EQ(tile0.palette_ * 16, 0); + EXPECT_EQ(tile1.palette_ * 16, 16); // Test palette clamping - palettes 6,7 wrap to 0,1 gfx::TileInfo tile6; diff --git a/test/unit/zelda3/dungeon/room_layer_manager_test.cc b/test/unit/zelda3/dungeon/room_layer_manager_test.cc index f915d1d9..7430daed 100644 --- a/test/unit/zelda3/dungeon/room_layer_manager_test.cc +++ b/test/unit/zelda3/dungeon/room_layer_manager_test.cc @@ -80,11 +80,11 @@ TEST_F(RoomLayerManagerTest, BG2OnTopDrawOrderBG1First) { manager_.SetBG2OnTop(true); auto order = manager_.GetDrawOrder(); - // BG1 should be drawn first when BG2 is on top - EXPECT_EQ(order[0], LayerType::BG1_Layout); - EXPECT_EQ(order[1], LayerType::BG1_Objects); - EXPECT_EQ(order[2], LayerType::BG2_Layout); - EXPECT_EQ(order[3], LayerType::BG2_Objects); + // Draw order remains BG2 then BG1; "BG2 on top" only affects color math + EXPECT_EQ(order[0], LayerType::BG2_Layout); + EXPECT_EQ(order[1], LayerType::BG2_Objects); + EXPECT_EQ(order[2], LayerType::BG1_Layout); + EXPECT_EQ(order[3], LayerType::BG1_Objects); } // ============================================================================= @@ -143,8 +143,8 @@ TEST_F(RoomLayerManagerTest, ApplyLayerMergingOff) { LayerMergeType merge{0x00, "Off", false, false, false}; manager_.ApplyLayerMerging(merge); - EXPECT_EQ(manager_.GetLayerBlendMode(LayerType::BG2_Layout), LayerBlendMode::Off); - EXPECT_EQ(manager_.GetLayerBlendMode(LayerType::BG2_Objects), LayerBlendMode::Off); + EXPECT_EQ(manager_.GetLayerBlendMode(LayerType::BG2_Layout), LayerBlendMode::Normal); + EXPECT_EQ(manager_.GetLayerBlendMode(LayerType::BG2_Objects), LayerBlendMode::Normal); } // ============================================================================= diff --git a/test/unit/zelda3/object_parser_test.cc b/test/unit/zelda3/object_parser_test.cc index e646f66a..a5703eb4 100644 --- a/test/unit/zelda3/object_parser_test.cc +++ b/test/unit/zelda3/object_parser_test.cc @@ -78,7 +78,7 @@ TEST_F(ObjectParserTest, ParseSubtype2Object) { ASSERT_TRUE(result.ok()); const auto& tiles = result.value(); - EXPECT_EQ(tiles.size(), 8); + EXPECT_EQ(tiles.size(), 16); } TEST_F(ObjectParserTest, ParseSubtype3Object) { @@ -146,4 +146,4 @@ TEST_F(ObjectParserTest, NullRom) { } } // namespace test -} // namespace yaze \ No newline at end of file +} // namespace yaze diff --git a/tools/test_helpers/dungeon_test_harness.cc b/tools/test_helpers/dungeon_test_harness.cc index f2696a65..9d2cb69b 100644 --- a/tools/test_helpers/dungeon_test_harness.cc +++ b/tools/test_helpers/dungeon_test_harness.cc @@ -6,6 +6,7 @@ #include "app/emu/snes.h" #include "rom/rom.h" +#include "util/macro.h" using namespace yaze;