yaze 0.3.2
Link to the Past ROM Editor
 
Loading...
Searching...
No Matches
overworld.cc
Go to the documentation of this file.
1#include "overworld.h"
2
3#include <algorithm>
4#include <cstdint>
5#include <future>
6#include <mutex>
7#include <set>
8#include <thread>
9#include <unordered_map>
10#include <vector>
11
12#include "absl/status/status.h"
13#include "core/features.h"
17#include "app/rom.h"
18#include "app/snes.h"
19#include "zelda3/common.h"
22#include "util/hex.h"
23#include "util/log.h"
24#include "util/macro.h"
27
28namespace yaze {
29namespace zelda3 {
30
31absl::Status Overworld::Load(Rom* rom) {
32 gfx::ScopedTimer timer("Overworld::Load");
33
34 if (rom->size() == 0) {
35 return absl::InvalidArgumentError("ROM file not loaded");
36 }
37 rom_ = rom;
38
39 // Phase 1: Tile Assembly (can be parallelized)
40 {
41 gfx::ScopedTimer assembly_timer("AssembleTiles");
44 }
45
46 // Phase 2: Map Decompression (major bottleneck - now parallelized)
47 {
48 gfx::ScopedTimer decompression_timer("DecompressAllMapTiles");
50 }
51
52 // Phase 3: Map Object Creation (fast)
53 {
54 gfx::ScopedTimer map_creation_timer("CreateOverworldMapObjects");
55 for (int map_index = 0; map_index < kNumOverworldMaps; ++map_index)
56 overworld_maps_.emplace_back(map_index, rom_);
57
58 // Populate map_parent_ array with parent information from each map
59 for (int map_index = 0; map_index < kNumOverworldMaps; ++map_index) {
60 map_parent_[map_index] = overworld_maps_[map_index].parent();
61 }
62 }
63
64 // Phase 4: Map Configuration
65 uint8_t asm_version = (*rom_)[OverworldCustomASMHasBeenApplied];
66 if (asm_version >= 3) {
68 } else {
70 }
71
72 // Phase 5: Data Loading (with individual timing)
73 {
74 gfx::ScopedTimer data_loading_timer("LoadOverworldData");
75
76 {
77 gfx::ScopedTimer tile_types_timer("LoadTileTypes");
79 }
80
81 {
82 gfx::ScopedTimer entrances_timer("LoadEntrances");
84 }
85
86 {
87 gfx::ScopedTimer holes_timer("LoadHoles");
89 }
90
91 {
92 gfx::ScopedTimer exits_timer("LoadExits");
94 }
95
96 {
97 gfx::ScopedTimer items_timer("LoadItems");
99 }
100
101 {
102 gfx::ScopedTimer overworld_maps_timer("LoadOverworldMaps");
104 }
105
106 {
107 gfx::ScopedTimer sprites_timer("LoadSprites");
109 }
110 }
111
112 is_loaded_ = true;
113 return absl::OkStatus();
114}
115
117 for (int i = 128; i < 145; i++) {
118 overworld_maps_[i].SetAsSmallMap(0);
119 }
120
121 overworld_maps_[129].SetAsLargeMap(129, 0);
122 overworld_maps_[130].SetAsLargeMap(129, 1);
123 overworld_maps_[137].SetAsLargeMap(129, 2);
124 overworld_maps_[138].SetAsLargeMap(129, 3);
125 overworld_maps_[136].SetAsSmallMap();
126
127 std::array<bool, kNumMapsPerWorld> map_checked;
128 std::ranges::fill(map_checked, false);
129
130 int xx = 0;
131 int yy = 0;
132 while (true) {
133 if (int i = xx + (yy * 8); map_checked[i] == false) {
134 if (overworld_maps_[i].is_large_map()) {
135 map_checked[i] = true;
136 overworld_maps_[i].SetAsLargeMap(i, 0);
137 overworld_maps_[i + 64].SetAsLargeMap(i + 64, 0);
138
139 map_checked[i + 1] = true;
140 overworld_maps_[i + 1].SetAsLargeMap(i, 1);
141 overworld_maps_[i + 65].SetAsLargeMap(i + 64, 1);
142
143 map_checked[i + 8] = true;
144 overworld_maps_[i + 8].SetAsLargeMap(i, 2);
145 overworld_maps_[i + 72].SetAsLargeMap(i + 64, 2);
146
147 map_checked[i + 9] = true;
148 overworld_maps_[i + 9].SetAsLargeMap(i, 3);
149 overworld_maps_[i + 73].SetAsLargeMap(i + 64, 3);
150 xx++;
151 } else {
152 overworld_maps_[i].SetAsSmallMap();
153 overworld_maps_[i + 64].SetAsSmallMap();
154 map_checked[i] = true;
155 }
156 }
157
158 xx++;
159 if (xx >= 8) {
160 xx = 0;
161 yy += 1;
162 if (yy >= 8) {
163 break;
164 }
165 }
166 }
167}
168
173void Overworld::AssignMapSizes(std::vector<OverworldMap>& maps) {
174 std::vector<bool> map_checked(kNumOverworldMaps, false);
175
176 int xx = 0;
177 int yy = 0;
178 int world = 0;
179
180 while (true) {
181 int i = world + xx + (yy * 8);
182
183 if (i >= static_cast<int>(map_checked.size())) {
184 break;
185 }
186
187 if (!map_checked[i]) {
188 switch (maps[i].area_size()) {
190 map_checked[i] = true;
191 maps[i].SetAreaSize(AreaSizeEnum::SmallArea);
192 break;
193
195 map_checked[i] = true;
196 maps[i].SetAsLargeMap(i, 0);
197
198 if (i + 1 < static_cast<int>(maps.size())) {
199 map_checked[i + 1] = true;
200 maps[i + 1].SetAsLargeMap(i, 1);
201 }
202
203 if (i + 8 < static_cast<int>(maps.size())) {
204 map_checked[i + 8] = true;
205 maps[i + 8].SetAsLargeMap(i, 2);
206 }
207
208 if (i + 9 < static_cast<int>(maps.size())) {
209 map_checked[i + 9] = true;
210 maps[i + 9].SetAsLargeMap(i, 3);
211 }
212
213 xx++;
214 break;
215
217 map_checked[i] = true;
218 // CRITICAL FIX: Set parent for wide area maps
219 // Map i is parent (left), map i+1 is child (right)
220 maps[i].SetParent(i); // Parent points to itself
221 maps[i].SetAreaSize(AreaSizeEnum::WideArea);
222
223 if (i + 1 < static_cast<int>(maps.size())) {
224 map_checked[i + 1] = true;
225 maps[i + 1].SetParent(i); // Child points to parent
226 maps[i + 1].SetAreaSize(AreaSizeEnum::WideArea);
227 }
228
229 xx++;
230 break;
231
233 map_checked[i] = true;
234 // CRITICAL FIX: Set parent for tall area maps
235 // Map i is parent (top), map i+8 is child (bottom)
236 maps[i].SetParent(i); // Parent points to itself
237 maps[i].SetAreaSize(AreaSizeEnum::TallArea);
238
239 if (i + 8 < static_cast<int>(maps.size())) {
240 map_checked[i + 8] = true;
241 maps[i + 8].SetParent(i); // Child points to parent
242 maps[i + 8].SetAreaSize(AreaSizeEnum::TallArea);
243 }
244 break;
245 }
246 }
247
248 xx++;
249 if (xx >= 8) {
250 xx = 0;
251 yy += 1;
252
253 if (yy >= 8) {
254 yy = 0;
255 world += 0x40;
256 }
257 }
258 }
259}
260
261absl::Status Overworld::ConfigureMultiAreaMap(int parent_index, AreaSizeEnum size) {
262 if (parent_index < 0 || parent_index >= kNumOverworldMaps) {
263 return absl::InvalidArgumentError(
264 absl::StrFormat("Invalid parent index: %d", parent_index));
265 }
266
267 // Check ROM version
268 uint8_t asm_version = (*rom_)[OverworldCustomASMHasBeenApplied];
269
270 // Version requirements:
271 // - Vanilla (0xFF): Supports Small and Large only
272 // - v1-v2: Supports Small and Large only
273 // - v3+: Supports all 4 sizes (Small, Large, Wide, Tall)
274 if ((size == AreaSizeEnum::WideArea || size == AreaSizeEnum::TallArea) &&
275 (asm_version < 3 || asm_version == 0xFF)) {
276 return absl::FailedPreconditionError(
277 "Wide and Tall areas require ZSCustomOverworld v3+");
278 }
279
280 LOG_DEBUG("Overworld", "ConfigureMultiAreaMap: parent=%d, current_size=%d, new_size=%d, version=%d",
281 parent_index, static_cast<int>(overworld_maps_[parent_index].area_size()),
282 static_cast<int>(size), asm_version);
283
284 // CRITICAL: First, get OLD siblings (before changing) so we can reset them
285 std::vector<int> old_siblings;
286 auto old_size = overworld_maps_[parent_index].area_size();
287 int old_parent = overworld_maps_[parent_index].parent();
288
289 switch (old_size) {
291 old_siblings = {old_parent, old_parent + 1, old_parent + 8, old_parent + 9};
292 break;
294 old_siblings = {old_parent, old_parent + 1};
295 break;
297 old_siblings = {old_parent, old_parent + 8};
298 break;
299 default:
300 old_siblings = {parent_index}; // Was small, just this map
301 break;
302 }
303
304 // Reset all old siblings to SmallArea first (clean slate)
305 for (int old_sibling : old_siblings) {
306 if (old_sibling >= 0 && old_sibling < kNumOverworldMaps) {
307 overworld_maps_[old_sibling].SetAsSmallMap(old_sibling);
308 }
309 }
310
311 // Now configure NEW siblings based on requested size
312 std::vector<int> new_siblings;
313
314 switch (size) {
316 // Just configure this single map as small
317 overworld_maps_[parent_index].SetParent(parent_index);
318 overworld_maps_[parent_index].SetAreaSize(AreaSizeEnum::SmallArea);
319 new_siblings = {parent_index};
320 break;
321
323 new_siblings = {parent_index, parent_index + 1, parent_index + 8, parent_index + 9};
324 for (size_t i = 0; i < new_siblings.size(); ++i) {
325 int sibling = new_siblings[i];
326 if (sibling < 0 || sibling >= kNumOverworldMaps) continue;
327 overworld_maps_[sibling].SetAsLargeMap(parent_index, i);
328 }
329 break;
330
332 new_siblings = {parent_index, parent_index + 1};
333 for (int sibling : new_siblings) {
334 if (sibling < 0 || sibling >= kNumOverworldMaps) continue;
335 overworld_maps_[sibling].SetParent(parent_index);
336 overworld_maps_[sibling].SetAreaSize(AreaSizeEnum::WideArea);
337 }
338 break;
339
341 new_siblings = {parent_index, parent_index + 8};
342 for (int sibling : new_siblings) {
343 if (sibling < 0 || sibling >= kNumOverworldMaps) continue;
344 overworld_maps_[sibling].SetParent(parent_index);
345 overworld_maps_[sibling].SetAreaSize(AreaSizeEnum::TallArea);
346 }
347 break;
348 }
349
350 // Update ROM data for ALL affected siblings (old + new)
351 std::set<int> all_affected;
352 for (int s : old_siblings) all_affected.insert(s);
353 for (int s : new_siblings) all_affected.insert(s);
354
355 if (asm_version >= 3 && asm_version != 0xFF) {
356 // v3+: Update expanded tables
357 for (int sibling : all_affected) {
358 if (sibling < 0 || sibling >= kNumOverworldMaps) continue;
359
361 overworld_maps_[sibling].parent()));
362 RETURN_IF_ERROR(rom()->WriteByte(
363 kOverworldScreenSize + sibling,
364 static_cast<uint8_t>(overworld_maps_[sibling].area_size())));
365 }
366 } else if (asm_version < 3 && asm_version != 0xFF) {
367 // v1/v2: Update basic parent table
368 for (int sibling : all_affected) {
369 if (sibling < 0 || sibling >= kNumOverworldMaps) continue;
370
371 RETURN_IF_ERROR(rom()->WriteByte(kOverworldMapParentId + sibling,
372 overworld_maps_[sibling].parent()));
373 RETURN_IF_ERROR(rom()->WriteByte(
374 kOverworldScreenSize + (sibling & 0x3F),
375 static_cast<uint8_t>(overworld_maps_[sibling].area_size())));
376 }
377 } else {
378 // Vanilla: Update parent and screen size tables
379 for (int sibling : all_affected) {
380 if (sibling < 0 || sibling >= kNumOverworldMaps) continue;
381
382 RETURN_IF_ERROR(rom()->WriteByte(kOverworldMapParentId + sibling,
383 overworld_maps_[sibling].parent()));
384 RETURN_IF_ERROR(rom()->WriteByte(
385 kOverworldScreenSize + (sibling & 0x3F),
386 (overworld_maps_[sibling].area_size() == AreaSizeEnum::LargeArea) ? 0x00 : 0x01));
387 }
388 }
389
390 LOG_DEBUG("Overworld", "Configured %s area: parent=%d, old_siblings=%zu, new_siblings=%zu",
391 (size == AreaSizeEnum::LargeArea) ? "Large" :
392 (size == AreaSizeEnum::WideArea) ? "Wide" :
393 (size == AreaSizeEnum::TallArea) ? "Tall" : "Small",
394 parent_index, old_siblings.size(), new_siblings.size());
395
396 return absl::OkStatus();
397}
398
399absl::StatusOr<uint16_t> Overworld::GetTile16ForTile32(
400 int index, int quadrant, int dimension, const uint32_t* map32address) {
402 auto arg1, rom()->ReadByte(map32address[dimension] + quadrant + (index)));
403 ASSIGN_OR_RETURN(auto arg2,
404 rom()->ReadWord(map32address[dimension] + (index) +
405 (quadrant <= 1 ? 4 : 5)));
406 return (uint16_t)(arg1 +
407 (((arg2 >> (quadrant % 2 == 0 ? 4 : 0)) & 0x0F) * 256));
408}
409
411 constexpr int kMap32TilesLength = 0x33F0;
412 int num_tile32 = kMap32TilesLength;
413 uint32_t map32address[4] = {rom()->version_constants().kMap32TileTL,
414 rom()->version_constants().kMap32TileTR,
415 rom()->version_constants().kMap32TileBL,
416 rom()->version_constants().kMap32TileBR};
417
418 // Check if expanded tile32 data is actually present in ROM
419 // The flag position should contain 0x04 for vanilla, something else for expanded
420 uint8_t asm_version = (*rom_)[OverworldCustomASMHasBeenApplied];
421 uint8_t expanded_flag = rom()->data()[kMap32ExpandedFlagPos];
422 util::logf("Expanded tile32 flag: %d", expanded_flag);
423 if (expanded_flag != 0x04 || asm_version >= 3) {
424 // ROM has expanded tile32 data - use expanded addresses
425 map32address[0] = rom()->version_constants().kMap32TileTL;
426 map32address[1] = kMap32TileTRExpanded;
427 map32address[2] = kMap32TileBLExpanded;
428 map32address[3] = kMap32TileBRExpanded;
429 num_tile32 = kMap32TileCountExpanded;
430 expanded_tile32_ = true;
431 }
432 // Otherwise use vanilla addresses (already set above)
433
434 // Loop through each 32x32 pixel tile in the rom
435 for (int i = 0; i < num_tile32; i += 6) {
436 // Loop through each quadrant of the 32x32 pixel tile.
437 for (int k = 0; k < 4; k++) {
438 // Generate the 16-bit tile for the current quadrant of the current
439 // 32x32 pixel tile.
441 uint16_t tl,
442 GetTile16ForTile32(i, k, (int)Dimension::map32TilesTL, map32address));
444 uint16_t tr,
445 GetTile16ForTile32(i, k, (int)Dimension::map32TilesTR, map32address));
447 uint16_t bl,
448 GetTile16ForTile32(i, k, (int)Dimension::map32TilesBL, map32address));
450 uint16_t br,
451 GetTile16ForTile32(i, k, (int)Dimension::map32TilesBR, map32address));
452
453 // Add the generated 16-bit tiles to the tiles32 vector.
454 tiles32_unique_.emplace_back(gfx::Tile32(tl, tr, bl, br));
455 }
456 }
457
458 map_tiles_.light_world.resize(0x200);
459 map_tiles_.dark_world.resize(0x200);
460 map_tiles_.special_world.resize(0x200);
461 for (int i = 0; i < 0x200; i++) {
462 map_tiles_.light_world[i].resize(0x200);
463 map_tiles_.dark_world[i].resize(0x200);
464 map_tiles_.special_world[i].resize(0x200);
465 }
466
467 return absl::OkStatus();
468}
469
471 int tpos = kMap16Tiles;
472 int num_tile16 = kNumTile16Individual;
473
474 // Check if expanded tile16 data is actually present in ROM
475 // The flag position should contain 0x0F for vanilla, something else for expanded
476 uint8_t asm_version = (*rom_)[OverworldCustomASMHasBeenApplied];
477 uint8_t expanded_flag = rom()->data()[kMap16ExpandedFlagPos];
478 util::logf("Expanded tile16 flag: %d", expanded_flag);
479 if (rom()->data()[kMap16ExpandedFlagPos] == 0x0F || asm_version >= 3) {
480 // ROM has expanded tile16 data - use expanded addresses
481 tpos = kMap16TilesExpanded;
482 num_tile16 = NumberOfMap16Ex;
483 expanded_tile16_ = true;
484 }
485 // Otherwise use vanilla addresses (already set above)
486
487 for (int i = 0; i < num_tile16; i += 1) {
488 ASSIGN_OR_RETURN(auto t0_data, rom()->ReadWord(tpos));
489 gfx::TileInfo t0 = gfx::GetTilesInfo(t0_data);
490 tpos += 2;
491 ASSIGN_OR_RETURN(auto t1_data, rom()->ReadWord(tpos));
492 gfx::TileInfo t1 = gfx::GetTilesInfo(t1_data);
493 tpos += 2;
494 ASSIGN_OR_RETURN(auto t2_data, rom()->ReadWord(tpos));
495 gfx::TileInfo t2 = gfx::GetTilesInfo(t2_data);
496 tpos += 2;
497 ASSIGN_OR_RETURN(auto t3_data, rom()->ReadWord(tpos));
498 gfx::TileInfo t3 = gfx::GetTilesInfo(t3_data);
499 tpos += 2;
500 tiles16_.emplace_back(t0, t1, t2, t3);
501 }
502 return absl::OkStatus();
503}
504
505void Overworld::AssignWorldTiles(int x, int y, int sx, int sy, int tpos,
506 OverworldBlockset& world) {
507 int position_x1 = (x * 2) + (sx * 32);
508 int position_y1 = (y * 2) + (sy * 32);
509 int position_x2 = (x * 2) + 1 + (sx * 32);
510 int position_y2 = (y * 2) + 1 + (sy * 32);
511 world[position_x1][position_y1] = tiles32_unique_[tpos].tile0_;
512 world[position_x2][position_y1] = tiles32_unique_[tpos].tile1_;
513 world[position_x1][position_y2] = tiles32_unique_[tpos].tile2_;
514 world[position_x2][position_y2] = tiles32_unique_[tpos].tile3_;
515}
516
517void Overworld::OrganizeMapTiles(std::vector<uint8_t>& bytes,
518 std::vector<uint8_t>& bytes2, int i, int sx,
519 int sy, int& ttpos) {
520 for (int y = 0; y < 16; y++) {
521 for (int x = 0; x < 16; x++) {
522 auto tidD = (uint16_t)((bytes2[ttpos] << 8) + bytes[ttpos]);
523 if (int tpos = tidD; tpos < tiles32_unique_.size()) {
524 if (i < kDarkWorldMapIdStart) {
525 AssignWorldTiles(x, y, sx, sy, tpos, map_tiles_.light_world);
526 } else if (i < kSpecialWorldMapIdStart && i >= kDarkWorldMapIdStart) {
527 AssignWorldTiles(x, y, sx, sy, tpos, map_tiles_.dark_world);
528 } else {
529 AssignWorldTiles(x, y, sx, sy, tpos, map_tiles_.special_world);
530 }
531 }
532 ttpos += 1;
533 }
534 }
535}
536
538 // Keep original method for fallback/compatibility
539 // Note: This method is void, so we can't return status
540 // The parallel version will be called from Load()
541}
542
544 // For now, fall back to the original sequential implementation
545 // The parallel version has synchronization issues that cause data corruption
546 util::logf("Using sequential decompression (parallel version disabled due to data integrity issues)");
547
548 const auto get_ow_map_gfx_ptr = [this](int index, uint32_t map_ptr) {
549 int p = (rom()->data()[map_ptr + 2 + (3 * index)] << 16) +
550 (rom()->data()[map_ptr + 1 + (3 * index)] << 8) +
551 (rom()->data()[map_ptr + (3 * index)]);
552 return SnesToPc(p);
553 };
554
555 constexpr uint32_t kBaseLowest = 0x0FFFFF;
556 constexpr uint32_t kBaseHighest = 0x0F8000;
557
558 uint32_t lowest = kBaseLowest;
559 uint32_t highest = kBaseHighest;
560 int sx = 0;
561 int sy = 0;
562 int c = 0;
563
564 for (int i = 0; i < kNumOverworldMaps; i++) {
565 auto p1 = get_ow_map_gfx_ptr(
566 i, rom()->version_constants().kCompressedAllMap32PointersHigh);
567 auto p2 = get_ow_map_gfx_ptr(
568 i, rom()->version_constants().kCompressedAllMap32PointersLow);
569
570 int ttpos = 0;
571
572 if (p1 >= highest)
573 highest = p1;
574 if (p2 >= highest)
575 highest = p2;
576
577 if (p1 <= lowest && p1 > kBaseHighest)
578 lowest = p1;
579 if (p2 <= lowest && p2 > kBaseHighest)
580 lowest = p2;
581
582 int size1, size2;
583 auto bytes = gfx::HyruleMagicDecompress(rom()->data() + p2, &size1, 1);
584 auto bytes2 = gfx::HyruleMagicDecompress(rom()->data() + p1, &size2, 1);
585 OrganizeMapTiles(bytes, bytes2, i, sx, sy, ttpos);
586
587 sx++;
588 if (sx >= 8) {
589 sy++;
590 sx = 0;
591 }
592
593 c++;
594 if (c >= 64) {
595 sx = 0;
596 sy = 0;
597 c = 0;
598 }
599 }
600
601 return absl::OkStatus();
602}
603
605 auto size = tiles16_.size();
606
607 // Performance optimization: Only build essential maps initially
608 // Essential maps are the first few maps of each world that are commonly accessed
609 constexpr int kEssentialMapsPerWorld = 8; // Build first 8 maps of each world
610 constexpr int kLightWorldEssential = kEssentialMapsPerWorld;
611 constexpr int kDarkWorldEssential = kDarkWorldMapIdStart + kEssentialMapsPerWorld;
612 constexpr int kSpecialWorldEssential = kSpecialWorldMapIdStart + kEssentialMapsPerWorld;
613
614 util::logf("Building essential maps only (first %d maps per world) for faster loading", kEssentialMapsPerWorld);
615
616 std::vector<std::future<absl::Status>> futures;
617
618 // Build essential maps only
619 for (int i = 0; i < kNumOverworldMaps; ++i) {
620 bool is_essential = false;
621
622 // Check if this is an essential map
623 if (i < kLightWorldEssential) {
624 is_essential = true;
625 } else if (i >= kDarkWorldMapIdStart && i < kDarkWorldEssential) {
626 is_essential = true;
627 } else if (i >= kSpecialWorldMapIdStart && i < kSpecialWorldEssential) {
628 is_essential = true;
629 }
630
631 if (is_essential) {
632 int world_type = 0;
634 world_type = 1;
635 } else if (i >= kSpecialWorldMapIdStart) {
636 world_type = 2;
637 }
638
639 auto task_function = [this, i, size, world_type]() {
640 return overworld_maps_[i].BuildMap(size, game_state_, world_type,
641 tiles16_, GetMapTiles(world_type));
642 };
643 futures.emplace_back(std::async(std::launch::async, task_function));
644 } else {
645 // Mark non-essential maps as not built yet
646 overworld_maps_[i].SetNotBuilt();
647 }
648 }
649
650 // Wait for essential maps to complete
651 for (auto& future : futures) {
652 future.wait();
653 RETURN_IF_ERROR(future.get());
654 }
655
656 util::logf("Essential maps built. Remaining maps will be built on-demand.");
657 return absl::OkStatus();
658}
659
660absl::Status Overworld::EnsureMapBuilt(int map_index) {
661 if (map_index < 0 || map_index >= kNumOverworldMaps) {
662 return absl::InvalidArgumentError("Invalid map index");
663 }
664
665 // Check if map is already built
666 if (overworld_maps_[map_index].is_built()) {
667 return absl::OkStatus();
668 }
669
670 // Build the map on-demand
671 auto size = tiles16_.size();
672 int world_type = 0;
673 if (map_index >= kDarkWorldMapIdStart && map_index < kSpecialWorldMapIdStart) {
674 world_type = 1;
675 } else if (map_index >= kSpecialWorldMapIdStart) {
676 world_type = 2;
677 }
678
679 return overworld_maps_[map_index].BuildMap(size, game_state_, world_type,
680 tiles16_, GetMapTiles(world_type));
681}
682
684 for (int i = 0; i < kNumTileTypes; ++i) {
686 rom()->data()[rom()->version_constants().kOverworldTilesType + i];
687 }
688}
689
691 std::vector<std::future<absl::Status>> futures;
692
693 // Determine sprite table locations based on actual ASM version in ROM
694 uint8_t asm_version = (*rom_)[zelda3::OverworldCustomASMHasBeenApplied];
695
696 if (asm_version >= 3 && asm_version != 0xFF) {
697 // v3: Use expanded sprite tables
698 futures.emplace_back(std::async(std::launch::async, [this]() {
700 }));
701 futures.emplace_back(std::async(std::launch::async, [this]() {
703 }));
704 futures.emplace_back(std::async(std::launch::async, [this]() {
706 }));
707 } else {
708 // Vanilla/v2: Use original sprite tables
709 futures.emplace_back(std::async(std::launch::async, [this]() {
711 }));
712 futures.emplace_back(std::async(std::launch::async, [this]() {
714 }));
715 futures.emplace_back(std::async(std::launch::async, [this]() {
717 }));
718 }
719
720 for (auto& future : futures) {
721 future.wait();
722 RETURN_IF_ERROR(future.get());
723 }
724 return absl::OkStatus();
725}
726
727absl::Status Overworld::LoadSpritesFromMap(int sprites_per_gamestate_ptr,
728 int num_maps_per_gamestate,
729 int game_state) {
730 for (int i = 0; i < num_maps_per_gamestate; i++) {
731 if (map_parent_[i] != i)
732 continue;
733
734 int current_spr_ptr = sprites_per_gamestate_ptr + (i * 2);
735 ASSIGN_OR_RETURN(auto word_addr, rom()->ReadWord(current_spr_ptr));
736 int sprite_address = SnesToPc((0x09 << 0x10) | word_addr);
737 while (true) {
738 ASSIGN_OR_RETURN(uint8_t b1, rom()->ReadByte(sprite_address));
739 ASSIGN_OR_RETURN(uint8_t b2, rom()->ReadByte(sprite_address + 1));
740 ASSIGN_OR_RETURN(uint8_t b3, rom()->ReadByte(sprite_address + 2));
741 if (b1 == 0xFF)
742 break;
743
744 int editor_map_index = i;
745 if (game_state != 0) {
746 if (editor_map_index >= 128)
747 editor_map_index -= 128;
748 else if (editor_map_index >= 64)
749 editor_map_index -= 64;
750 }
751 int mapY = (editor_map_index / 8);
752 int mapX = (editor_map_index % 8);
753
754 int realX = ((b2 & 0x3F) * 16) + mapX * 512;
755 int realY = ((b1 & 0x3F) * 16) + mapY * 512;
756 all_sprites_[game_state].emplace_back(
757 *overworld_maps_[i].mutable_current_graphics(), (uint8_t)i, b3,
758 (uint8_t)(b2 & 0x3F), (uint8_t)(b1 & 0x3F), realX, realY);
759 all_sprites_[game_state].back().Draw();
760
761 sprite_address += 3;
762 }
763 }
764
765 return absl::OkStatus();
766}
767
791
793 util::logf("Saving Overworld Maps");
794
795 // Initialize map pointers
796 std::fill(map_pointers1_id.begin(), map_pointers1_id.end(), -1);
797 std::fill(map_pointers2_id.begin(), map_pointers2_id.end(), -1);
798
799 // Compress and save each map
801 for (int i = 0; i < kNumOverworldMaps; i++) {
802 std::vector<uint8_t> single_map_1(512);
803 std::vector<uint8_t> single_map_2(512);
804
805 // Copy tiles32 data to single_map_1 and single_map_2
806 int npos = 0;
807 for (int y = 0; y < 16; y++) {
808 for (int x = 0; x < 16; x++) {
809 auto packed = tiles32_list_[npos + (i * 256)];
810 single_map_1[npos] = packed & 0xFF; // Lower 8 bits
811 single_map_2[npos] = (packed >> 8) & 0xFF; // Next 8 bits
812 npos++;
813 }
814 }
815
816 int size_a, size_b;
817 // Compress single_map_1 and single_map_2
818 auto a = gfx::HyruleMagicCompress(single_map_1.data(), 256, &size_a, 1);
819 auto b = gfx::HyruleMagicCompress(single_map_2.data(), 256, &size_b, 1);
820 if (a.empty() || b.empty()) {
821 return absl::AbortedError("Error compressing map gfx.");
822 }
823
824 // Save compressed data and pointers
825 map_data_p1[i] = std::vector<uint8_t>(size_a);
826 map_data_p2[i] = std::vector<uint8_t>(size_b);
827
828 if ((pos + size_a) >= 0x5FE70 && (pos + size_a) <= 0x60000) {
829 pos = 0x60000;
830 }
831
832 if ((pos + size_a) >= 0x6411F && (pos + size_a) <= 0x70000) {
833 util::logf("Pos set to overflow region for map %s at %s",
834 std::to_string(i), util::HexLong(pos));
835 pos = kOverworldMapDataOverflow; // 0x0F8780;
836 }
837
838 const auto compare_array = [](const std::vector<uint8_t>& array1,
839 const std::vector<uint8_t>& array2) -> bool {
840 if (array1.size() != array2.size()) {
841 return false;
842 }
843
844 for (size_t i = 0; i < array1.size(); i++) {
845 if (array1[i] != array2[i]) {
846 return false;
847 }
848 }
849
850 return true;
851 };
852
853 for (int j = 0; j < i; j++) {
854 if (compare_array(a, map_data_p1[j])) {
855 // Reuse pointer id j for P1 (a)
856 map_pointers1_id[i] = j;
857 }
858
859 if (compare_array(b, map_data_p2[j])) {
860 map_pointers2_id[i] = j;
861 // Reuse pointer id j for P2 (b)
862 }
863 }
864
865 if (map_pointers1_id[i] == -1) {
866 // Save compressed data and pointer for map1
867 std::copy(a.begin(), a.end(), map_data_p1[i].begin());
868 int snes_pos = PcToSnes(pos);
869 map_pointers1[i] = snes_pos;
870 util::logf("Saving map pointers1 and compressed data for map %s at %s",
871 util::HexByte(i), util::HexLong(snes_pos));
872 RETURN_IF_ERROR(rom()->WriteLong(
873 rom()->version_constants().kCompressedAllMap32PointersLow + (3 * i),
874 snes_pos));
875 RETURN_IF_ERROR(rom()->WriteVector(pos, a));
876 pos += size_a;
877 } else {
878 // Save pointer for map1
879 int snes_pos = map_pointers1[map_pointers1_id[i]];
880 util::logf("Saving map pointers1 for map %s at %s", util::HexByte(i),
881 util::HexLong(snes_pos));
882 RETURN_IF_ERROR(rom()->WriteLong(
883 rom()->version_constants().kCompressedAllMap32PointersLow + (3 * i),
884 snes_pos));
885 }
886
887 if ((pos + b.size()) >= 0x5FE70 && (pos + b.size()) <= 0x60000) {
888 pos = 0x60000;
889 }
890
891 if ((pos + b.size()) >= 0x6411F && (pos + b.size()) <= 0x70000) {
892 util::logf("Pos set to overflow region for map %s at %s",
895 }
896
897 if (map_pointers2_id[i] == -1) {
898 // Save compressed data and pointer for map2
899 std::copy(b.begin(), b.end(), map_data_p2[i].begin());
900 int snes_pos = PcToSnes(pos);
901 map_pointers2[i] = snes_pos;
902 util::logf("Saving map pointers2 and compressed data for map %s at %s",
903 util::HexByte(i), util::HexLong(snes_pos));
904 RETURN_IF_ERROR(rom()->WriteLong(
905 rom()->version_constants().kCompressedAllMap32PointersHigh + (3 * i),
906 snes_pos));
907 RETURN_IF_ERROR(rom()->WriteVector(pos, b));
908 pos += size_b;
909 } else {
910 // Save pointer for map2
911 int snes_pos = map_pointers2[map_pointers2_id[i]];
912 util::logf("Saving map pointers2 for map %s at %s", util::HexByte(i),
913 util::HexLong(snes_pos));
914 RETURN_IF_ERROR(rom()->WriteLong(
915 rom()->version_constants().kCompressedAllMap32PointersHigh + (3 * i),
916 snes_pos));
917 }
918 }
919
920 // Check if too many maps data
922 util::logf("Too many maps data %s", util::HexLong(pos));
923 return absl::AbortedError("Too many maps data " + std::to_string(pos));
924 }
925
927 return absl::OkStatus();
928}
929
931 util::logf("Saving Large Maps");
932
933 // Check if this is a v3+ ROM to use expanded transition system
934 uint8_t asm_version = (*rom_)[zelda3::OverworldCustomASMHasBeenApplied];
935 bool use_expanded_transitions = (asm_version >= 3 && asm_version != 0xFF);
936
937 if (use_expanded_transitions) {
938 // Use new v3+ complex transition system with neighbor awareness
939 return SaveLargeMapsExpanded();
940 }
941
942 // Original vanilla/v2 logic preserved
943 std::vector<uint8_t> checked_map;
944
945 for (int i = 0; i < kNumMapsPerWorld; ++i) {
946 int y_pos = i / 8;
947 int x_pos = i % 8;
948 int parent_y_pos = overworld_maps_[i].parent() / 8;
949 int parent_x_pos = overworld_maps_[i].parent() % 8;
950
951 // Always write the map parent since it should not matter
953 overworld_maps_[i].parent()))
954
955 if (std::find(checked_map.begin(), checked_map.end(), i) !=
956 checked_map.end()) {
957 continue;
958 }
959
960 // If it's large then save parent pos *
961 // 0x200 otherwise pos * 0x200
962 if (overworld_maps_[i].is_large_map()) {
963 const uint8_t large_map_offsets[] = {0, 1, 8, 9};
964 for (const auto& offset : large_map_offsets) {
965 // Check 1
966 RETURN_IF_ERROR(rom()->WriteByte(kOverworldMapSize + i + offset, 0x20));
967 // Check 2
969 rom()->WriteByte(kOverworldMapSizeHighByte + i + offset, 0x03));
970 // Check 3
972 rom()->WriteByte(kOverworldScreenSize + i + offset, 0x00));
974 rom()->WriteByte(kOverworldScreenSize + i + offset + 64, 0x00));
975 // Check 4
976 RETURN_IF_ERROR(rom()->WriteByte(
977 kOverworldScreenSizeForLoading + i + offset, 0x04));
978 RETURN_IF_ERROR(rom()->WriteByte(
980 0x04));
983 0x04));
984 }
985
986 // Check 5 and 6 - transition targets
988 rom()->WriteShort(kTransitionTargetNorth + (i * 2),
989 (uint16_t)((parent_y_pos * 0x200) - 0xE0)));
991 rom()->WriteShort(kTransitionTargetWest + (i * 2),
992 (uint16_t)((parent_x_pos * 0x200) - 0x100)));
993
995 rom()->WriteShort(kTransitionTargetNorth + (i * 2) + 2,
996 (uint16_t)((parent_y_pos * 0x200) - 0xE0)));
998 rom()->WriteShort(kTransitionTargetWest + (i * 2) + 2,
999 (uint16_t)((parent_x_pos * 0x200) - 0x100)));
1000
1002 rom()->WriteShort(kTransitionTargetNorth + (i * 2) + 16,
1003 (uint16_t)((parent_y_pos * 0x200) - 0xE0)));
1005 rom()->WriteShort(kTransitionTargetWest + (i * 2) + 16,
1006 (uint16_t)((parent_x_pos * 0x200) - 0x100)));
1007
1009 rom()->WriteShort(kTransitionTargetNorth + (i * 2) + 18,
1010 (uint16_t)((parent_y_pos * 0x200) - 0xE0)));
1012 rom()->WriteShort(kTransitionTargetWest + (i * 2) + 18,
1013 (uint16_t)((parent_x_pos * 0x200) - 0x100)));
1014
1015 // Check 7 and 8 - transition positions
1016 RETURN_IF_ERROR(rom()->WriteShort(kOverworldTransitionPositionX + (i * 2),
1017 (parent_x_pos * 0x200)));
1018 RETURN_IF_ERROR(rom()->WriteShort(kOverworldTransitionPositionY + (i * 2),
1019 (parent_y_pos * 0x200)));
1020
1022 rom()->WriteShort(kOverworldTransitionPositionX + (i * 2) + 02,
1023 (parent_x_pos * 0x200)));
1025 rom()->WriteShort(kOverworldTransitionPositionY + (i * 2) + 02,
1026 (parent_y_pos * 0x200)));
1027
1029 rom()->WriteShort(kOverworldTransitionPositionX + (i * 2) + 16,
1030 (parent_x_pos * 0x200)));
1032 rom()->WriteShort(kOverworldTransitionPositionY + (i * 2) + 16,
1033 (parent_y_pos * 0x200)));
1034
1036 rom()->WriteShort(kOverworldTransitionPositionX + (i * 2) + 18,
1037 (parent_x_pos * 0x200)));
1039 rom()->WriteShort(kOverworldTransitionPositionY + (i * 2) + 18,
1040 (parent_y_pos * 0x200)));
1041
1042 // Check 9 - simple vanilla large area transitions
1043 RETURN_IF_ERROR(rom()->WriteShort(
1044 kOverworldScreenTileMapChangeByScreen1 + (i * 2) + 00, 0x0060));
1045 RETURN_IF_ERROR(rom()->WriteShort(
1046 kOverworldScreenTileMapChangeByScreen1 + (i * 2) + 02, 0x0060));
1047
1048 // If parentX == 0 then lower submaps == 0x0060 too
1049 if (parent_x_pos == 0) {
1050 RETURN_IF_ERROR(rom()->WriteShort(
1051 kOverworldScreenTileMapChangeByScreen1 + (i * 2) + 16, 0x0060));
1052 RETURN_IF_ERROR(rom()->WriteShort(
1053 kOverworldScreenTileMapChangeByScreen1 + (i * 2) + 18, 0x0060));
1054 } else {
1055 // Otherwise lower submaps == 0x1060
1056 RETURN_IF_ERROR(rom()->WriteShort(
1057 kOverworldScreenTileMapChangeByScreen1 + (i * 2) + 16, 0x1060));
1058 RETURN_IF_ERROR(rom()->WriteShort(
1059 kOverworldScreenTileMapChangeByScreen1 + (i * 2) + 18, 0x1060));
1060
1061 // If the area to the left is a large map, we don't need to add an
1062 // offset to it. otherwise leave it the same. Just to make sure where
1063 // don't try to read outside of the array.
1064 if ((i - 1) >= 0) {
1065 // If the area to the left is a large area.
1066 if (overworld_maps_[i - 1].is_large_map()) {
1067 // If the area to the left is the bottom right of a large area.
1068 if (overworld_maps_[i - 1].large_index() == 1) {
1069 RETURN_IF_ERROR(rom()->WriteShort(
1071 0x0060));
1072 }
1073 }
1074 }
1075 }
1076
1077 // Always 0x0080
1078 RETURN_IF_ERROR(rom()->WriteShort(
1079 kOverworldScreenTileMapChangeByScreen2 + (i * 2) + 00, 0x0080));
1080 RETURN_IF_ERROR(rom()->WriteShort(
1081 kOverworldScreenTileMapChangeByScreen2 + (i * 2) + 2, 0x0080));
1082 // Lower always 0x1080
1083 RETURN_IF_ERROR(rom()->WriteShort(
1084 kOverworldScreenTileMapChangeByScreen2 + (i * 2) + 16, 0x1080));
1085 RETURN_IF_ERROR(rom()->WriteShort(
1086 kOverworldScreenTileMapChangeByScreen2 + (i * 2) + 18, 0x1080));
1087
1088 // If the area to the right is a large map, we don't need to add an offset
1089 // to it. otherwise leave it the same. Just to make sure where don't try
1090 // to read outside of the array.
1091 if ((i + 2) < 64) {
1092 // If the area to the right is a large area.
1093 if (overworld_maps_[i + 2].is_large_map()) {
1094 // If the area to the right is the top left of a large area.
1095 if (overworld_maps_[i + 2].large_index() == 0) {
1096 RETURN_IF_ERROR(rom()->WriteShort(
1097 kOverworldScreenTileMapChangeByScreen2 + (i * 2) + 18, 0x0080));
1098 }
1099 }
1100 }
1101
1102 // Always 0x1800
1103 RETURN_IF_ERROR(rom()->WriteShort(
1104 kOverworldScreenTileMapChangeByScreen3 + (i * 2), 0x1800));
1105 RETURN_IF_ERROR(rom()->WriteShort(
1106 kOverworldScreenTileMapChangeByScreen3 + (i * 2) + 16, 0x1800));
1107 // Right side is always 0x1840
1108 RETURN_IF_ERROR(rom()->WriteShort(
1109 kOverworldScreenTileMapChangeByScreen3 + (i * 2) + 2, 0x1840));
1110 RETURN_IF_ERROR(rom()->WriteShort(
1111 kOverworldScreenTileMapChangeByScreen3 + (i * 2) + 18, 0x1840));
1112
1113 // If the area above is a large map, we don't need to add an offset to it.
1114 // otherwise leave it the same.
1115 // Just to make sure where don't try to read outside of the array.
1116 if (i - 8 >= 0) {
1117 // If the area just above us is a large area.
1118 if (overworld_maps_[i - 8].is_large_map()) {
1119 // If the area just above us is the bottom left of a large area.
1120 if (overworld_maps_[i - 8].large_index() == 2) {
1121 RETURN_IF_ERROR(rom()->WriteShort(
1122 kOverworldScreenTileMapChangeByScreen3 + (i * 2) + 02, 0x1800));
1123 }
1124 }
1125 }
1126
1127 // Always 0x2000
1128 RETURN_IF_ERROR(rom()->WriteShort(
1129 kOverworldScreenTileMapChangeByScreen4 + (i * 2) + 00, 0x2000));
1130 RETURN_IF_ERROR(rom()->WriteShort(
1131 kOverworldScreenTileMapChangeByScreen4 + (i * 2) + 16, 0x2000));
1132 // Right side always 0x2040
1133 RETURN_IF_ERROR(rom()->WriteShort(
1134 kOverworldScreenTileMapChangeByScreen4 + (i * 2) + 2, 0x2040));
1135 RETURN_IF_ERROR(rom()->WriteShort(
1136 kOverworldScreenTileMapChangeByScreen4 + (i * 2) + 18, 0x2040));
1137
1138 // If the area below is a large map, we don't need to add an offset to it.
1139 // otherwise leave it the same.
1140 // Just to make sure where don't try to read outside of the array.
1141 if (i + 16 < 64) {
1142 // If the area just below us is a large area.
1143 if (overworld_maps_[i + 16].is_large_map()) {
1144 // If the area just below us is the top left of a large area.
1145 if (overworld_maps_[i + 16].large_index() == 0) {
1146 RETURN_IF_ERROR(rom()->WriteShort(
1147 kOverworldScreenTileMapChangeByScreen4 + (i * 2) + 18, 0x2000));
1148 }
1149 }
1150 }
1151
1152 checked_map.emplace_back(i);
1153 checked_map.emplace_back((i + 1));
1154 checked_map.emplace_back((i + 8));
1155 checked_map.emplace_back((i + 9));
1156
1157 } else {
1158 RETURN_IF_ERROR(rom()->WriteByte(kOverworldMapSize + i, 0x00));
1159 RETURN_IF_ERROR(rom()->WriteByte(kOverworldMapSizeHighByte + i, 0x01));
1160
1161 RETURN_IF_ERROR(rom()->WriteByte(kOverworldScreenSize + i, 0x01));
1162 RETURN_IF_ERROR(rom()->WriteByte(kOverworldScreenSize + i + 64, 0x01));
1163
1165 rom()->WriteByte(kOverworldScreenSizeForLoading + i, 0x02));
1166 RETURN_IF_ERROR(rom()->WriteByte(
1168 RETURN_IF_ERROR(rom()->WriteByte(
1170
1171 RETURN_IF_ERROR(rom()->WriteShort(
1172 kOverworldScreenTileMapChangeByScreen1 + (i * 2), 0x0060));
1173
1174 // If the area to the left is a large map, we don't need to add an offset
1175 // to it. otherwise leave it the same.
1176 // Just to make sure where don't try to read outside of the array.
1177 if (i - 1 >= 0 && parent_x_pos != 0) {
1178 if (overworld_maps_[i - 1].is_large_map()) {
1179 if (overworld_maps_[i - 1].large_index() == 3) {
1180 RETURN_IF_ERROR(rom()->WriteShort(
1181 kOverworldScreenTileMapChangeByScreen1 + (i * 2), 0xF060));
1182 }
1183 }
1184 }
1185
1186 RETURN_IF_ERROR(rom()->WriteShort(
1187 kOverworldScreenTileMapChangeByScreen2 + (i * 2), 0x0040));
1188
1189 if (i + 1 < 64 && parent_x_pos != 7) {
1190 if (overworld_maps_[i + 1].is_large_map()) {
1191 if (overworld_maps_[i + 1].large_index() == 2) {
1192 RETURN_IF_ERROR(rom()->WriteShort(
1193 kOverworldScreenTileMapChangeByScreen2 + (i * 2), 0xF040));
1194 }
1195 }
1196 }
1197
1198 RETURN_IF_ERROR(rom()->WriteShort(
1199 kOverworldScreenTileMapChangeByScreen3 + (i * 2), 0x1800));
1200
1201 // If the area above is a large map, we don't need to add an offset to it.
1202 // otherwise leave it the same.
1203 // Just to make sure where don't try to read outside of the array.
1204 if (i - 8 >= 0) {
1205 // If the area just above us is a large area.
1206 if (overworld_maps_[i - 8].is_large_map()) {
1207 // If we are under the bottom right of the large area.
1208 if (overworld_maps_[i - 8].large_index() == 3) {
1209 RETURN_IF_ERROR(rom()->WriteShort(
1210 kOverworldScreenTileMapChangeByScreen3 + (i * 2), 0x17C0));
1211 }
1212 }
1213 }
1214
1215 RETURN_IF_ERROR(rom()->WriteShort(
1216 kOverworldScreenTileMapChangeByScreen4 + (i * 2), 0x1000));
1217
1218 // If the area below is a large map, we don't need to add an offset to it.
1219 // otherwise leave it the same.
1220 // Just to make sure where don't try to read outside of the array.
1221 if (i + 8 < 64) {
1222 // If the area just below us is a large area.
1223 if (overworld_maps_[i + 8].is_large_map()) {
1224 // If we are on top of the top right of the large area.
1225 if (overworld_maps_[i + 8].large_index() == 1) {
1226 RETURN_IF_ERROR(rom()->WriteShort(
1227 kOverworldScreenTileMapChangeByScreen4 + (i * 2), 0x0FC0));
1228 }
1229 }
1230 }
1231
1232 RETURN_IF_ERROR(rom()->WriteShort(kTransitionTargetNorth + (i * 2),
1233 (uint16_t)((y_pos * 0x200) - 0xE0)));
1234 RETURN_IF_ERROR(rom()->WriteShort(kTransitionTargetWest + (i * 2),
1235 (uint16_t)((x_pos * 0x200) - 0x100)));
1236
1237 RETURN_IF_ERROR(rom()->WriteShort(kOverworldTransitionPositionX + (i * 2),
1238 (x_pos * 0x200)));
1239 RETURN_IF_ERROR(rom()->WriteShort(kOverworldTransitionPositionY + (i * 2),
1240 (y_pos * 0x200)));
1241
1242 checked_map.emplace_back(i);
1243 }
1244 }
1245
1246 constexpr int OverworldScreenTileMapChangeMask = 0x1262C;
1247
1249 rom()->WriteShort(OverworldScreenTileMapChangeMask + 0, 0x1F80));
1251 rom()->WriteShort(OverworldScreenTileMapChangeMask + 2, 0x1F80));
1253 rom()->WriteShort(OverworldScreenTileMapChangeMask + 4, 0x007F));
1255 rom()->WriteShort(OverworldScreenTileMapChangeMask + 6, 0x007F));
1256
1257 return absl::OkStatus();
1258}
1259
1261 int i, int parent_x_pos, int parent_y_pos, int transition_target_north,
1262 int transition_target_west, int transition_pos_x, int transition_pos_y,
1263 int screen_change_1, int screen_change_2, int screen_change_3,
1264 int screen_change_4) {
1265 // Set basic transition targets
1267 rom()->WriteShort(transition_target_north + (i * 2),
1268 (uint16_t)((parent_y_pos * 0x0200) - 0x00E0)));
1270 rom()->WriteShort(transition_target_west + (i * 2),
1271 (uint16_t)((parent_x_pos * 0x0200) - 0x0100)));
1272
1274 rom()->WriteShort(transition_pos_x + (i * 2), parent_x_pos * 0x0200));
1276 rom()->WriteShort(transition_pos_y + (i * 2), parent_y_pos * 0x0200));
1277
1278 // byScreen1 = Transitioning right
1279 uint16_t by_screen1_small = 0x0060;
1280
1281 // Check west neighbor for transition adjustments
1282 if ((i % 0x40) - 1 >= 0) {
1283 auto& west_neighbor = overworld_maps_[i - 1];
1284
1285 // Transition from bottom right quadrant of large area to small area
1286 if (west_neighbor.area_size() == AreaSizeEnum::LargeArea &&
1287 west_neighbor.large_index() == 3) {
1288 by_screen1_small = 0xF060;
1289 }
1290 // Transition from bottom quadrant of tall area to small area
1291 else if (west_neighbor.area_size() == AreaSizeEnum::TallArea &&
1292 west_neighbor.large_index() == 2) {
1293 by_screen1_small = 0xF060;
1294 }
1295 }
1296
1298 rom()->WriteShort(screen_change_1 + (i * 2), by_screen1_small));
1299
1300 // byScreen2 = Transitioning left
1301 uint16_t by_screen2_small = 0x0040;
1302
1303 // Check east neighbor for transition adjustments
1304 if ((i % 0x40) + 1 < 0x40 && i + 1 < kNumOverworldMaps) {
1305 auto& east_neighbor = overworld_maps_[i + 1];
1306
1307 // Transition from bottom left quadrant of large area to small area
1308 if (east_neighbor.area_size() == AreaSizeEnum::LargeArea &&
1309 east_neighbor.large_index() == 2) {
1310 by_screen2_small = 0xF040;
1311 }
1312 // Transition from bottom quadrant of tall area to small area
1313 else if (east_neighbor.area_size() == AreaSizeEnum::TallArea &&
1314 east_neighbor.large_index() == 2) {
1315 by_screen2_small = 0xF040;
1316 }
1317 }
1318
1320 rom()->WriteShort(screen_change_2 + (i * 2), by_screen2_small));
1321
1322 // byScreen3 = Transitioning down
1323 uint16_t by_screen3_small = 0x1800;
1324
1325 // Check north neighbor for transition adjustments
1326 if ((i % 0x40) - 8 >= 0) {
1327 auto& north_neighbor = overworld_maps_[i - 8];
1328
1329 // Transition from bottom right quadrant of large area to small area
1330 if (north_neighbor.area_size() == AreaSizeEnum::LargeArea &&
1331 north_neighbor.large_index() == 3) {
1332 by_screen3_small = 0x17C0;
1333 }
1334 // Transition from right quadrant of wide area to small area
1335 else if (north_neighbor.area_size() == AreaSizeEnum::WideArea &&
1336 north_neighbor.large_index() == 1) {
1337 by_screen3_small = 0x17C0;
1338 }
1339 }
1340
1342 rom()->WriteShort(screen_change_3 + (i * 2), by_screen3_small));
1343
1344 // byScreen4 = Transitioning up
1345 uint16_t by_screen4_small = 0x1000;
1346
1347 // Check south neighbor for transition adjustments
1348 if ((i % 0x40) + 8 < 0x40 && i + 8 < kNumOverworldMaps) {
1349 auto& south_neighbor = overworld_maps_[i + 8];
1350
1351 // Transition from top right quadrant of large area to small area
1352 if (south_neighbor.area_size() == AreaSizeEnum::LargeArea &&
1353 south_neighbor.large_index() == 1) {
1354 by_screen4_small = 0x0FC0;
1355 }
1356 // Transition from right quadrant of wide area to small area
1357 else if (south_neighbor.area_size() == AreaSizeEnum::WideArea &&
1358 south_neighbor.large_index() == 1) {
1359 by_screen4_small = 0x0FC0;
1360 }
1361 }
1362
1364 rom()->WriteShort(screen_change_4 + (i * 2), by_screen4_small));
1365
1366 return absl::OkStatus();
1367}
1368
1370 int i, int parent_x_pos, int parent_y_pos, int transition_target_north,
1371 int transition_target_west, int transition_pos_x, int transition_pos_y,
1372 int screen_change_1, int screen_change_2, int screen_change_3,
1373 int screen_change_4) {
1374 // Set transition targets for all 4 quadrants
1375 const uint16_t offsets[] = {0, 2, 16, 18};
1376 for (auto offset : offsets) {
1378 rom()->WriteShort(transition_target_north + (i * 2) + offset,
1379 (uint16_t)((parent_y_pos * 0x0200) - 0x00E0)));
1381 rom()->WriteShort(transition_target_west + (i * 2) + offset,
1382 (uint16_t)((parent_x_pos * 0x0200) - 0x0100)));
1383 RETURN_IF_ERROR(rom()->WriteShort(transition_pos_x + (i * 2) + offset,
1384 parent_x_pos * 0x0200));
1385 RETURN_IF_ERROR(rom()->WriteShort(transition_pos_y + (i * 2) + offset,
1386 parent_y_pos * 0x0200));
1387 }
1388
1389 // Complex neighbor-aware transition calculations for large areas
1390 // byScreen1 = Transitioning right
1391 std::array<uint16_t, 4> by_screen1_large = {0x0060, 0x0060, 0x1060, 0x1060};
1392
1393 // Check west neighbor
1394 if ((i % 0x40) - 1 >= 0) {
1395 auto& west_neighbor = overworld_maps_[i - 1];
1396
1397 if (west_neighbor.area_size() == AreaSizeEnum::LargeArea) {
1398 switch (west_neighbor.large_index()) {
1399 case 1: // From bottom right to bottom left of large area
1400 by_screen1_large[2] = 0x0060;
1401 break;
1402 case 3: // From bottom right to top left of large area
1403 by_screen1_large[0] = 0xF060;
1404 break;
1405 }
1406 } else if (west_neighbor.area_size() == AreaSizeEnum::TallArea) {
1407 switch (west_neighbor.large_index()) {
1408 case 0: // From bottom of tall to bottom left of large
1409 by_screen1_large[2] = 0x0060;
1410 break;
1411 case 2: // From bottom of tall to top left of large
1412 by_screen1_large[0] = 0xF060;
1413 break;
1414 }
1415 }
1416 }
1417
1418 for (int j = 0; j < 4; j++) {
1419 RETURN_IF_ERROR(rom()->WriteShort(screen_change_1 + (i * 2) + offsets[j],
1420 by_screen1_large[j]));
1421 }
1422
1423 // byScreen2 = Transitioning left
1424 std::array<uint16_t, 4> by_screen2_large = {0x0080, 0x0080, 0x1080, 0x1080};
1425
1426 // Check east neighbor
1427 if ((i % 0x40) + 2 < 0x40 && i + 2 < kNumOverworldMaps) {
1428 auto& east_neighbor = overworld_maps_[i + 2];
1429
1430 if (east_neighbor.area_size() == AreaSizeEnum::LargeArea) {
1431 switch (east_neighbor.large_index()) {
1432 case 0: // From bottom left to bottom right of large area
1433 by_screen2_large[3] = 0x0080;
1434 break;
1435 case 2: // From bottom left to top right of large area
1436 by_screen2_large[1] = 0xF080;
1437 break;
1438 }
1439 } else if (east_neighbor.area_size() == AreaSizeEnum::TallArea) {
1440 switch (east_neighbor.large_index()) {
1441 case 0: // From bottom of tall to bottom right of large
1442 by_screen2_large[3] = 0x0080;
1443 break;
1444 case 2: // From bottom of tall to top right of large
1445 by_screen2_large[1] = 0xF080;
1446 break;
1447 }
1448 }
1449 }
1450
1451 for (int j = 0; j < 4; j++) {
1452 RETURN_IF_ERROR(rom()->WriteShort(screen_change_2 + (i * 2) + offsets[j],
1453 by_screen2_large[j]));
1454 }
1455
1456 // byScreen3 = Transitioning down
1457 std::array<uint16_t, 4> by_screen3_large = {0x1800, 0x1840, 0x1800, 0x1840};
1458
1459 // Check north neighbor
1460 if ((i % 0x40) - 8 >= 0) {
1461 auto& north_neighbor = overworld_maps_[i - 8];
1462
1463 if (north_neighbor.area_size() == AreaSizeEnum::LargeArea) {
1464 switch (north_neighbor.large_index()) {
1465 case 2: // From bottom right to top right of large area
1466 by_screen3_large[1] = 0x1800;
1467 break;
1468 case 3: // From bottom right to top left of large area
1469 by_screen3_large[0] = 0x17C0;
1470 break;
1471 }
1472 } else if (north_neighbor.area_size() == AreaSizeEnum::WideArea) {
1473 switch (north_neighbor.large_index()) {
1474 case 0: // From right of wide to top right of large
1475 by_screen3_large[1] = 0x1800;
1476 break;
1477 case 1: // From right of wide to top left of large
1478 by_screen3_large[0] = 0x17C0;
1479 break;
1480 }
1481 }
1482 }
1483
1484 for (int j = 0; j < 4; j++) {
1485 RETURN_IF_ERROR(rom()->WriteShort(screen_change_3 + (i * 2) + offsets[j],
1486 by_screen3_large[j]));
1487 }
1488
1489 // byScreen4 = Transitioning up
1490 std::array<uint16_t, 4> by_screen4_large = {0x2000, 0x2040, 0x2000, 0x2040};
1491
1492 // Check south neighbor
1493 if ((i % 0x40) + 16 < 0x40 && i + 16 < kNumOverworldMaps) {
1494 auto& south_neighbor = overworld_maps_[i + 16];
1495
1496 if (south_neighbor.area_size() == AreaSizeEnum::LargeArea) {
1497 switch (south_neighbor.large_index()) {
1498 case 0: // From top right to bottom right of large area
1499 by_screen4_large[3] = 0x2000;
1500 break;
1501 case 1: // From top right to bottom left of large area
1502 by_screen4_large[2] = 0x1FC0;
1503 break;
1504 }
1505 } else if (south_neighbor.area_size() == AreaSizeEnum::WideArea) {
1506 switch (south_neighbor.large_index()) {
1507 case 0: // From right of wide to bottom right of large
1508 by_screen4_large[3] = 0x2000;
1509 break;
1510 case 1: // From right of wide to bottom left of large
1511 by_screen4_large[2] = 0x1FC0;
1512 break;
1513 }
1514 }
1515 }
1516
1517 for (int j = 0; j < 4; j++) {
1518 RETURN_IF_ERROR(rom()->WriteShort(screen_change_4 + (i * 2) + offsets[j],
1519 by_screen4_large[j]));
1520 }
1521
1522 return absl::OkStatus();
1523}
1524
1526 int i, int parent_x_pos, int parent_y_pos, int transition_target_north,
1527 int transition_target_west, int transition_pos_x, int transition_pos_y,
1528 int screen_change_1, int screen_change_2, int screen_change_3,
1529 int screen_change_4) {
1530 // Set transition targets for both quadrants
1531 const uint16_t offsets[] = {0, 2};
1532 for (auto offset : offsets) {
1534 rom()->WriteShort(transition_target_north + (i * 2) + offset,
1535 (uint16_t)((parent_y_pos * 0x0200) - 0x00E0)));
1537 rom()->WriteShort(transition_target_west + (i * 2) + offset,
1538 (uint16_t)((parent_x_pos * 0x0200) - 0x0100)));
1539 RETURN_IF_ERROR(rom()->WriteShort(transition_pos_x + (i * 2) + offset,
1540 parent_x_pos * 0x0200));
1541 RETURN_IF_ERROR(rom()->WriteShort(transition_pos_y + (i * 2) + offset,
1542 parent_y_pos * 0x0200));
1543 }
1544
1545 // byScreen1 = Transitioning right
1546 std::array<uint16_t, 2> by_screen1_wide = {0x0060, 0x0060};
1547
1548 // Check west neighbor
1549 if ((i % 0x40) - 1 >= 0) {
1550 auto& west_neighbor = overworld_maps_[i - 1];
1551
1552 // From bottom right of large to left of wide
1553 if (west_neighbor.area_size() == AreaSizeEnum::LargeArea &&
1554 west_neighbor.large_index() == 3) {
1555 by_screen1_wide[0] = 0xF060;
1556 }
1557 // From bottom of tall to left of wide
1558 else if (west_neighbor.area_size() == AreaSizeEnum::TallArea &&
1559 west_neighbor.large_index() == 2) {
1560 by_screen1_wide[0] = 0xF060;
1561 }
1562 }
1563
1564 for (int j = 0; j < 2; j++) {
1565 RETURN_IF_ERROR(rom()->WriteShort(screen_change_1 + (i * 2) + offsets[j],
1566 by_screen1_wide[j]));
1567 }
1568
1569 // byScreen2 = Transitioning left
1570 std::array<uint16_t, 2> by_screen2_wide = {0x0080, 0x0080};
1571
1572 // Check east neighbor
1573 if ((i % 0x40) + 2 < 0x40 && i + 2 < kNumOverworldMaps) {
1574 auto& east_neighbor = overworld_maps_[i + 2];
1575
1576 // From bottom left of large to right of wide
1577 if (east_neighbor.area_size() == AreaSizeEnum::LargeArea &&
1578 east_neighbor.large_index() == 2) {
1579 by_screen2_wide[1] = 0xF080;
1580 }
1581 // From bottom of tall to right of wide
1582 else if (east_neighbor.area_size() == AreaSizeEnum::TallArea &&
1583 east_neighbor.large_index() == 2) {
1584 by_screen2_wide[1] = 0xF080;
1585 }
1586 }
1587
1588 for (int j = 0; j < 2; j++) {
1589 RETURN_IF_ERROR(rom()->WriteShort(screen_change_2 + (i * 2) + offsets[j],
1590 by_screen2_wide[j]));
1591 }
1592
1593 // byScreen3 = Transitioning down
1594 std::array<uint16_t, 2> by_screen3_wide = {0x1800, 0x1840};
1595
1596 // Check north neighbor
1597 if ((i % 0x40) - 8 >= 0) {
1598 auto& north_neighbor = overworld_maps_[i - 8];
1599
1600 if (north_neighbor.area_size() == AreaSizeEnum::LargeArea) {
1601 switch (north_neighbor.large_index()) {
1602 case 2: // From bottom right of large to right of wide
1603 by_screen3_wide[1] = 0x1800;
1604 break;
1605 case 3: // From bottom right of large to left of wide
1606 by_screen3_wide[0] = 0x17C0;
1607 break;
1608 }
1609 } else if (north_neighbor.area_size() == AreaSizeEnum::WideArea) {
1610 switch (north_neighbor.large_index()) {
1611 case 0: // From right of wide to right of wide
1612 by_screen3_wide[1] = 0x1800;
1613 break;
1614 case 1: // From right of wide to left of wide
1615 by_screen3_wide[0] = 0x07C0;
1616 break;
1617 }
1618 }
1619 }
1620
1621 for (int j = 0; j < 2; j++) {
1622 RETURN_IF_ERROR(rom()->WriteShort(screen_change_3 + (i * 2) + offsets[j],
1623 by_screen3_wide[j]));
1624 }
1625
1626 // byScreen4 = Transitioning up
1627 std::array<uint16_t, 2> by_screen4_wide = {0x1000, 0x1040};
1628
1629 // Check south neighbor
1630 if ((i % 0x40) + 8 < 0x40 && i + 8 < kNumOverworldMaps) {
1631 auto& south_neighbor = overworld_maps_[i + 8];
1632
1633 if (south_neighbor.area_size() == AreaSizeEnum::LargeArea) {
1634 switch (south_neighbor.large_index()) {
1635 case 0: // From top right of large to right of wide
1636 by_screen4_wide[1] = 0x1000;
1637 break;
1638 case 1: // From top right of large to left of wide
1639 by_screen4_wide[0] = 0x0FC0;
1640 break;
1641 }
1642 } else if (south_neighbor.area_size() == AreaSizeEnum::WideArea) {
1643 if (south_neighbor.large_index() == 1) {
1644 by_screen4_wide[0] = 0x0FC0;
1645 }
1646 switch (south_neighbor.large_index()) {
1647 case 0: // From right of wide to right of wide
1648 by_screen4_wide[1] = 0x1000;
1649 break;
1650 case 1: // From right of wide to left of wide
1651 by_screen4_wide[0] = 0x0FC0;
1652 break;
1653 }
1654 }
1655 }
1656
1657 for (int j = 0; j < 2; j++) {
1658 RETURN_IF_ERROR(rom()->WriteShort(screen_change_4 + (i * 2) + offsets[j],
1659 by_screen4_wide[j]));
1660 }
1661
1662 return absl::OkStatus();
1663}
1664
1666 int i, int parent_x_pos, int parent_y_pos, int transition_target_north,
1667 int transition_target_west, int transition_pos_x, int transition_pos_y,
1668 int screen_change_1, int screen_change_2, int screen_change_3,
1669 int screen_change_4) {
1670 // Set transition targets for both quadrants
1671 const uint16_t offsets[] = {0, 16};
1672 for (auto offset : offsets) {
1674 rom()->WriteShort(transition_target_north + (i * 2) + offset,
1675 (uint16_t)((parent_y_pos * 0x0200) - 0x00E0)));
1677 rom()->WriteShort(transition_target_west + (i * 2) + offset,
1678 (uint16_t)((parent_x_pos * 0x0200) - 0x0100)));
1679 RETURN_IF_ERROR(rom()->WriteShort(transition_pos_x + (i * 2) + offset,
1680 parent_x_pos * 0x0200));
1681 RETURN_IF_ERROR(rom()->WriteShort(transition_pos_y + (i * 2) + offset,
1682 parent_y_pos * 0x0200));
1683 }
1684
1685 // byScreen1 = Transitioning right
1686 std::array<uint16_t, 2> by_screen1_tall = {0x0060, 0x1060};
1687
1688 // Check west neighbor
1689 if ((i % 0x40) - 1 >= 0) {
1690 auto& west_neighbor = overworld_maps_[i - 1];
1691
1692 if (west_neighbor.area_size() == AreaSizeEnum::LargeArea) {
1693 switch (west_neighbor.large_index()) {
1694 case 1: // From bottom right of large to bottom of tall
1695 by_screen1_tall[1] = 0x0060;
1696 break;
1697 case 3: // From bottom right of large to top of tall
1698 by_screen1_tall[0] = 0xF060;
1699 break;
1700 }
1701 } else if (west_neighbor.area_size() == AreaSizeEnum::TallArea) {
1702 switch (west_neighbor.large_index()) {
1703 case 0: // From bottom of tall to bottom of tall
1704 by_screen1_tall[1] = 0x0060;
1705 break;
1706 case 2: // From bottom of tall to top of tall
1707 by_screen1_tall[0] = 0xF060;
1708 break;
1709 }
1710 }
1711 }
1712
1713 for (int j = 0; j < 2; j++) {
1714 RETURN_IF_ERROR(rom()->WriteShort(screen_change_1 + (i * 2) + offsets[j],
1715 by_screen1_tall[j]));
1716 }
1717
1718 // byScreen2 = Transitioning left
1719 std::array<uint16_t, 2> by_screen2_tall = {0x0040, 0x1040};
1720
1721 // Check east neighbor
1722 if ((i % 0x40) + 1 < 0x40 && i + 1 < kNumOverworldMaps) {
1723 auto& east_neighbor = overworld_maps_[i + 1];
1724
1725 if (east_neighbor.area_size() == AreaSizeEnum::LargeArea) {
1726 switch (east_neighbor.large_index()) {
1727 case 0: // From bottom left of large to bottom of tall
1728 by_screen2_tall[1] = 0x0040;
1729 break;
1730 case 2: // From bottom left of large to top of tall
1731 by_screen2_tall[0] = 0xF040;
1732 break;
1733 }
1734 } else if (east_neighbor.area_size() == AreaSizeEnum::TallArea) {
1735 switch (east_neighbor.large_index()) {
1736 case 0: // From bottom of tall to bottom of tall
1737 by_screen2_tall[1] = 0x0040;
1738 break;
1739 case 2: // From bottom of tall to top of tall
1740 by_screen2_tall[0] = 0xF040;
1741 break;
1742 }
1743 }
1744 }
1745
1746 for (int j = 0; j < 2; j++) {
1747 RETURN_IF_ERROR(rom()->WriteShort(screen_change_2 + (i * 2) + offsets[j],
1748 by_screen2_tall[j]));
1749 }
1750
1751 // byScreen3 = Transitioning down
1752 std::array<uint16_t, 2> by_screen3_tall = {0x1800, 0x1800};
1753
1754 // Check north neighbor
1755 if ((i % 0x40) - 8 >= 0) {
1756 auto& north_neighbor = overworld_maps_[i - 8];
1757
1758 // From bottom right of large to top of tall
1759 if (north_neighbor.area_size() == AreaSizeEnum::LargeArea &&
1760 north_neighbor.large_index() == 3) {
1761 by_screen3_tall[0] = 0x17C0;
1762 }
1763 // From right of wide to top of tall
1764 else if (north_neighbor.area_size() == AreaSizeEnum::WideArea &&
1765 north_neighbor.large_index() == 1) {
1766 by_screen3_tall[0] = 0x17C0;
1767 }
1768 }
1769
1770 for (int j = 0; j < 2; j++) {
1771 RETURN_IF_ERROR(rom()->WriteShort(screen_change_3 + (i * 2) + offsets[j],
1772 by_screen3_tall[j]));
1773 }
1774
1775 // byScreen4 = Transitioning up
1776 std::array<uint16_t, 2> by_screen4_tall = {0x2000, 0x2000};
1777
1778 // Check south neighbor
1779 if ((i % 0x40) + 16 < 0x40 && i + 16 < kNumOverworldMaps) {
1780 auto& south_neighbor = overworld_maps_[i + 16];
1781
1782 // From top right of large to bottom of tall
1783 if (south_neighbor.area_size() == AreaSizeEnum::LargeArea &&
1784 south_neighbor.large_index() == 1) {
1785 by_screen4_tall[1] = 0x1FC0;
1786 }
1787 // From right of wide to bottom of tall
1788 else if (south_neighbor.area_size() == AreaSizeEnum::WideArea &&
1789 south_neighbor.large_index() == 1) {
1790 by_screen4_tall[1] = 0x1FC0;
1791 }
1792 }
1793
1794 for (int j = 0; j < 2; j++) {
1795 RETURN_IF_ERROR(rom()->WriteShort(screen_change_4 + (i * 2) + offsets[j],
1796 by_screen4_tall[j]));
1797 }
1798
1799 return absl::OkStatus();
1800}
1801
1803 util::logf("Saving Large Maps (v3+ Expanded)");
1804
1805 // Use expanded memory locations for v3+
1806 int transition_target_north = zelda3::transition_target_northExpanded;
1807 int transition_target_west = zelda3::transition_target_westExpanded;
1808 int transition_pos_x = zelda3::kOverworldTransitionPositionXExpanded;
1809 int transition_pos_y = zelda3::kOverworldTransitionPositionYExpanded;
1814
1815 std::vector<uint8_t> checked_map;
1816
1817 // Process all overworld maps (0xA0 for v3)
1818 for (int i = 0; i < kNumOverworldMaps; ++i) {
1819 // Skip if this map was already processed as part of a multi-area structure
1820 if (std::find(checked_map.begin(), checked_map.end(), i) !=
1821 checked_map.end()) {
1822 continue;
1823 }
1824
1825 int parent_y_pos = (overworld_maps_[i].parent() % 0x40) / 8;
1826 int parent_x_pos = (overworld_maps_[i].parent() % 0x40) % 8;
1827
1828 // Write the map parent ID to expanded parent table
1830 overworld_maps_[i].parent()));
1831
1832 // Handle transitions based on area size
1833 switch (overworld_maps_[i].area_size()) {
1836 i, parent_x_pos, parent_y_pos, transition_target_north,
1837 transition_target_west, transition_pos_x, transition_pos_y,
1838 screen_change_1, screen_change_2, screen_change_3,
1839 screen_change_4));
1840 checked_map.emplace_back(i);
1841 break;
1842
1845 i, parent_x_pos, parent_y_pos, transition_target_north,
1846 transition_target_west, transition_pos_x, transition_pos_y,
1847 screen_change_1, screen_change_2, screen_change_3,
1848 screen_change_4));
1849 // Mark all 4 quadrants as processed
1850 checked_map.emplace_back(i);
1851 checked_map.emplace_back(i + 1);
1852 checked_map.emplace_back(i + 8);
1853 checked_map.emplace_back(i + 9);
1854 break;
1855
1858 i, parent_x_pos, parent_y_pos, transition_target_north,
1859 transition_target_west, transition_pos_x, transition_pos_y,
1860 screen_change_1, screen_change_2, screen_change_3,
1861 screen_change_4));
1862 // Mark both horizontal quadrants as processed
1863 checked_map.emplace_back(i);
1864 checked_map.emplace_back(i + 1);
1865 break;
1866
1869 i, parent_x_pos, parent_y_pos, transition_target_north,
1870 transition_target_west, transition_pos_x, transition_pos_y,
1871 screen_change_1, screen_change_2, screen_change_3,
1872 screen_change_4));
1873 // Mark both vertical quadrants as processed
1874 checked_map.emplace_back(i);
1875 checked_map.emplace_back(i + 8);
1876 break;
1877 }
1878 }
1879
1880 return absl::OkStatus();
1881}
1882
1883namespace {
1884std::vector<uint64_t> GetAllTile16(OverworldMapTiles& map_tiles_) {
1885 std::vector<uint64_t> all_tile_16; // Ensure it's 64 bits
1886
1887 int sx = 0;
1888 int sy = 0;
1889 int c = 0;
1890 OverworldBlockset tiles_used;
1891 for (int i = 0; i < kNumOverworldMaps; i++) {
1892 if (i < kDarkWorldMapIdStart) {
1893 tiles_used = map_tiles_.light_world;
1894 } else if (i < kSpecialWorldMapIdStart && i >= kDarkWorldMapIdStart) {
1895 tiles_used = map_tiles_.dark_world;
1896 } else {
1897 tiles_used = map_tiles_.special_world;
1898 }
1899
1900 for (int y = 0; y < 32; y += 2) {
1901 for (int x = 0; x < 32; x += 2) {
1902 gfx::Tile32 current_tile(
1903 tiles_used[x + (sx * 32)][y + (sy * 32)],
1904 tiles_used[x + 1 + (sx * 32)][y + (sy * 32)],
1905 tiles_used[x + (sx * 32)][y + 1 + (sy * 32)],
1906 tiles_used[x + 1 + (sx * 32)][y + 1 + (sy * 32)]);
1907
1908 all_tile_16.emplace_back(current_tile.GetPackedValue());
1909 }
1910 }
1911
1912 sx++;
1913 if (sx >= 8) {
1914 sy++;
1915 sx = 0;
1916 }
1917
1918 c++;
1919 if (c >= 64) {
1920 sx = 0;
1921 sy = 0;
1922 c = 0;
1923 }
1924 }
1925
1926 return all_tile_16;
1927}
1928} // namespace
1929
1931 tiles32_unique_.clear();
1932 tiles32_list_.clear();
1933
1934 // Get all tiles16 and packs them into tiles32
1935 std::vector<uint64_t> all_tile_16 = GetAllTile16(map_tiles_);
1936
1937 // Convert to set then back to vector
1938 std::set<uint64_t> unique_tiles_set(all_tile_16.begin(), all_tile_16.end());
1939
1940 std::vector<uint64_t> unique_tiles(all_tile_16);
1941 unique_tiles.assign(unique_tiles_set.begin(), unique_tiles_set.end());
1942
1943 // Create the indexed tiles list
1944 std::unordered_map<uint64_t, uint16_t> all_tiles_indexed;
1945 for (size_t tile32_id = 0; tile32_id < unique_tiles.size(); tile32_id++) {
1946 all_tiles_indexed.insert(
1947 {unique_tiles[tile32_id], static_cast<uint16_t>(tile32_id)});
1948 }
1949
1950 // Add all tiles32 from all maps.
1951 // Convert all tiles32 non-unique IDs into unique array of IDs.
1952 for (int j = 0; j < NumberOfMap32; j++) {
1953 tiles32_list_.emplace_back(all_tiles_indexed[all_tile_16[j]]);
1954 }
1955
1956 // Create the unique tiles list
1957 for (size_t i = 0; i < unique_tiles.size(); ++i) {
1958 tiles32_unique_.emplace_back(gfx::Tile32(unique_tiles[i]));
1959 }
1960
1961 while (tiles32_unique_.size() % 4 != 0) {
1962 gfx::Tile32 padding_tile(0, 0, 0, 0);
1963 tiles32_unique_.emplace_back(padding_tile.GetPackedValue());
1964 }
1965
1966 if (tiles32_unique_.size() > LimitOfMap32) {
1967 return absl::InternalError(absl::StrFormat(
1968 "Number of unique Tiles32: %d Out of: %d\nUnique Tile32 count exceed "
1969 "the limit\nThe ROM Has not been saved\nYou can fill maps with grass "
1970 "tiles to free some space\nOr use the option Clear DW Tiles in the "
1971 "Overworld Menu",
1972 unique_tiles.size(), LimitOfMap32));
1973 }
1974
1975 if (core::FeatureFlags::get().kLogToConsole) {
1976 std::cout << "Number of unique Tiles32: " << tiles32_unique_.size()
1977 << " Saved:" << tiles32_unique_.size()
1978 << " Out of: " << LimitOfMap32 << std::endl;
1979 }
1980
1981 int v = tiles32_unique_.size();
1982 for (int i = v; i < LimitOfMap32; i++) {
1983 gfx::Tile32 padding_tile(420, 420, 420, 420);
1984 tiles32_unique_.emplace_back(padding_tile.GetPackedValue());
1985 }
1986
1987 return absl::OkStatus();
1988}
1989
1991 int bottomLeft = kMap32TileBLExpanded;
1992 int bottomRight = kMap32TileBRExpanded;
1993 int topRight = kMap32TileTRExpanded;
1994 int limit = 0x8A80;
1995
1996 // Updates the pointers too for the tile32
1997 // Top Right
1998 RETURN_IF_ERROR(rom()->WriteLong(0x0176EC, PcToSnes(kMap32TileTRExpanded)));
2000 rom()->WriteLong(0x0176F3, PcToSnes(kMap32TileTRExpanded + 1)));
2002 rom()->WriteLong(0x0176FA, PcToSnes(kMap32TileTRExpanded + 2)));
2004 rom()->WriteLong(0x017701, PcToSnes(kMap32TileTRExpanded + 3)));
2006 rom()->WriteLong(0x017708, PcToSnes(kMap32TileTRExpanded + 4)));
2008 rom()->WriteLong(0x01771A, PcToSnes(kMap32TileTRExpanded + 5)));
2009
2010 // BottomLeft
2011 RETURN_IF_ERROR(rom()->WriteLong(0x01772C, PcToSnes(kMap32TileBLExpanded)));
2013 rom()->WriteLong(0x017733, PcToSnes(kMap32TileBLExpanded + 1)));
2015 rom()->WriteLong(0x01773A, PcToSnes(kMap32TileBLExpanded + 2)));
2017 rom()->WriteLong(0x017741, PcToSnes(kMap32TileBLExpanded + 3)));
2019 rom()->WriteLong(0x017748, PcToSnes(kMap32TileBLExpanded + 4)));
2021 rom()->WriteLong(0x01775A, PcToSnes(kMap32TileBLExpanded + 5)));
2022
2023 // BottomRight
2024 RETURN_IF_ERROR(rom()->WriteLong(0x01776C, PcToSnes(kMap32TileBRExpanded)));
2026 rom()->WriteLong(0x017773, PcToSnes(kMap32TileBRExpanded + 1)));
2028 rom()->WriteLong(0x01777A, PcToSnes(kMap32TileBRExpanded + 2)));
2030 rom()->WriteLong(0x017781, PcToSnes(kMap32TileBRExpanded + 3)));
2032 rom()->WriteLong(0x017788, PcToSnes(kMap32TileBRExpanded + 4)));
2034 rom()->WriteLong(0x01779A, PcToSnes(kMap32TileBRExpanded + 5)));
2035
2036 constexpr int kTilesPer32x32Tile = 6;
2037 int unique_tile_index = 0;
2038 int num_unique_tiles = tiles32_unique_.size();
2039
2040 for (int i = 0; i < num_unique_tiles; i += kTilesPer32x32Tile) {
2041 if (unique_tile_index >= limit) {
2042 return absl::AbortedError("Too many unique tile32 definitions.");
2043 }
2044
2045 // Top Left.
2046 auto top_left = rom()->version_constants().kMap32TileTL;
2047 RETURN_IF_ERROR(rom()->WriteByte(
2048 top_left + i,
2049 (uint8_t)(tiles32_unique_[unique_tile_index].tile0_ & 0xFF)));
2050 RETURN_IF_ERROR(rom()->WriteByte(
2051 top_left + (i + 1),
2052 (uint8_t)(tiles32_unique_[unique_tile_index + 1].tile0_ & 0xFF)));
2053 RETURN_IF_ERROR(rom()->WriteByte(
2054 top_left + (i + 2),
2055 (uint8_t)(tiles32_unique_[unique_tile_index + 2].tile0_ & 0xFF)));
2056 RETURN_IF_ERROR(rom()->WriteByte(
2057 top_left + (i + 3),
2058 (uint8_t)(tiles32_unique_[unique_tile_index + 3].tile0_ & 0xFF)));
2059
2060 RETURN_IF_ERROR(rom()->WriteByte(
2061 top_left + (i + 4),
2062 (uint8_t)(((tiles32_unique_[unique_tile_index].tile0_ >> 4) & 0xF0) +
2063 ((tiles32_unique_[unique_tile_index + 1].tile0_ >> 8) &
2064 0x0F))));
2065 RETURN_IF_ERROR(rom()->WriteByte(
2066 top_left + (i + 5),
2067 (uint8_t)(((tiles32_unique_[unique_tile_index + 2].tile0_ >> 4) &
2068 0xF0) +
2069 ((tiles32_unique_[unique_tile_index + 3].tile0_ >> 8) &
2070 0x0F))));
2071
2072 // Top Right.
2073 auto top_right = topRight;
2074 RETURN_IF_ERROR(rom()->WriteByte(
2075 top_right + i,
2076 (uint8_t)(tiles32_unique_[unique_tile_index].tile1_ & 0xFF)));
2077 RETURN_IF_ERROR(rom()->WriteByte(
2078 top_right + (i + 1),
2079 (uint8_t)(tiles32_unique_[unique_tile_index + 1].tile1_ & 0xFF)));
2080 RETURN_IF_ERROR(rom()->WriteByte(
2081 top_right + (i + 2),
2082 (uint8_t)(tiles32_unique_[unique_tile_index + 2].tile1_ & 0xFF)));
2083 RETURN_IF_ERROR(rom()->WriteByte(
2084 top_right + (i + 3),
2085 (uint8_t)(tiles32_unique_[unique_tile_index + 3].tile1_ & 0xFF)));
2086
2087 RETURN_IF_ERROR(rom()->WriteByte(
2088 top_right + (i + 4),
2089 (uint8_t)(((tiles32_unique_[unique_tile_index].tile1_ >> 4) & 0xF0) |
2090 ((tiles32_unique_[unique_tile_index + 1].tile1_ >> 8) &
2091 0x0F))));
2092 RETURN_IF_ERROR(rom()->WriteByte(
2093 top_right + (i + 5),
2094 (uint8_t)(((tiles32_unique_[unique_tile_index + 2].tile1_ >> 4) &
2095 0xF0) |
2096 ((tiles32_unique_[unique_tile_index + 3].tile1_ >> 8) &
2097 0x0F))));
2098
2099 // Bottom Left.
2100 auto bottom_left = bottomLeft;
2101 RETURN_IF_ERROR(rom()->WriteByte(
2102 bottom_left + i,
2103 (uint8_t)(tiles32_unique_[unique_tile_index].tile2_ & 0xFF)));
2104 RETURN_IF_ERROR(rom()->WriteByte(
2105 bottom_left + (i + 1),
2106 (uint8_t)(tiles32_unique_[unique_tile_index + 1].tile2_ & 0xFF)));
2107 RETURN_IF_ERROR(rom()->WriteByte(
2108 bottom_left + (i + 2),
2109 (uint8_t)(tiles32_unique_[unique_tile_index + 2].tile2_ & 0xFF)));
2110 RETURN_IF_ERROR(rom()->WriteByte(
2111 bottom_left + (i + 3),
2112 (uint8_t)(tiles32_unique_[unique_tile_index + 3].tile2_ & 0xFF)));
2113
2114 RETURN_IF_ERROR(rom()->WriteByte(
2115 bottom_left + (i + 4),
2116 (uint8_t)(((tiles32_unique_[unique_tile_index].tile2_ >> 4) & 0xF0) |
2117 ((tiles32_unique_[unique_tile_index + 1].tile2_ >> 8) &
2118 0x0F))));
2119 RETURN_IF_ERROR(rom()->WriteByte(
2120 bottom_left + (i + 5),
2121 (uint8_t)(((tiles32_unique_[unique_tile_index + 2].tile2_ >> 4) &
2122 0xF0) |
2123 ((tiles32_unique_[unique_tile_index + 3].tile2_ >> 8) &
2124 0x0F))));
2125
2126 // Bottom Right.
2127 auto bottom_right = bottomRight;
2128 RETURN_IF_ERROR(rom()->WriteByte(
2129 bottom_right + i,
2130 (uint8_t)(tiles32_unique_[unique_tile_index].tile3_ & 0xFF)));
2131 RETURN_IF_ERROR(rom()->WriteByte(
2132 bottom_right + (i + 1),
2133 (uint8_t)(tiles32_unique_[unique_tile_index + 1].tile3_ & 0xFF)));
2134 RETURN_IF_ERROR(rom()->WriteByte(
2135 bottom_right + (i + 2),
2136 (uint8_t)(tiles32_unique_[unique_tile_index + 2].tile3_ & 0xFF)));
2137 RETURN_IF_ERROR(rom()->WriteByte(
2138 bottom_right + (i + 3),
2139 (uint8_t)(tiles32_unique_[unique_tile_index + 3].tile3_ & 0xFF)));
2140
2141 RETURN_IF_ERROR(rom()->WriteByte(
2142 bottom_right + (i + 4),
2143 (uint8_t)(((tiles32_unique_[unique_tile_index].tile3_ >> 4) & 0xF0) |
2144 ((tiles32_unique_[unique_tile_index + 1].tile3_ >> 8) &
2145 0x0F))));
2146 RETURN_IF_ERROR(rom()->WriteByte(
2147 bottom_right + (i + 5),
2148 (uint8_t)(((tiles32_unique_[unique_tile_index + 2].tile3_ >> 4) &
2149 0xF0) |
2150 ((tiles32_unique_[unique_tile_index + 3].tile3_ >> 8) &
2151 0x0F))));
2152
2153 unique_tile_index += 4;
2154 }
2155
2156 return absl::OkStatus();
2157}
2158
2160 util::logf("Saving Map32 Tiles");
2161 constexpr int kMaxUniqueTiles = 0x4540;
2162 constexpr int kTilesPer32x32Tile = 6;
2163
2164 int unique_tile_index = 0;
2165 int num_unique_tiles = tiles32_unique_.size();
2166
2167 for (int i = 0; i < num_unique_tiles; i += kTilesPer32x32Tile) {
2168 if (unique_tile_index >= kMaxUniqueTiles) {
2169 return absl::AbortedError("Too many unique tile32 definitions.");
2170 }
2171
2172 // Top Left.
2173 auto top_left = rom()->version_constants().kMap32TileTL;
2174
2175 RETURN_IF_ERROR(rom()->WriteByte(
2176 top_left + i,
2177 (uint8_t)(tiles32_unique_[unique_tile_index].tile0_ & 0xFF)));
2178 RETURN_IF_ERROR(rom()->WriteByte(
2179 top_left + (i + 1),
2180 (uint8_t)(tiles32_unique_[unique_tile_index + 1].tile0_ & 0xFF)));
2181 RETURN_IF_ERROR(rom()->WriteByte(
2182 top_left + (i + 2),
2183 (uint8_t)(tiles32_unique_[unique_tile_index + 2].tile0_ & 0xFF)));
2184 RETURN_IF_ERROR(rom()->WriteByte(
2185 top_left + (i + 3),
2186 (uint8_t)(tiles32_unique_[unique_tile_index + 3].tile0_ & 0xFF)));
2187
2188 RETURN_IF_ERROR(rom()->WriteByte(
2189 top_left + (i + 4),
2190 (uint8_t)(((tiles32_unique_[unique_tile_index].tile0_ >> 4) & 0xF0) +
2191 ((tiles32_unique_[unique_tile_index + 1].tile0_ >> 8) &
2192 0x0F))));
2193 RETURN_IF_ERROR(rom()->WriteByte(
2194 top_left + (i + 5),
2195 (uint8_t)(((tiles32_unique_[unique_tile_index + 2].tile0_ >> 4) &
2196 0xF0) +
2197 ((tiles32_unique_[unique_tile_index + 3].tile0_ >> 8) &
2198 0x0F))));
2199
2200 // Top Right.
2201 auto top_right = rom()->version_constants().kMap32TileTR;
2202 RETURN_IF_ERROR(rom()->WriteByte(
2203 top_right + i,
2204 (uint8_t)(tiles32_unique_[unique_tile_index].tile1_ & 0xFF)));
2205 RETURN_IF_ERROR(rom()->WriteByte(
2206 top_right + (i + 1),
2207 (uint8_t)(tiles32_unique_[unique_tile_index + 1].tile1_ & 0xFF)));
2208 RETURN_IF_ERROR(rom()->WriteByte(
2209 top_right + (i + 2),
2210 (uint8_t)(tiles32_unique_[unique_tile_index + 2].tile1_ & 0xFF)));
2211 RETURN_IF_ERROR(rom()->WriteByte(
2212 top_right + (i + 3),
2213 (uint8_t)(tiles32_unique_[unique_tile_index + 3].tile1_ & 0xFF)));
2214
2215 RETURN_IF_ERROR(rom()->WriteByte(
2216 top_right + (i + 4),
2217 (uint8_t)(((tiles32_unique_[unique_tile_index].tile1_ >> 4) & 0xF0) |
2218 ((tiles32_unique_[unique_tile_index + 1].tile1_ >> 8) &
2219 0x0F))));
2220 RETURN_IF_ERROR(rom()->WriteByte(
2221 top_right + (i + 5),
2222 (uint8_t)(((tiles32_unique_[unique_tile_index + 2].tile1_ >> 4) &
2223 0xF0) |
2224 ((tiles32_unique_[unique_tile_index + 3].tile1_ >> 8) &
2225 0x0F))));
2226
2227 // Bottom Left.
2228 const auto map32TilesBL = rom()->version_constants().kMap32TileBL;
2229 RETURN_IF_ERROR(rom()->WriteByte(
2230 map32TilesBL + i,
2231 (uint8_t)(tiles32_unique_[unique_tile_index].tile2_ & 0xFF)));
2232 RETURN_IF_ERROR(rom()->WriteByte(
2233 map32TilesBL + (i + 1),
2234 (uint8_t)(tiles32_unique_[unique_tile_index + 1].tile2_ & 0xFF)));
2235 RETURN_IF_ERROR(rom()->WriteByte(
2236 map32TilesBL + (i + 2),
2237 (uint8_t)(tiles32_unique_[unique_tile_index + 2].tile2_ & 0xFF)));
2238 RETURN_IF_ERROR(rom()->WriteByte(
2239 map32TilesBL + (i + 3),
2240 (uint8_t)(tiles32_unique_[unique_tile_index + 3].tile2_ & 0xFF)));
2241
2242 RETURN_IF_ERROR(rom()->WriteByte(
2243 map32TilesBL + (i + 4),
2244 (uint8_t)(((tiles32_unique_[unique_tile_index].tile2_ >> 4) & 0xF0) |
2245 ((tiles32_unique_[unique_tile_index + 1].tile2_ >> 8) &
2246 0x0F))));
2247 RETURN_IF_ERROR(rom()->WriteByte(
2248 map32TilesBL + (i + 5),
2249 (uint8_t)(((tiles32_unique_[unique_tile_index + 2].tile2_ >> 4) &
2250 0xF0) |
2251 ((tiles32_unique_[unique_tile_index + 3].tile2_ >> 8) &
2252 0x0F))));
2253
2254 // Bottom Right.
2255 const auto map32TilesBR = rom()->version_constants().kMap32TileBR;
2256 RETURN_IF_ERROR(rom()->WriteByte(
2257 map32TilesBR + i,
2258 (uint8_t)(tiles32_unique_[unique_tile_index].tile3_ & 0xFF)));
2259 RETURN_IF_ERROR(rom()->WriteByte(
2260 map32TilesBR + (i + 1),
2261 (uint8_t)(tiles32_unique_[unique_tile_index + 1].tile3_ & 0xFF)));
2262 RETURN_IF_ERROR(rom()->WriteByte(
2263 map32TilesBR + (i + 2),
2264 (uint8_t)(tiles32_unique_[unique_tile_index + 2].tile3_ & 0xFF)));
2265 RETURN_IF_ERROR(rom()->WriteByte(
2266 map32TilesBR + (i + 3),
2267 (uint8_t)(tiles32_unique_[unique_tile_index + 3].tile3_ & 0xFF)));
2268
2269 RETURN_IF_ERROR(rom()->WriteByte(
2270 map32TilesBR + (i + 4),
2271 (uint8_t)(((tiles32_unique_[unique_tile_index].tile3_ >> 4) & 0xF0) |
2272 ((tiles32_unique_[unique_tile_index + 1].tile3_ >> 8) &
2273 0x0F))));
2274 RETURN_IF_ERROR(rom()->WriteByte(
2275 map32TilesBR + (i + 5),
2276 (uint8_t)(((tiles32_unique_[unique_tile_index + 2].tile3_ >> 4) &
2277 0xF0) |
2278 ((tiles32_unique_[unique_tile_index + 3].tile3_ >> 8) &
2279 0x0F))));
2280
2281 unique_tile_index += 4;
2282 num_unique_tiles += 2;
2283 }
2284
2285 return absl::OkStatus();
2286}
2287
2290 rom()->WriteLong(SnesToPc(0x008865), PcToSnes(kMap16TilesExpanded)));
2292 rom()->WriteLong(SnesToPc(0x0EDE4F), PcToSnes(kMap16TilesExpanded)));
2294 rom()->WriteLong(SnesToPc(0x0EDEE9), PcToSnes(kMap16TilesExpanded)));
2295
2297 rom()->WriteLong(SnesToPc(0x1BBC2D), PcToSnes(kMap16TilesExpanded + 2)));
2299 rom()->WriteLong(SnesToPc(0x1BBC4C), PcToSnes(kMap16TilesExpanded)));
2301 rom()->WriteLong(SnesToPc(0x1BBCC2), PcToSnes(kMap16TilesExpanded + 4)));
2303 rom()->WriteLong(SnesToPc(0x1BBCCB), PcToSnes(kMap16TilesExpanded + 6)));
2304
2306 rom()->WriteLong(SnesToPc(0x1BBEF6), PcToSnes(kMap16TilesExpanded)));
2308 rom()->WriteLong(SnesToPc(0x1BBF23), PcToSnes(kMap16TilesExpanded)));
2310 rom()->WriteLong(SnesToPc(0x1BC041), PcToSnes(kMap16TilesExpanded)));
2312 rom()->WriteLong(SnesToPc(0x1BC9B3), PcToSnes(kMap16TilesExpanded)));
2313
2315 rom()->WriteLong(SnesToPc(0x1BC9BA), PcToSnes(kMap16TilesExpanded + 2)));
2317 rom()->WriteLong(SnesToPc(0x1BC9C1), PcToSnes(kMap16TilesExpanded + 4)));
2319 rom()->WriteLong(SnesToPc(0x1BC9C8), PcToSnes(kMap16TilesExpanded + 6)));
2320
2322 rom()->WriteLong(SnesToPc(0x1BCA40), PcToSnes(kMap16TilesExpanded)));
2324 rom()->WriteLong(SnesToPc(0x1BCA47), PcToSnes(kMap16TilesExpanded + 2)));
2326 rom()->WriteLong(SnesToPc(0x1BCA4E), PcToSnes(kMap16TilesExpanded + 4)));
2328 rom()->WriteLong(SnesToPc(0x1BCA55), PcToSnes(kMap16TilesExpanded + 6)));
2329
2331 rom()->WriteLong(SnesToPc(0x02F457), PcToSnes(kMap16TilesExpanded)));
2333 rom()->WriteLong(SnesToPc(0x02F45E), PcToSnes(kMap16TilesExpanded + 2)));
2335 rom()->WriteLong(SnesToPc(0x02F467), PcToSnes(kMap16TilesExpanded + 4)));
2337 rom()->WriteLong(SnesToPc(0x02F46E), PcToSnes(kMap16TilesExpanded + 6)));
2339 rom()->WriteLong(SnesToPc(0x02F51F), PcToSnes(kMap16TilesExpanded)));
2341 rom()->WriteLong(SnesToPc(0x02F526), PcToSnes(kMap16TilesExpanded + 4)));
2343 rom()->WriteLong(SnesToPc(0x02F52F), PcToSnes(kMap16TilesExpanded + 2)));
2345 rom()->WriteLong(SnesToPc(0x02F536), PcToSnes(kMap16TilesExpanded + 6)));
2346
2348 rom()->WriteShort(SnesToPc(0x02FE1C), PcToSnes(kMap16TilesExpanded)));
2350 rom()->WriteShort(SnesToPc(0x02FE23), PcToSnes(kMap16TilesExpanded + 4)));
2352 rom()->WriteShort(SnesToPc(0x02FE2C), PcToSnes(kMap16TilesExpanded + 2)));
2354 rom()->WriteShort(SnesToPc(0x02FE33), PcToSnes(kMap16TilesExpanded + 6)));
2355
2356 RETURN_IF_ERROR(rom()->WriteByte(
2357 SnesToPc(0x02FD28),
2358 static_cast<uint8_t>(PcToSnes(kMap16TilesExpanded) >> 16)));
2359 RETURN_IF_ERROR(rom()->WriteByte(
2360 SnesToPc(0x02FD39),
2361 static_cast<uint8_t>(PcToSnes(kMap16TilesExpanded) >> 16)));
2362
2363 int tpos = kMap16TilesExpanded;
2364 for (int i = 0; i < NumberOfMap16Ex; i += 1) // 4096
2365 {
2367 rom()->WriteShort(tpos, TileInfoToShort(tiles16_[i].tile0_)));
2368 tpos += 2;
2370 rom()->WriteShort(tpos, TileInfoToShort(tiles16_[i].tile1_)));
2371 tpos += 2;
2373 rom()->WriteShort(tpos, TileInfoToShort(tiles16_[i].tile2_)));
2374 tpos += 2;
2376 rom()->WriteShort(tpos, TileInfoToShort(tiles16_[i].tile3_)));
2377 tpos += 2;
2378 }
2379
2380 return absl::OkStatus();
2381}
2382
2384 util::logf("Saving Map16 Tiles");
2385 int tpos = kMap16Tiles;
2386 // 3760
2387 for (int i = 0; i < NumberOfMap16; i += 1) {
2389 rom()->WriteShort(tpos, TileInfoToShort(tiles16_[i].tile0_)))
2390 tpos += 2;
2392 rom()->WriteShort(tpos, TileInfoToShort(tiles16_[i].tile1_)))
2393 tpos += 2;
2395 rom()->WriteShort(tpos, TileInfoToShort(tiles16_[i].tile2_)))
2396 tpos += 2;
2398 rom()->WriteShort(tpos, TileInfoToShort(tiles16_[i].tile3_)))
2399 tpos += 2;
2400 }
2401 return absl::OkStatus();
2402}
2403
2409
2410absl::Status Overworld::SaveExits() {
2412 return absl::OkStatus();
2413}
2414
2415absl::Status Overworld::SaveItems() {
2417 return absl::OkStatus();
2418}
2419
2421 util::logf("Saving Map Overlays");
2422
2423 // Generate the new overlay code that handles interactive overlays
2424 std::vector<uint8_t> new_overlay_code = {
2425 0xC2, 0x30, // REP #$30
2426 0xA5, 0x8A, // LDA $8A
2427 0x0A, 0x18, // ASL : CLC
2428 0x65, 0x8A, // ADC $8A
2429 0xAA, // TAX
2430 0xBF, 0x00, 0x00, 0x00, // LDA, X
2431 0x85, 0x00, // STA $00
2432 0xBF, 0x00, 0x00, 0x00, // LDA, X +2
2433 0x85, 0x02, // STA $02
2434 0x4B, // PHK
2435 0xF4, 0x00, 0x00, // This position +3 ?
2436 0xDC, 0x00, 0x00, // JML [$00 00]
2437 0xE2, 0x30, // SEP #$30
2438 0xAB, // PLB
2439 0x6B, // RTL
2440 };
2441
2442 // Write overlay code to ROM
2443 constexpr int kOverlayCodeStart = 0x077657;
2444 RETURN_IF_ERROR(rom()->WriteVector(kOverlayCodeStart, new_overlay_code));
2445
2446 // Set up overlay pointers
2447 int ptr_start = kOverlayCodeStart + 0x20;
2448 int snes_ptr_start = PcToSnes(ptr_start);
2449
2450 // Write overlay pointer addresses in the code
2451 RETURN_IF_ERROR(rom()->WriteLong(kOverlayCodeStart + 10, snes_ptr_start));
2452 RETURN_IF_ERROR(rom()->WriteLong(kOverlayCodeStart + 16, snes_ptr_start + 2));
2453
2454 int pea_addr = PcToSnes(kOverlayCodeStart + 27);
2455 RETURN_IF_ERROR(rom()->WriteShort(kOverlayCodeStart + 23, pea_addr));
2456
2457 // Write overlay data to expanded space
2458 constexpr int kExpandedOverlaySpace = 0x120000;
2459 int pos = kExpandedOverlaySpace;
2460 int ptr_pos = kOverlayCodeStart + 32;
2461
2462 for (int i = 0; i < kNumOverworldMaps; i++) {
2463 int snes_addr = PcToSnes(pos);
2464 RETURN_IF_ERROR(rom()->WriteLong(ptr_pos, snes_addr & 0xFFFFFF));
2465 ptr_pos += 3;
2466
2467 // Write overlay data for each map that has overlays
2468 if (overworld_maps_[i].has_overlay()) {
2469 const auto& overlay_data = overworld_maps_[i].overlay_data();
2470 for (size_t t = 0; t < overlay_data.size(); t += 3) {
2471 if (t + 2 < overlay_data.size()) {
2472 // Generate LDA/STA sequence for each overlay tile
2473 RETURN_IF_ERROR(rom()->WriteByte(pos, 0xA9)); // LDA #$
2474 RETURN_IF_ERROR(rom()->WriteShort(
2475 pos + 1, overlay_data[t] | (overlay_data[t + 1] << 8)));
2476 pos += 3;
2477
2478 RETURN_IF_ERROR(rom()->WriteByte(pos, 0x8D)); // STA $xxxx
2479 RETURN_IF_ERROR(rom()->WriteShort(pos + 1, overlay_data[t + 2]));
2480 pos += 3;
2481 }
2482 }
2483 }
2484
2485 RETURN_IF_ERROR(rom()->WriteByte(pos, 0x6B)); // RTL
2486 pos++;
2487 }
2488
2489 return absl::OkStatus();
2490}
2491
2493 util::logf("Saving Overworld Tiles Types");
2494
2495 for (int i = 0; i < kNumTileTypes; i++) {
2497 rom()->WriteByte(overworldTilesType + i, all_tiles_types_[i]));
2498 }
2499
2500 return absl::OkStatus();
2501}
2502
2503absl::Status Overworld::SaveCustomOverworldASM(bool enable_bg_color,
2504 bool enable_main_palette,
2505 bool enable_mosaic,
2506 bool enable_gfx_groups,
2507 bool enable_subscreen_overlay,
2508 bool enable_animated) {
2509 util::logf("Applying Custom Overworld ASM");
2510
2511 // Set the enable/disable settings
2512 uint8_t enable_value = enable_bg_color ? 0xFF : 0x00;
2514 rom()->WriteByte(OverworldCustomAreaSpecificBGEnabled, enable_value));
2515
2516 enable_value = enable_main_palette ? 0xFF : 0x00;
2518 rom()->WriteByte(OverworldCustomMainPaletteEnabled, enable_value));
2519
2520 enable_value = enable_mosaic ? 0xFF : 0x00;
2521 RETURN_IF_ERROR(rom()->WriteByte(OverworldCustomMosaicEnabled, enable_value));
2522
2523 enable_value = enable_gfx_groups ? 0xFF : 0x00;
2525 rom()->WriteByte(OverworldCustomTileGFXGroupEnabled, enable_value));
2526
2527 enable_value = enable_animated ? 0xFF : 0x00;
2529 rom()->WriteByte(OverworldCustomAnimatedGFXEnabled, enable_value));
2530
2531 enable_value = enable_subscreen_overlay ? 0xFF : 0x00;
2533 rom()->WriteByte(OverworldCustomSubscreenOverlayEnabled, enable_value));
2534
2535 // Write the main palette table
2536 for (int i = 0; i < kNumOverworldMaps; i++) {
2538 overworld_maps_[i].main_palette()));
2539 }
2540
2541 // Write the mosaic table
2542 for (int i = 0; i < kNumOverworldMaps; i++) {
2543 const auto& mosaic = overworld_maps_[i].mosaic_expanded();
2544 // .... udlr bit format
2545 uint8_t mosaic_byte = (mosaic[0] ? 0x08 : 0x00) | // up
2546 (mosaic[1] ? 0x04 : 0x00) | // down
2547 (mosaic[2] ? 0x02 : 0x00) | // left
2548 (mosaic[3] ? 0x01 : 0x00); // right
2549
2551 rom()->WriteByte(OverworldCustomMosaicArray + i, mosaic_byte));
2552 }
2553
2554 // Write the main and animated gfx tiles table
2555 for (int i = 0; i < kNumOverworldMaps; i++) {
2556 for (int j = 0; j < 8; j++) {
2558 rom()->WriteByte(OverworldCustomTileGFXGroupArray + (i * 8) + j,
2559 overworld_maps_[i].custom_tileset(j)));
2560 }
2562 overworld_maps_[i].animated_gfx()));
2563 }
2564
2565 // Write the subscreen overlay table
2566 for (int i = 0; i < kNumOverworldMaps; i++) {
2568 rom()->WriteShort(OverworldCustomSubscreenOverlayArray + (i * 2),
2569 overworld_maps_[i].subscreen_overlay()));
2570 }
2571
2572 return absl::OkStatus();
2573}
2574
2576 util::logf("Saving Area Specific Background Colors");
2577
2578 // Write area-specific background colors if enabled
2579 for (int i = 0; i < kNumOverworldMaps; i++) {
2580 uint16_t bg_color = overworld_maps_[i].area_specific_bg_color();
2581 RETURN_IF_ERROR(rom()->WriteShort(
2582 OverworldCustomAreaSpecificBGPalette + (i * 2), bg_color));
2583 }
2584
2585 return absl::OkStatus();
2586}
2587
2589 util::logf("Saving Map Properties");
2590 for (int i = 0; i < kDarkWorldMapIdStart; i++) {
2591 RETURN_IF_ERROR(rom()->WriteByte(kAreaGfxIdPtr + i,
2592 overworld_maps_[i].area_graphics()));
2594 overworld_maps_[i].area_palette()));
2595 RETURN_IF_ERROR(rom()->WriteByte(kOverworldSpriteset + i,
2596 overworld_maps_[i].sprite_graphics(0)));
2598 rom()->WriteByte(kOverworldSpriteset + kDarkWorldMapIdStart + i,
2599 overworld_maps_[i].sprite_graphics(1)));
2602 overworld_maps_[i].sprite_graphics(2)));
2604 overworld_maps_[i].sprite_palette(0)));
2607 overworld_maps_[i].sprite_palette(1)));
2608 RETURN_IF_ERROR(rom()->WriteByte(
2610 overworld_maps_[i].sprite_palette(2)));
2611 }
2612
2613 for (int i = kDarkWorldMapIdStart; i < kSpecialWorldMapIdStart; i++) {
2614 RETURN_IF_ERROR(rom()->WriteByte(kAreaGfxIdPtr + i,
2615 overworld_maps_[i].area_graphics()));
2616 RETURN_IF_ERROR(rom()->WriteByte(kOverworldSpriteset + i,
2617 overworld_maps_[i].sprite_graphics(0)));
2619 rom()->WriteByte(kOverworldSpriteset + kDarkWorldMapIdStart + i,
2620 overworld_maps_[i].sprite_graphics(1)));
2623 overworld_maps_[i].sprite_graphics(2)));
2625 overworld_maps_[i].area_palette()));
2628 overworld_maps_[i].sprite_palette(0)));
2629 RETURN_IF_ERROR(rom()->WriteByte(
2631 overworld_maps_[i].sprite_palette(1)));
2632 RETURN_IF_ERROR(rom()->WriteByte(kOverworldSpritePaletteIds + 192 + i,
2633 overworld_maps_[i].sprite_palette(2)));
2634 }
2635
2636 return absl::OkStatus();
2637}
2638
2639absl::Status Overworld::SaveMusic() {
2640 util::logf("Saving Music Data");
2641
2642 // Save music data for Light World maps
2643 for (int i = 0; i < kDarkWorldMapIdStart; i++) {
2645 overworld_maps_[i].area_music(0)));
2646 RETURN_IF_ERROR(rom()->WriteByte(kOverworldMusicZelda + i,
2647 overworld_maps_[i].area_music(1)));
2649 overworld_maps_[i].area_music(2)));
2651 overworld_maps_[i].area_music(3)));
2652 }
2653
2654 // Save music data for Dark World maps
2655 for (int i = kDarkWorldMapIdStart; i < kSpecialWorldMapIdStart; i++) {
2658 overworld_maps_[i].area_music(0)));
2659 }
2660
2661 return absl::OkStatus();
2662}
2663
2665 util::logf("Saving V3 Area Sizes");
2666
2667 // Check if this is a v3 ROM
2668 uint8_t asm_version = (*rom_)[zelda3::OverworldCustomASMHasBeenApplied];
2669 if (asm_version < 3 || asm_version == 0xFF) {
2670 return absl::OkStatus(); // Not a v3 ROM, nothing to do
2671 }
2672
2673 // Save area sizes to the expanded table
2674 for (int i = 0; i < kNumOverworldMaps; i++) {
2675 uint8_t area_size_byte =
2676 static_cast<uint8_t>(overworld_maps_[i].area_size());
2677 RETURN_IF_ERROR(rom()->WriteByte(kOverworldScreenSize + i, area_size_byte));
2678 }
2679
2680 // Save message IDs to expanded table
2681 for (int i = 0; i < kNumOverworldMaps; i++) {
2682 uint16_t message_id = overworld_maps_[i].message_id();
2684 rom()->WriteShort(kOverworldMessagesExpanded + (i * 2), message_id));
2685 }
2686
2687 return absl::OkStatus();
2688}
2689
2690} // namespace zelda3
2691} // namespace yaze
The Rom class is used to load, save, and modify Rom data.
Definition rom.h:74
static Flags & get()
Definition features.h:82
RAII timer for automatic timing management.
Tile composition of four 16x16 tiles.
Definition snes_tile.h:88
uint64_t GetPackedValue() const
Definition snes_tile.h:118
SNES 16-bit tile metadata container.
Definition snes_tile.h:50
absl::Status SaveMap32Expanded()
absl::Status DecompressAllMapTilesParallel()
Definition overworld.cc:543
std::vector< uint16_t > tiles32_list_
Definition overworld.h:356
absl::Status Load(Rom *rom)
Definition overworld.cc:31
std::vector< OverworldItem > all_items_
Definition overworld.h:350
void OrganizeMapTiles(std::vector< uint8_t > &bytes, std::vector< uint8_t > &bytes2, int i, int sx, int sy, int &ttpos)
Definition overworld.cc:517
std::array< int, kNumOverworldMaps > map_pointers1
Definition overworld.h:366
std::vector< gfx::Tile32 > tiles32_unique_
Definition overworld.h:354
absl::Status SaveMapProperties()
absl::Status SaveMap32Tiles()
std::vector< OverworldEntrance > all_entrances_
Definition overworld.h:347
absl::Status SaveTallAreaTransitions(int i, int parent_x_pos, int parent_y_pos, int transition_target_north, int transition_target_west, int transition_pos_x, int transition_pos_y, int screen_change_1, int screen_change_2, int screen_change_3, int screen_change_4)
OverworldMapTiles map_tiles_
Definition overworld.h:341
absl::Status SaveMap16Tiles()
absl::Status SaveAreaSizes()
absl::Status SaveLargeMaps()
Definition overworld.cc:930
std::array< uint8_t, kNumOverworldMaps > map_parent_
Definition overworld.h:359
void AssignWorldTiles(int x, int y, int sx, int sy, int tpos, OverworldBlockset &world)
Definition overworld.cc:505
std::array< uint8_t, kNumTileTypes > all_tiles_types_
Definition overworld.h:360
absl::Status CreateTile32Tilemap()
std::array< int, kNumOverworldMaps > map_pointers1_id
Definition overworld.h:364
absl::Status SaveLargeAreaTransitions(int i, int parent_x_pos, int parent_y_pos, int transition_target_north, int transition_target_west, int transition_pos_x, int transition_pos_y, int screen_change_1, int screen_change_2, int screen_change_3, int screen_change_4)
absl::Status SaveCustomOverworldASM(bool enable_bg_color, bool enable_main_palette, bool enable_mosaic, bool enable_gfx_groups, bool enable_subscreen_overlay, bool enable_animated)
absl::Status SaveEntrances()
absl::Status SaveExits()
absl::Status LoadSprites()
Definition overworld.cc:690
absl::Status EnsureMapBuilt(int map_index)
Build a map on-demand if it hasn't been built yet.
Definition overworld.cc:660
std::vector< OverworldMap > overworld_maps_
Definition overworld.h:346
absl::Status SaveItems()
absl::Status SaveAreaSpecificBGColors()
absl::Status Save(Rom *rom)
Definition overworld.cc:768
absl::Status SaveWideAreaTransitions(int i, int parent_x_pos, int parent_y_pos, int transition_target_north, int transition_target_west, int transition_pos_x, int transition_pos_y, int screen_change_1, int screen_change_2, int screen_change_3, int screen_change_4)
absl::Status SaveOverworldMaps()
Definition overworld.cc:792
std::array< std::vector< Sprite >, 3 > all_sprites_
Definition overworld.h:361
std::array< int, kNumOverworldMaps > map_pointers2_id
Definition overworld.h:365
absl::Status LoadOverworldMaps()
Definition overworld.cc:604
std::vector< gfx::Tile16 > tiles16_
Definition overworld.h:352
absl::Status AssembleMap16Tiles()
Definition overworld.cc:470
absl::Status LoadSpritesFromMap(int sprite_start, int sprite_count, int sprite_index)
Definition overworld.cc:727
std::array< std::vector< uint8_t >, kNumOverworldMaps > map_data_p1
Definition overworld.h:362
absl::Status SaveLargeMapsExpanded()
void AssignMapSizes(std::vector< OverworldMap > &maps)
Loads all maps from ROM to see what size they are.
Definition overworld.cc:173
absl::Status SaveMap16Expanded()
std::vector< OverworldExit > all_exits_
Definition overworld.h:349
std::array< int, kNumOverworldMaps > map_pointers2
Definition overworld.h:367
absl::StatusOr< uint16_t > GetTile16ForTile32(int index, int quadrant, int dimension, const uint32_t *map32address)
Definition overworld.cc:399
std::array< std::vector< uint8_t >, kNumOverworldMaps > map_data_p2
Definition overworld.h:363
absl::Status SaveSmallAreaTransitions(int i, int parent_x_pos, int parent_y_pos, int transition_target_north, int transition_target_west, int transition_pos_x, int transition_pos_y, int screen_change_1, int screen_change_2, int screen_change_3, int screen_change_4)
absl::Status SaveMapOverlays()
absl::Status AssembleMap32Tiles()
Definition overworld.cc:410
OverworldBlockset & GetMapTiles(int world_type)
Definition overworld.h:233
absl::Status SaveOverworldTilesType()
absl::Status SaveMusic()
absl::Status ConfigureMultiAreaMap(int parent_index, AreaSizeEnum size)
Configure a multi-area map structure (Large/Wide/Tall)
Definition overworld.cc:261
std::vector< OverworldEntrance > all_holes_
Definition overworld.h:348
#define LOG_DEBUG(category, format,...)
Definition log.h:104
#define RETURN_IF_ERROR(expression)
Definition macro.h:53
#define ASSIGN_OR_RETURN(type_variable_name, expression)
Definition macro.h:61
std::vector< uint8_t > HyruleMagicDecompress(uint8_t const *src, int *const size, int const p_big_endian)
TileInfo GetTilesInfo(uint16_t tile)
Definition snes_tile.cc:354
std::vector< uint8_t > HyruleMagicCompress(uint8_t const *const src, int const oldsize, int *const size, int const flag)
std::string HexByte(uint8_t byte, HexStringParams params)
Definition hex.cc:30
void logf(const absl::FormatSpec< Args... > &format, Args &&... args)
Definition log.h:116
std::string HexLong(uint32_t dword, HexStringParams params)
Definition hex.cc:52
std::vector< uint64_t > GetAllTile16(OverworldMapTiles &map_tiles_)
constexpr int kAreaGfxIdPtr
Definition overworld.h:43
absl::Status SaveEntrances(Rom *rom, const std::vector< OverworldEntrance > &entrances, bool expanded_entrances)
constexpr int OverworldCustomTileGFXGroupEnabled
constexpr int OverworldCustomAreaSpecificBGEnabled
constexpr int kOverworldTransitionPositionY
Definition overworld.h:67
constexpr int kNumMapsPerWorld
Definition overworld.h:120
constexpr int kOverworldSpriteset
Definition overworld.h:36
constexpr int kMap16ExpandedFlagPos
Definition overworld.h:91
constexpr int LimitOfMap32
Definition overworld.h:117
constexpr int NumberOfMap16Ex
Definition overworld.h:116
absl::StatusOr< std::vector< OverworldEntrance > > LoadEntrances(Rom *rom)
absl::Status SaveItems(Rom *rom, const std::vector< OverworldItem > &items)
constexpr int kOverworldScreenTileMapChangeByScreen1
Definition overworld.h:72
constexpr int kOverworldScreenTileMapChangeByScreen2Expanded
constexpr int kOverworldMapDataOverflow
Definition overworld.h:77
constexpr int kOverworldMapSizeHighByte
Definition overworld.h:58
absl::StatusOr< std::vector< OverworldItem > > LoadItems(Rom *rom, std::vector< OverworldMap > &overworld_maps)
constexpr int overworldSpritesBeginingExpanded
Definition overworld.h:93
constexpr int kNumTileTypes
Definition overworld.h:110
constexpr int NumberOfMap32
Definition overworld.h:119
constexpr int kOverworldScreenSize
Definition overworld.h:69
constexpr int kOverworldScreenTileMapChangeByScreen4
Definition overworld.h:75
constexpr int kNumTile16Individual
Definition overworld.h:113
constexpr int kSpecialWorldMapIdStart
constexpr int OverworldCustomMosaicArray
constexpr int kOverworldTransitionPositionXExpanded
constexpr int kMap16Tiles
Definition overworld.h:111
constexpr int overworldSpritesAgahnimExpanded
Definition overworld.h:95
constexpr int OverworldCustomAnimatedGFXEnabled
constexpr int OverworldCustomMainPaletteEnabled
constexpr int kNumOverworldMaps
Definition common.h:46
constexpr int OverworldCustomMainPaletteArray
constexpr int kOverworldTransitionPositionYExpanded
constexpr int kOverworldMapParentIdExpanded
constexpr int kOverworldMusicBeginning
Definition overworld.h:46
constexpr int kMap32TileBLExpanded
Definition overworld.h:87
constexpr int kOverworldTransitionPositionX
Definition overworld.h:68
constexpr int kOverworldMusicDarkWorld
Definition overworld.h:50
constexpr int kOverworldScreenSizeForLoading
Definition overworld.h:70
constexpr int kOverworldSpritePaletteIds
Definition overworld.h:34
constexpr int overworldTilesType
Definition overworld.h:103
constexpr int transition_target_westExpanded
absl::StatusOr< std::vector< OverworldExit > > LoadExits(Rom *rom)
constexpr int kMap32TileBRExpanded
Definition overworld.h:88
constexpr int kMap32TileCountExpanded
Definition overworld.h:89
constexpr int kTransitionTargetWest
Definition overworld.h:80
constexpr int OverworldCustomASMHasBeenApplied
Definition common.h:50
constexpr int kOverworldMusicAgahnim
Definition overworld.h:49
constexpr int kOverworldSpritesZelda
Definition overworld.h:41
constexpr int kOverworldMapParentId
Definition overworld.h:66
constexpr int kMap32ExpandedFlagPos
Definition overworld.h:90
constexpr int kOverworldMessagesExpanded
constexpr int kOverworldMusicMasterSword
Definition overworld.h:48
constexpr int kOverworldScreenTileMapChangeByScreen3Expanded
constexpr int kOverworldMusicZelda
Definition overworld.h:47
constexpr int transition_target_northExpanded
constexpr int NumberOfMap16
Definition overworld.h:115
constexpr int kOverworldMapSize
Definition overworld.h:55
constexpr int kOverworldScreenTileMapChangeByScreen2
Definition overworld.h:73
constexpr int OverworldCustomAnimatedGFXArray
constexpr int kDarkWorldMapIdStart
absl::Status SaveHoles(Rom *rom, const std::vector< OverworldEntrance > &holes)
absl::StatusOr< std::vector< OverworldEntrance > > LoadHoles(Rom *rom)
constexpr int OverworldCustomMosaicEnabled
constexpr int kOverworldCompressedMapPos
Definition overworld.h:107
constexpr int kOverworldScreenTileMapChangeByScreen4Expanded
constexpr int kOverworldSpritesBeginning
Definition overworld.h:39
std::vector< std::vector< uint16_t > > OverworldBlockset
Represents tile32 data for the overworld.
constexpr int kOverworldScreenTileMapChangeByScreen3
Definition overworld.h:74
constexpr int kOverworldScreenTileMapChangeByScreen1Expanded
constexpr int OverworldCustomTileGFXGroupArray
constexpr int kMap16TilesExpanded
Definition overworld.h:85
constexpr int OverworldCustomSubscreenOverlayEnabled
constexpr int OverworldCustomAreaSpecificBGPalette
constexpr int kOverworldSpritesAgahnim
Definition overworld.h:40
constexpr int kTransitionTargetNorth
Definition overworld.h:79
constexpr int overworldSpritesZeldaExpanded
Definition overworld.h:94
constexpr int kOverworldCompressedOverflowPos
Definition overworld.h:108
constexpr int kOverlayCodeStart
constexpr int kMap32TileTRExpanded
Definition overworld.h:86
absl::Status SaveExits(Rom *rom, const std::vector< OverworldExit > &exits)
constexpr int kOverworldMapPaletteIds
Definition overworld.h:33
constexpr int OverworldCustomSubscreenOverlayArray
Main namespace for the application.
Definition controller.cc:20
uint32_t PcToSnes(uint32_t addr)
Definition snes.h:17
uint32_t SnesToPc(uint32_t addr) noexcept
Definition snes.h:8
Overworld map tile32 data.