yaze 0.3.2
Link to the Past ROM Editor
 
Loading...
Searching...
No Matches
overworld_integration_test.cc
Go to the documentation of this file.
1#include <gtest/gtest.h>
2#include <memory>
3#include <vector>
4#include <filesystem>
5#include <string>
6
7#include "app/rom.h"
10#include "testing.h"
11
12namespace yaze {
13namespace zelda3 {
14
25class OverworldIntegrationTest : public ::testing::Test {
26 protected:
27 void SetUp() override {
28#if defined(__linux__)
29 GTEST_SKIP();
30#endif
31
32 // Check if we should use real ROM or mock data
33 const char* rom_path_env = getenv("YAZE_TEST_ROM_PATH");
34 const char* skip_rom_tests = getenv("YAZE_SKIP_ROM_TESTS");
35
36 if (skip_rom_tests) {
37 GTEST_SKIP() << "ROM tests disabled";
38 }
39
40 if (rom_path_env && std::filesystem::exists(rom_path_env)) {
41 // Use real ROM for testing
42 rom_ = std::make_unique<Rom>();
43 auto status = rom_->LoadFromFile(rom_path_env);
44 if (status.ok()) {
45 use_real_rom_ = true;
46 overworld_ = std::make_unique<Overworld>(rom_.get());
47 return;
48 }
49 }
50
51 // Fall back to mock data
52 use_real_rom_ = false;
53 rom_ = std::make_unique<Rom>();
55 rom_->LoadFromData(mock_rom_data_);
56 overworld_ = std::make_unique<Overworld>(rom_.get());
57 }
58
59 void TearDown() override {
60 overworld_.reset();
61 rom_.reset();
62 }
63
65 mock_rom_data_.resize(0x200000, 0x00);
66
67 // Basic ROM structure
68 mock_rom_data_[0x140145] = 0xFF; // Vanilla ASM
69
70 // Tile16 expansion flag
71 mock_rom_data_[0x017D28] = 0x0F; // Vanilla
72
73 // Tile32 expansion flag
74 mock_rom_data_[0x01772E] = 0x04; // Vanilla
75
76 // Basic map data
77 for (int i = 0; i < 160; i++) {
78 mock_rom_data_[0x012844 + i] = 0x00; // Small areas
79 }
80
81 // Setup entrance data (matches ZScream Constants.OWEntranceMap/Pos/EntranceId)
82 for (int i = 0; i < 129; i++) {
83 mock_rom_data_[0x0DB96F + (i * 2)] = i & 0xFF; // Map ID
84 mock_rom_data_[0x0DB96F + (i * 2) + 1] = (i >> 8) & 0xFF;
85 mock_rom_data_[0x0DBA71 + (i * 2)] = (i * 16) & 0xFF; // Map Position
86 mock_rom_data_[0x0DBA71 + (i * 2) + 1] = ((i * 16) >> 8) & 0xFF;
87 mock_rom_data_[0x0DBB73 + i] = i & 0xFF; // Entrance ID
88 }
89
90 // Setup exit data (matches ZScream Constants.OWExit*)
91 for (int i = 0; i < 0x4F; i++) {
92 mock_rom_data_[0x015D8A + (i * 2)] = i & 0xFF; // Room ID
93 mock_rom_data_[0x015D8A + (i * 2) + 1] = (i >> 8) & 0xFF;
94 mock_rom_data_[0x015E28 + i] = i & 0xFF; // Map ID
95 mock_rom_data_[0x015E77 + (i * 2)] = i & 0xFF; // VRAM
96 mock_rom_data_[0x015E77 + (i * 2) + 1] = (i >> 8) & 0xFF;
97 // Add other exit fields...
98 }
99 }
100
101 std::vector<uint8_t> mock_rom_data_;
102 std::unique_ptr<Rom> rom_;
103 std::unique_ptr<Overworld> overworld_;
104 bool use_real_rom_ = false;
105};
106
107// Test Tile32 expansion detection
108TEST_F(OverworldIntegrationTest, Tile32ExpansionDetection) {
109 mock_rom_data_[0x01772E] = 0x04;
110 mock_rom_data_[0x140145] = 0xFF;
111
112 auto status = overworld_->Load(rom_.get());
113 ASSERT_TRUE(status.ok());
114
115 // Test expanded detection
116 mock_rom_data_[0x01772E] = 0x05;
117 overworld_ = std::make_unique<Overworld>(rom_.get());
118
119 status = overworld_->Load(rom_.get());
120 ASSERT_TRUE(status.ok());
121}
122
123// Test Tile16 expansion detection
124TEST_F(OverworldIntegrationTest, Tile16ExpansionDetection) {
125 mock_rom_data_[0x017D28] = 0x0F;
126 mock_rom_data_[0x140145] = 0xFF;
127
128 auto status = overworld_->Load(rom_.get());
129 ASSERT_TRUE(status.ok());
130
131 // Test expanded detection
132 mock_rom_data_[0x017D28] = 0x10;
133 overworld_ = std::make_unique<Overworld>(rom_.get());
134
135 status = overworld_->Load(rom_.get());
136 ASSERT_TRUE(status.ok());
137}
138
139// Test entrance loading matches ZScream coordinate calculation
140TEST_F(OverworldIntegrationTest, EntranceCoordinateCalculation) {
141 auto status = overworld_->Load(rom_.get());
142 ASSERT_TRUE(status.ok());
143
144 const auto& entrances = overworld_->entrances();
145 EXPECT_EQ(entrances.size(), 129);
146
147 // Verify coordinate calculation matches ZScream logic:
148 // int p = mapPos >> 1;
149 // int x = p % 64;
150 // int y = p >> 6;
151 // int real_x = (x * 16) + (((mapId % 64) - (((mapId % 64) / 8) * 8)) * 512);
152 // int real_y = (y * 16) + (((mapId % 64) / 8) * 512);
153
154 for (int i = 0; i < std::min(10, static_cast<int>(entrances.size())); i++) {
155 const auto& entrance = entrances[i];
156
157 uint16_t map_pos = i * 16; // Our test data
158 uint16_t map_id = i; // Our test data
159
160 int position = map_pos >> 1;
161 int x_coord = position % 64;
162 int y_coord = position >> 6;
163 int expected_x = (x_coord * 16) + (((map_id % 64) - (((map_id % 64) / 8) * 8)) * 512);
164 int expected_y = (y_coord * 16) + (((map_id % 64) / 8) * 512);
165
166 EXPECT_EQ(entrance.x_, expected_x);
167 EXPECT_EQ(entrance.y_, expected_y);
168 EXPECT_EQ(entrance.entrance_id_, i);
169 EXPECT_FALSE(entrance.is_hole_);
170 }
171}
172
173// Test exit loading matches ZScream data structure
175 auto status = overworld_->Load(rom_.get());
176 ASSERT_TRUE(status.ok());
177
178 const auto& exits = overworld_->exits();
179 EXPECT_EQ(exits->size(), 0x4F);
180
181 // Verify exit data matches our test data
182 for (int i = 0; i < std::min(5, static_cast<int>(exits->size())); i++) {
183 const auto& exit = exits->at(i);
184 // EXPECT_EQ(exit.room_id_, i);
185 // EXPECT_EQ(exit.map_id_, i);
186 // EXPECT_EQ(exit.map_pos_, i);
187 }
188}
189
190// Test ASM version detection affects item loading
191TEST_F(OverworldIntegrationTest, ASMVersionItemLoading) {
192 // Test vanilla ASM (should limit to 0x80 maps)
193 mock_rom_data_[0x140145] = 0xFF;
194 overworld_ = std::make_unique<Overworld>(rom_.get());
195
196 auto status = overworld_->Load(rom_.get());
197 ASSERT_TRUE(status.ok());
198
199 const auto& items = overworld_->all_items();
200
201 // Test v3+ ASM (should support all 0xA0 maps)
202 mock_rom_data_[0x140145] = 0x03;
203 overworld_ = std::make_unique<Overworld>(rom_.get());
204
205 status = overworld_->Load(rom_.get());
206 ASSERT_TRUE(status.ok());
207
208 const auto& items_v3 = overworld_->all_items();
209 // v3 should have more comprehensive support
210 EXPECT_GE(items_v3.size(), items.size());
211}
212
213// Test map size assignment logic
214TEST_F(OverworldIntegrationTest, MapSizeAssignment) {
215 auto status = overworld_->Load(rom_.get());
216 ASSERT_TRUE(status.ok());
217
218 const auto& maps = overworld_->overworld_maps();
219 EXPECT_EQ(maps.size(), 160);
220
221 // Verify all maps are initialized
222 for (const auto& map : maps) {
223 EXPECT_GE(map.area_size(), AreaSizeEnum::SmallArea);
224 EXPECT_LE(map.area_size(), AreaSizeEnum::TallArea);
225 }
226}
227
228// Test integration with ZSCustomOverworld version detection
229TEST_F(OverworldIntegrationTest, ZSCustomOverworldVersionIntegration) {
230 if (!use_real_rom_) {
231 GTEST_SKIP() << "Real ROM required for ZSCustomOverworld version testing";
232 }
233
234 auto status = overworld_->Load(rom_.get());
235 ASSERT_TRUE(status.ok());
236
237 // Check ASM version detection
238 auto version_byte = rom_->ReadByte(0x140145);
239 ASSERT_TRUE(version_byte.ok());
240
241 uint8_t asm_version = *version_byte;
242
243 if (asm_version == 0xFF) {
244 // Vanilla ROM
245 EXPECT_FALSE(overworld_->expanded_tile16());
246 EXPECT_FALSE(overworld_->expanded_tile32());
247 } else if (asm_version >= 0x02 && asm_version <= 0x03) {
248 // ZSCustomOverworld v2/v3
249 // Should have expanded features
250 EXPECT_TRUE(overworld_->expanded_tile16());
251 EXPECT_TRUE(overworld_->expanded_tile32());
252 }
253
254 // Verify version-specific features are properly detected
255 if (asm_version >= 0x03) {
256 // v3 features should be available
257 const auto& maps = overworld_->overworld_maps();
258 EXPECT_EQ(maps.size(), 160); // All 160 maps supported in v3
259 }
260}
261
262// Test compatibility with RomDependentTestSuite infrastructure
263TEST_F(OverworldIntegrationTest, RomDependentTestSuiteCompatibility) {
264 if (!use_real_rom_) {
265 GTEST_SKIP() << "Real ROM required for RomDependentTestSuite compatibility testing";
266 }
267
268 // Test that our overworld loading works with the same patterns as RomDependentTestSuite
269 auto status = overworld_->Load(rom_.get());
270 ASSERT_TRUE(status.ok());
271
272 // Verify ROM-dependent features work correctly
273 EXPECT_TRUE(overworld_->is_loaded());
274
275 const auto& maps = overworld_->overworld_maps();
276 EXPECT_EQ(maps.size(), 160);
277
278 // Test that we can access the same data structures as RomDependentTestSuite
279 for (int i = 0; i < std::min(10, static_cast<int>(maps.size())); i++) {
280 const auto& map = maps[i];
281
282 // Verify map properties are accessible
283 EXPECT_GE(map.area_graphics(), 0);
284 EXPECT_GE(map.main_palette(), 0);
285 EXPECT_GE(map.area_size(), AreaSizeEnum::SmallArea);
286 EXPECT_LE(map.area_size(), AreaSizeEnum::TallArea);
287 }
288
289 // Test that sprite data is accessible (matches RomDependentTestSuite expectations)
290 const auto& sprites = overworld_->sprites(0);
291 EXPECT_EQ(sprites.size(), 3); // Three game states
292
293 // Test that item data is accessible
294 const auto& items = overworld_->all_items();
295 EXPECT_GE(items.size(), 0);
296
297 // Test that entrance/exit data is accessible
298 const auto& entrances = overworld_->entrances();
299 const auto& exits = overworld_->exits();
300 EXPECT_EQ(entrances.size(), 129);
301 EXPECT_EQ(exits->size(), 0x4F);
302}
303
304// Test comprehensive overworld data integrity
305TEST_F(OverworldIntegrationTest, ComprehensiveDataIntegrity) {
306 auto status = overworld_->Load(rom_.get());
307 ASSERT_TRUE(status.ok());
308
309 // Verify all major data structures are properly loaded
310 EXPECT_GT(overworld_->tiles16().size(), 0);
311 EXPECT_GT(overworld_->tiles32_unique().size(), 0);
312
313 // Verify map organization matches ZScream expectations
314 const auto& map_tiles = overworld_->map_tiles();
315 EXPECT_EQ(map_tiles.light_world.size(), 512);
316 EXPECT_EQ(map_tiles.dark_world.size(), 512);
317 EXPECT_EQ(map_tiles.special_world.size(), 512);
318
319 // Verify each world has proper 512x512 tile data
320 for (const auto& row : map_tiles.light_world) {
321 EXPECT_EQ(row.size(), 512);
322 }
323 for (const auto& row : map_tiles.dark_world) {
324 EXPECT_EQ(row.size(), 512);
325 }
326 for (const auto& row : map_tiles.special_world) {
327 EXPECT_EQ(row.size(), 512);
328 }
329
330 // Verify overworld maps are properly initialized
331 const auto& maps = overworld_->overworld_maps();
332 EXPECT_EQ(maps.size(), 160);
333
334 for (const auto& map : maps) {
335 // TODO: Find a way to compare
336 // EXPECT_TRUE(map.bitmap_data() != nullptr);
337 }
338
339 // Verify tile types are loaded
340 const auto& tile_types = overworld_->all_tiles_types();
341 EXPECT_EQ(tile_types.size(), 0x200);
342}
343
344// Test ZScream coordinate calculation compatibility
345TEST_F(OverworldIntegrationTest, ZScreamCoordinateCompatibility) {
346 auto status = overworld_->Load(rom_.get());
347 ASSERT_TRUE(status.ok());
348
349 const auto& entrances = overworld_->entrances();
350 EXPECT_EQ(entrances.size(), 129);
351
352 // Test coordinate calculation matches ZScream logic exactly
353 for (int i = 0; i < std::min(10, static_cast<int>(entrances.size())); i++) {
354 const auto& entrance = entrances[i];
355
356 // ZScream coordinate calculation:
357 // int p = mapPos >> 1;
358 // int x = p % 64;
359 // int y = p >> 6;
360 // int real_x = (x * 16) + (((mapId % 64) - (((mapId % 64) / 8) * 8)) * 512);
361 // int real_y = (y * 16) + (((mapId % 64) / 8) * 512);
362
363 uint16_t map_pos = entrance.map_pos_;
364 uint16_t map_id = entrance.map_id_;
365
366 int position = map_pos >> 1;
367 int x_coord = position % 64;
368 int y_coord = position >> 6;
369 int expected_x = (x_coord * 16) + (((map_id % 64) - (((map_id % 64) / 8) * 8)) * 512);
370 int expected_y = (y_coord * 16) + (((map_id % 64) / 8) * 512);
371
372 EXPECT_EQ(entrance.x_, expected_x);
373 EXPECT_EQ(entrance.y_, expected_y);
374 }
375
376 // Test hole coordinate calculation with 0x400 offset
377 const auto& holes = overworld_->holes();
378 EXPECT_EQ(holes.size(), 0x13);
379
380 for (int i = 0; i < std::min(5, static_cast<int>(holes.size())); i++) {
381 const auto& hole = holes[i];
382
383 // ZScream hole coordinate calculation:
384 // int p = (mapPos + 0x400) >> 1;
385 // int x = p % 64;
386 // int y = p >> 6;
387 // int real_x = (x * 16) + (((mapId % 64) - (((mapId % 64) / 8) * 8)) * 512);
388 // int real_y = (y * 16) + (((mapId % 64) / 8) * 512);
389
390 uint16_t map_pos = hole.map_pos_;
391 uint16_t map_id = hole.map_id_;
392
393 int position = map_pos >> 1;
394 int x_coord = position % 64;
395 int y_coord = position >> 6;
396 int expected_x = (x_coord * 16) + (((map_id % 64) - (((map_id % 64) / 8) * 8)) * 512);
397 int expected_y = (y_coord * 16) + (((map_id % 64) / 8) * 512);
398
399 EXPECT_EQ(hole.x_, expected_x);
400 EXPECT_EQ(hole.y_, expected_y);
401 EXPECT_TRUE(hole.is_hole_);
402 }
403}
404
405} // namespace zelda3
406} // namespace yaze
Comprehensive overworld integration test that validates YAZE C++ implementation against ZScream C# lo...
TEST_F(DungeonEditorSystemIntegrationTest, BasicInitialization)
Main namespace for the application.