yaze 0.3.2
Link to the Past ROM Editor
 
Loading...
Searching...
No Matches
overworld_map.cc
Go to the documentation of this file.
1#include "overworld_map.h"
2
3#include <algorithm>
4#include <array>
5#include <cstddef>
6#include <cstdint>
7#include <unordered_map>
8#include <vector>
9
10#include "absl/status/status.h"
11#include "absl/status/statusor.h"
15#include "rom/rom.h"
16#include "core/features.h"
17#include "util/macro.h"
18#include "zelda3/common.h"
21
22namespace yaze::zelda3 {
23
24OverworldMap::OverworldMap(int index, Rom* rom, GameData* game_data)
25 : index_(index), parent_(index), rom_(rom), game_data_(game_data) {
27
28 // Load parent ID from ROM data for all versions
29 // This is critical for proper large map sibling coordination
31 if (version >= OverworldVersion::kZSCustomV3) {
32 // For v3+, parent ID is stored in expanded table (0x140998) with 160 entries
34 } else {
35 // For vanilla/v1/v2, parent ID table at 0x125EC only has 64 entries (LW only)
36 // DW maps mirror LW parents with +0x40 offset
37 // SW maps use hardcoded parent values based on vanilla game behavior
39 // Light World: direct lookup from 64-entry table
41 } else if (index_ < kSpecialWorldMapIdStart) {
42 // Dark World: mirror LW parent structure with +0x40 offset
43 // DW map 0x43's parent = LW map 0x03's parent (from table) + 0x40
44 uint8_t lw_equivalent = index_ - kDarkWorldMapIdStart;
45 uint8_t lw_parent = (*rom_)[kOverworldMapParentId + lw_equivalent];
46 parent_ = lw_parent + kDarkWorldMapIdStart;
47 } else {
48 // Special World: hardcoded parents for vanilla compatibility
49 // Zora's Domain (0x81, 0x82, 0x89, 0x8A) is a large area with parent 0x81
50 if (index_ == 0x81 || index_ == 0x82 || index_ == 0x89 || index_ == 0x8A) {
51 parent_ = 0x81;
52 } else {
53 // All other SW maps are small areas - parent is self
55 }
56 }
57 }
58
59 if (version != OverworldVersion::kVanilla) {
60 // For ALL custom ASM versions (v1-v3), read from custom arrays
61 // SetupCustomTileset checks enable flags and falls back to vanilla if disabled
63 } else if (core::FeatureFlags::get().overworld.kLoadCustomOverworld) {
64 // Pure vanilla ROM but flag enabled - set up hardcoded vanilla defaults
66 }
67 // For pure vanilla ROMs, LoadAreaInfo already handles everything
68}
69
70absl::Status OverworldMap::BuildMap(int count, int game_state, int world,
71 std::vector<gfx::Tile16>& tiles16,
72 OverworldBlockset& world_blockset) {
73 // Delegate to cached version with no cache
74 return BuildMapWithCache(count, game_state, world, tiles16, world_blockset,
75 nullptr);
76}
77
79 int count, int game_state, int world, std::vector<gfx::Tile16>& tiles16,
80 OverworldBlockset& world_blockset,
81 const std::vector<uint8_t>* cached_tileset) {
83 world_ = world;
85
86 // For large maps in vanilla ROMs, we need to handle special world graphics
87 // This ensures proper rendering of special overworld areas like Zora's Domain
88 // NOTE: Callers (LoadOverworldMaps, EnsureMapBuilt) also handle this and call
89 // LoadAreaGraphics() before BuildMapWithCache. This block is kept for the
90 // native async path which calls BuildMap() -> BuildMapWithCache() directly.
91 if (large_map_ && (version == OverworldVersion::kVanilla)) {
92 if (parent_ != index_ && !initialized_) {
93 if (index_ >= kSpecialWorldMapIdStart && index_ <= 0x8A &&
94 index_ != 0x88) {
95 // Zora's Domain children - set sprite_graphics and area graphics
96 sprite_graphics_[0] = 0x0E;
97 sprite_graphics_[1] = 0x0E;
98 sprite_graphics_[2] = 0x0E;
102 } else if (index_ == 0x88) {
103 // Triforce room has special hardcoded values
104 area_graphics_ = 0x51;
105 area_palette_ = 0x00;
106 } else if (index_ < kSpecialWorldMapIdStart) {
107 // LW/DW large map child - use parent's graphics
110 }
111 // Note: Other SW maps (>0x8A) keep their LoadAreaInfo values
112
113 initialized_ = true;
114 }
115 }
116
118
119 // Use cached tileset if available, otherwise build from scratch
120 if (cached_tileset && !cached_tileset->empty()) {
121 UseCachedTileset(*cached_tileset);
122 } else {
124 }
125
126 RETURN_IF_ERROR(BuildTiles16Gfx(tiles16, count))
129 RETURN_IF_ERROR(BuildBitmap(world_blockset))
130 built_ = true;
131 return absl::OkStatus();
132}
133
136
137 // ZSCustomOverworld ASM Version System:
138 // 0x00-0x02: Legacy versions with limited features
139 // 0x03: Current version with full area size expansion and custom data support
140 // 0xFF: Pure vanilla ROM (no ASM applied)
141
142 // Load message ID and area size based on ASM version
143 if (version < OverworldVersion::kZSCustomV2 ||
144 version == OverworldVersion::kVanilla) {
145 // v2 and vanilla: use original message table
146 message_id_ = (*rom_)[kOverworldMessageIds + (parent_ * 2)] |
147 ((*rom_)[kOverworldMessageIds + (parent_ * 2) + 1] << 8);
148
149 // Load area size for v2/vanilla
150 if (index_ < 0x80) {
151 // For LW and DW, check the screen size byte
152 // Note: v2 had a bug where large/small values were swapped
153 uint8_t size_byte = (*rom_)[kOverworldScreenSize + (index_ & 0x3F)];
154 switch (size_byte) {
155 case 0:
157 break;
158 case 1:
159 default:
161 break;
162 case 2:
164 break;
165 case 3:
167 break;
168 }
169 } else {
170 // For SW, use hardcoded values for v2 compatibility
171 // Zora's Domain areas (0x81, 0x82, 0x89, 0x8A) are large areas
172 area_size_ =
173 (index_ == 0x81 || index_ == 0x82 || index_ == 0x89 || index_ == 0x8A)
176 }
177 } else {
178 // v3: use expanded message table and area size table
179 // All area sizes are now stored in the expanded table, supporting all size
180 // types
182 (*rom_)[GetOverworldMessagesExpanded() + (parent_ * 2)] |
183 ((*rom_)[GetOverworldMessagesExpanded() + (parent_ * 2) + 1] << 8);
184 area_size_ =
185 static_cast<AreaSizeEnum>((*rom_)[kOverworldScreenSize + index_]);
186 }
187
188 // Update large_map_ based on area size
190
191 // Load area-specific data based on index range
193 // Light World (LW) areas
199
202
204 sprite_palette_[1] =
206 sprite_palette_[2] =
208
213
214 // For v2/vanilla, use original palette table
215 if (version < OverworldVersion::kZSCustomV2 ||
216 version == OverworldVersion::kVanilla) {
218 }
219 } else if (index_ < kSpecialWorldMapIdStart) {
220 // Dark World (DW) areas
227
230
231 sprite_palette_[0] =
233 sprite_palette_[1] =
235 sprite_palette_[2] =
237
238 area_music_[0] =
240
241 // For v2/vanilla, use original palette table
242 if (version < OverworldVersion::kZSCustomV2 ||
243 version == OverworldVersion::kVanilla) {
245 }
246 } else {
247 // Special World (SW) areas (index >= 0x80)
248 // Message ID already loaded above based on ASM version
249
250 // For v3, use expanded sprite tables with full customization support
251 if (version == OverworldVersion::kZSCustomV3) {
261
268 } else {
269 // For v2/vanilla, use original sprite tables
276
283 }
284
287
288 // For v2/vanilla, use original palette table and handle special cases
289 // These hardcoded cases are needed for vanilla compatibility
290 if (version < OverworldVersion::kZSCustomV2 ||
291 version == OverworldVersion::kVanilla) {
293
294 // Handle special world area cases based on ZScream documentation
295 if (index_ == 0x88 || index_ == 0x93) {
296 // Triforce room - special graphics and palette
297 area_graphics_ = 0x51;
298 area_palette_ = 0x00;
299 } else if (index_ == 0x80) {
300 // Master Sword area - use special graphics group
304 } else if (index_ == 0x81 || index_ == 0x82 || index_ == 0x89 ||
305 index_ == 0x8A) {
306 // Zora's Domain areas - use special sprite graphics and area graphics
307 // Note: These are the large area maps that were causing crashes
308 sprite_graphics_[0] = 0x0E;
309 sprite_graphics_[1] = 0x0E;
310 sprite_graphics_[2] = 0x0E;
311
315 } else if (index_ == 0x94) {
316 // Make this the same GFX as the true master sword area
318 (0x80 - kSpecialWorldMapIdStart)];
320 } else if (index_ == 0x95) {
321 // Make this the same GFX as the LW death mountain areas
322 area_graphics_ = (*rom_)[kAreaGfxIdPtr + 0x03];
323 area_palette_ = (*rom_)[kOverworldMapPaletteIds + 0x03];
324 } else if (index_ == 0x96) {
325 // Make this the same GFX as the pyramid areas
326 area_graphics_ = (*rom_)[kAreaGfxIdPtr + 0x5B];
327 area_palette_ = (*rom_)[kOverworldMapPaletteIds + 0x5B];
328 } else if (index_ == 0x9C) {
329 // Make this the same GFX as the DW death mountain areas
330 area_graphics_ = (*rom_)[kAreaGfxIdPtr + 0x43];
331 area_palette_ = (*rom_)[kOverworldMapPaletteIds + 0x43];
332 } else {
333 // Default case - use basic graphics
334 area_graphics_ = (*rom_)[kAreaGfxIdPtr + 0x00];
335 area_palette_ = (*rom_)[kOverworldMapPaletteIds + 0x00];
336 }
337 }
338 }
339}
340
342 // Set the main palette values based on ZScream logic
343 // Use parent_ to ensure all sibling maps in a large area share the same palette
344 if (parent_ < 0x40 || parent_ == 0x95) { // LW
345 main_palette_ = 0;
346 } else if ((parent_ >= 0x40 && parent_ < 0x80) || parent_ == 0x96) { // DW
347 main_palette_ = 1;
348 } else if (parent_ >= 0x80 && parent_ < 0xA0) { // SW
349 main_palette_ = 0;
350 }
351
352 // Death Mountain / special overrides - use parent_ so siblings get correct palette
353 if (parent_ == 0x03 || parent_ == 0x05 ||
354 parent_ == 0x07) { // LW Death Mountain
355 main_palette_ = 2;
356 } else if (parent_ == 0x43 || parent_ == 0x45 ||
357 parent_ == 0x47) { // DW Death Mountain
358 main_palette_ = 3;
359 } else if (parent_ == 0x88 || parent_ == 0x93) { // Triforce room
360 main_palette_ = 4;
361 }
362
363 // Set the mosaic values based on ZScream logic
364 switch (index_) {
365 case 0x00: // Leaving Skull Woods / Lost Woods
366 case 0x40:
367 mosaic_expanded_ = {false, true, false, true};
368 break;
369 case 0x02: // Going into Skull woods / Lost Woods west
370 case 0x0A:
371 case 0x42:
372 case 0x4A:
373 mosaic_expanded_ = {false, false, true, false};
374 break;
375 case 0x0F: // Going into Zora's Domain North
376 case 0x10: // Going into Skull Woods / Lost Woods North
377 case 0x11:
378 case 0x50:
379 case 0x51:
380 mosaic_expanded_ = {true, false, false, false};
381 break;
382 case 0x80: // Leaving Zora's Domain, the Master Sword area, and the
383 // Triforce area
384 case 0x81:
385 case 0x88:
386 mosaic_expanded_ = {false, true, false, false};
387 break;
388 default:
389 mosaic_expanded_ = {false, false, false, false};
390 break;
391 }
392
393 // Set up world index for GFX groups
394 int index_world = 0x20;
397 index_world = 0x21;
398 } else if (parent_ == 0x88 || parent_ == 0x93) { // Triforce room
399 index_world = 0x24;
400 }
401
402 const auto overworld_gfx_groups2 = version_constants().kOverworldGfxGroups2;
403
404 // Main Blocksets
405 for (int i = 0; i < 8; i++) {
406 custom_gfx_ids_[i] =
407 (uint8_t)(*rom_)[overworld_gfx_groups2 + (index_world * 8) + i];
408 }
409
410 const auto overworldgfxGroups = version_constants().kOverworldGfxGroups1;
411
412 // Replace the variable tiles with the variable ones
413 uint8_t temp = (*rom_)[overworldgfxGroups + (area_graphics_ * 4)];
414 if (temp != 0) {
415 custom_gfx_ids_[3] = temp;
416 } else {
417 custom_gfx_ids_[3] = 0xFF;
418 }
419
420 temp = (*rom_)[overworldgfxGroups + (area_graphics_ * 4) + 1];
421 if (temp != 0) {
422 custom_gfx_ids_[4] = temp;
423 } else {
424 custom_gfx_ids_[4] = 0xFF;
425 }
426
427 temp = (*rom_)[overworldgfxGroups + (area_graphics_ * 4) + 2];
428 if (temp != 0) {
429 custom_gfx_ids_[5] = temp;
430 } else {
431 custom_gfx_ids_[5] = 0xFF;
432 }
433
434 temp = (*rom_)[overworldgfxGroups + (area_graphics_ * 4) + 3];
435 if (temp != 0) {
436 custom_gfx_ids_[6] = temp;
437 } else {
438 custom_gfx_ids_[6] = 0xFF;
439 }
440
441 // Set the animated GFX values - use parent_ so siblings get correct animated sheet
442 // Death Mountain uses sheet 0x59, others use 0x5B
443 if (parent_ == 0x03 || parent_ == 0x05 || parent_ == 0x07 || parent_ == 0x43 ||
444 parent_ == 0x45 || parent_ == 0x47 || parent_ == 0x95) {
445 animated_gfx_ = 0x59;
446 } else {
447 animated_gfx_ = 0x5B;
448 }
449
450 // Set the subscreen overlay values
451 subscreen_overlay_ = 0x00FF;
452
453 if (index_ == 0x00 || index_ == 0x01 || index_ == 0x08 || index_ == 0x09 ||
454 index_ == 0x40 || index_ == 0x41 || index_ == 0x48 ||
455 index_ == 0x49) { // Add fog 2 to the lost woods and skull woods
456 subscreen_overlay_ = 0x009D;
457 } else if (index_ == 0x03 || index_ == 0x04 || index_ == 0x0B ||
458 index_ == 0x0C || index_ == 0x05 || index_ == 0x06 ||
459 index_ == 0x0D || index_ == 0x0E ||
460 index_ == 0x07) { // Add the sky BG to LW death mountain
461 subscreen_overlay_ = 0x0095;
462 } else if (index_ == 0x43 || index_ == 0x44 || index_ == 0x4B ||
463 index_ == 0x4C || index_ == 0x45 || index_ == 0x46 ||
464 index_ == 0x4D || index_ == 0x4E ||
465 index_ == 0x47) { // Add the lava to DW death mountain
466 subscreen_overlay_ = 0x009C;
467 } else if (index_ == 0x5B || index_ == 0x5C || index_ == 0x63 ||
468 index_ == 0x64) { // TODO: Might need this one too "index == 0x1B"
469 // but for now I don't think so
470 subscreen_overlay_ = 0x0096;
471 } else if (index_ == 0x80) { // Add fog 1 to the master sword area
472 subscreen_overlay_ = 0x0097;
473 } else if (index_ ==
474 0x88) { // Add the triforce room curtains to the triforce room
475 subscreen_overlay_ = 0x0093;
476 }
477}
478
480 uint8_t palette = 0;
481
482 // Base world selection - use parent_ to ensure siblings share the same palette
483 if (parent_ < 0x40 || parent_ == 0x95) { // LW
484 palette = 0;
485 } else if ((parent_ >= 0x40 && parent_ < 0x80) || parent_ == 0x96) { // DW
486 palette = 1;
487 } else if (parent_ >= 0x80 && parent_ < 0xA0) { // SW
488 palette = 0;
489 }
490
491 // Death Mountain / special overrides - use parent_ to ensure all siblings
492 // in a large area get the correct palette (brown grass for DM)
493 // LW DM parents: 0x03, 0x05, 0x07 (children: 0x04, 0x06, 0x0B-0x0E)
494 // DW DM parents: 0x43, 0x45, 0x47 (children: 0x44, 0x46, 0x4B-0x4E)
495 if (parent_ == 0x03 || parent_ == 0x05 || parent_ == 0x07) {
496 palette = 2; // LW Death Mountain
497 } else if (parent_ == 0x43 || parent_ == 0x45 || parent_ == 0x47) {
498 palette = 3; // DW Death Mountain
499 } else if (parent_ == 0x88 || parent_ == 0x93) {
500 palette = 4; // Triforce room
501 }
502
503 return palette;
504}
505
506void OverworldMap::SetupCustomTileset(uint8_t asm_version) {
507 // Load area size for v3
510 uint8_t size_byte = (*rom_)[kOverworldScreenSize + index_];
511 area_size_ = static_cast<AreaSizeEnum>(size_byte);
513 }
514
515 // Main Palette - check enable flag before reading from custom array
516 if ((*rom_)[OverworldCustomMainPaletteEnabled] != 0x00) {
518 } else {
519 // Fall back to world-based hardcoded logic
521 }
522
523 // Mosaic - check enable flag before reading from custom array
524 if ((*rom_)[OverworldCustomMosaicEnabled] != 0x00) {
525 mosaic_ = (*rom_)[OverworldCustomMosaicArray + index_] != 0x00;
526 uint8_t mosaicByte = (*rom_)[OverworldCustomMosaicArray + index_];
527 mosaic_expanded_ = {(mosaicByte & 0x08) != 0x00,
528 (mosaicByte & 0x04) != 0x00,
529 (mosaicByte & 0x02) != 0x00,
530 (mosaicByte & 0x01) != 0x00};
531 } else {
532 // Fall back to hardcoded vanilla mosaic values
533 mosaic_ = false;
534 mosaic_expanded_ = {false, false, false, false};
535 switch (index_) {
536 case 0x00: // Leaving Skull Woods / Lost Woods
537 case 0x40:
538 mosaic_expanded_ = {false, true, false, true};
539 break;
540 case 0x02: // Going into Skull woods / Lost Woods west
541 case 0x0A:
542 case 0x42:
543 case 0x4A:
544 mosaic_expanded_ = {false, false, true, false};
545 break;
546 case 0x0F: // Going into Zora's Domain North
547 case 0x10: // Going into Skull Woods / Lost Woods North
548 case 0x11:
549 case 0x50:
550 case 0x51:
551 mosaic_expanded_ = {true, false, false, false};
552 break;
553 case 0x80: // Leaving Zora's Domain, Master Sword area, Triforce area
554 case 0x81:
555 case 0x88:
556 mosaic_expanded_ = {false, true, false, false};
557 break;
558 }
559 }
560
561 // Animated GFX - check enable flag before reading from custom array
562 if ((*rom_)[OverworldCustomAnimatedGFXEnabled] != 0x00) {
564 } else {
565 // Death mountain uses 0x59, others use 0x5B
566 // Use parent_ to ensure siblings in large areas share the same animated sheet
567 if (parent_ == 0x03 || parent_ == 0x05 || parent_ == 0x07 || parent_ == 0x43 ||
568 parent_ == 0x45 || parent_ == 0x47 || parent_ == 0x95) {
569 animated_gfx_ = 0x59;
570 } else {
571 animated_gfx_ = 0x5B;
572 }
573 }
574
575 // Tile GFX Groups - check enable flag before reading from custom array
577 for (int i = 0; i < 8; i++) {
578 custom_gfx_ids_[i] =
579 (*rom_)[OverworldCustomTileGFXGroupArray + (index_ * 8) + i];
580 }
581 } else {
582 // Fall back to world-based GFX groups
583 int index_world = 0x20;
586 index_world = 0x21;
587 } else if (parent_ == 0x88 || parent_ == 0x93) { // Triforce room
588 index_world = 0x24;
589 }
590
591 // Main Blocksets
592 for (int i = 0; i < 8; i++) {
593 custom_gfx_ids_[i] =
595 (index_world * 8) + i];
596 }
597
598 const auto overworldgfxGroups =
600
601 // Replace the variable tiles with the variable ones
602 // If the variable is 00 set it to 0xFF which is the new "don't load
603 // anything" value
604 uint8_t temp = (*rom_)[overworldgfxGroups + (area_graphics_ * 4)];
605 if (temp != 0x00) {
606 custom_gfx_ids_[3] = temp;
607 } else {
608 custom_gfx_ids_[3] = 0xFF;
609 }
610
611 temp = (*rom_)[overworldgfxGroups + (area_graphics_ * 4) + 1];
612 if (temp != 0x00) {
613 custom_gfx_ids_[4] = temp;
614 } else {
615 custom_gfx_ids_[4] = 0xFF;
616 }
617
618 temp = (*rom_)[overworldgfxGroups + (area_graphics_ * 4) + 2];
619 if (temp != 0x00) {
620 custom_gfx_ids_[5] = temp;
621 } else {
622 custom_gfx_ids_[5] = 0xFF;
623 }
624
625 temp = (*rom_)[overworldgfxGroups + (area_graphics_ * 4) + 3];
626 if (temp != 0x00) {
627 custom_gfx_ids_[6] = temp;
628 } else {
629 custom_gfx_ids_[6] = 0xFF;
630 }
631 }
632
633 // Subscreen Overlay - check enable flag before reading from custom array
637 ((*rom_)[OverworldCustomSubscreenOverlayArray + (index_ * 2) + 1] << 8);
638 } else {
639 // Fall back to hardcoded overlay values
640 subscreen_overlay_ = 0x00FF;
641 if (index_ == 0x00 || index_ == 0x01 || index_ == 0x08 || index_ == 0x09 ||
642 index_ == 0x40 || index_ == 0x41 || index_ == 0x48 || index_ == 0x49) {
643 // Add fog 2 to the lost woods and skull woods
644 subscreen_overlay_ = 0x009D;
645 } else if (index_ == 0x03 || index_ == 0x04 || index_ == 0x0B ||
646 index_ == 0x0C || index_ == 0x05 || index_ == 0x06 ||
647 index_ == 0x0D || index_ == 0x0E || index_ == 0x07) {
648 // Add the sky BG to LW death mountain
649 subscreen_overlay_ = 0x0095;
650 } else if (index_ == 0x43 || index_ == 0x44 || index_ == 0x4B ||
651 index_ == 0x4C || index_ == 0x45 || index_ == 0x46 ||
652 index_ == 0x4D || index_ == 0x4E || index_ == 0x47) {
653 // Add the lava to DW death mountain
654 subscreen_overlay_ = 0x009C;
655 } else if (index_ == 0x5B || index_ == 0x5C || index_ == 0x63 ||
656 index_ == 0x64) {
657 subscreen_overlay_ = 0x0096;
658 } else if (index_ == 0x80) {
659 // Add fog 1 to the master sword area
660 subscreen_overlay_ = 0x0097;
661 } else if (index_ == 0x88) {
662 // Add the triforce room curtains to the triforce room
663 subscreen_overlay_ = 0x0093;
664 }
665 }
666}
667
670 main_gfx_id_ = 0x20;
671 } else if (parent_ >= kDarkWorldMapIdStart &&
673 main_gfx_id_ = 0x21;
674 } else if (parent_ >= kSpecialWorldMapIdStart) {
675 // Special world maps - use appropriate graphics ID based on the specific
676 // map
677 if (parent_ == 0x88) {
678 main_gfx_id_ = 0x24;
679 } else {
680 // Default special world graphics ID
681 main_gfx_id_ = 0x20;
682 }
683 }
684}
685
687 int static_graphics_base = 0x73;
688 static_graphics_[8] = static_graphics_base + 0x00;
689 static_graphics_[9] = static_graphics_base + 0x01;
690 static_graphics_[10] = static_graphics_base + 0x06;
691 static_graphics_[11] = static_graphics_base + 0x07;
692
693 for (int i = 0; i < 4; i++) {
694 static_graphics_[12 + i] =
696 (sprite_graphics_[game_state_] * 4) + i] +
697 static_graphics_base);
698 }
699}
700
702 for (int i = 0; i < 8; i++) {
705 (main_gfx_id_ * 8) + i];
706 }
707}
708
709// For animating water tiles on the overworld map.
710// We want to swap out static_graphics_[07] with the next sheet
711// Usually it is 5A, so we make it 5B instead.
712// There is a middle frame which contains tiles from the bottom half
713// of the 5A sheet, so this will need some special manipulation to make work
714// during the BuildBitmap step (or a new one specifically for animating).
716 if (static_graphics_[7] == 0x5B) {
717 static_graphics_[7] = 0x5A;
718 } else {
719 if (static_graphics_[7] == 0x59) {
720 static_graphics_[7] = 0x58;
721 }
722 static_graphics_[7] = 0x5B;
723 }
724}
725
727 for (int i = 0; i < 4; i++) {
728 uint8_t value = (*rom_)[version_constants().kOverworldGfxGroups1 +
729 (area_graphics_ * 4) + i];
730 if (value != 0) {
731 static_graphics_[3 + i] = value;
732 }
733 }
734}
735
736// TODO: Change the conditions for death mountain gfx
737// JaredBrian: This is how ZS did it, but in 3.0.4 I changed it to just check
738// for 03, 05, 07, and the DW ones as that's how it would appear in-game if
739// you were to make area 03 not a large area anymore for example, so you might
740// want to do the same.
742 // Match ZScream 3.0.4 behavior: only specific DM parents use animated GFX
743 const bool is_light_dm = (parent_ == 0x03 || parent_ == 0x05 || parent_ == 0x07);
744 const bool is_dark_dm = (parent_ == 0x43 || parent_ == 0x45 || parent_ == 0x47);
745 static_graphics_[7] = (is_light_dm || is_dark_dm) ? 0x59 : 0x5B;
746}
747
754
755 // v3 custom tile GFX groups: override main sheets when enabled
759 for (int i = 0; i < 8; i++) {
760 uint8_t custom_sheet = custom_gfx_ids_[i];
761 if (custom_sheet == 0x00 || custom_sheet == 0xFF) {
762 continue; // 0/FF = don't load/override this slot
763 }
764 static_graphics_[i] = custom_sheet;
765 }
766 }
767}
768
769namespace palette_internal {
770
771absl::Status SetColorsPalette(Rom& rom, GameData* game_data, int index,
772 gfx::SnesPalette& current,
775 gfx::SnesPalette hud, gfx::SnesColor bgrcolor,
777 // Palettes infos, color 0 of a palette is always transparent (the arrays
778 // contains 7 colors width wide) There is 16 color per line so 16*Y
779
780 // Left side of the palette - Main, Animated
781 std::array<gfx::SnesColor, 256> new_palette = {};
782
783 // Main Palette, Location 0,2 : 35 colors [7x5]
784 int k = 0;
785 for (int y = 2; y < 7; y++) {
786 for (int x = 1; x < 8; x++) {
787 new_palette[x + (16 * y)] = main[k];
788 k++;
789 }
790 }
791
792 // Animated Palette, Location 0,7 : 7colors
793 for (int x = 1; x < 8; x++) {
794 new_palette[(16 * 7) + (x)] = animated[(x - 1)];
795 }
796
797 // Right side of the palette - Aux1, Aux2
798
799 // Aux1 Palette, Location 8,2 : 21 colors [7x3]
800 k = 0;
801 for (int y = 2; y < 5; y++) {
802 for (int x = 9; x < 16; x++) {
803 new_palette[x + (16 * y)] = aux1[k];
804 k++;
805 }
806 }
807
808 // Aux2 Palette, Location 8,5 : 21 colors [7x3]
809 k = 0;
810 for (int y = 5; y < 8; y++) {
811 for (int x = 9; x < 16; x++) {
812 new_palette[x + (16 * y)] = aux2[k];
813 k++;
814 }
815 }
816
817 // Hud Palette, Location 0,0 : 32 colors [16x2]
818 for (int i = 0; i < 32; i++) {
819 new_palette[i] = hud[i];
820 }
821
822 // Hardcoded grass color (that might change to become invisible instead)
823 for (int i = 0; i < 8; i++) {
824 new_palette[(i * 16)] = bgrcolor;
825 new_palette[(i * 16) + 8] = bgrcolor;
826 }
827
828 // Sprite Palettes
829 k = 0;
830 for (int y = 8; y < 9; y++) {
831 for (int x = 1; x < 8; x++) {
832 new_palette[x + (16 * y)] = game_data->palette_groups.sprites_aux1[1][k];
833 k++;
834 }
835 }
836
837 // Sprite Palettes
838 k = 0;
839 for (int y = 8; y < 9; y++) {
840 for (int x = 9; x < 16; x++) {
841 new_palette[x + (16 * y)] = game_data->palette_groups.sprites_aux3[0][k];
842 k++;
843 }
844 }
845
846 // Sprite Palettes
847 k = 0;
848 for (int y = 9; y < 13; y++) {
849 for (int x = 1; x < 16; x++) {
850 new_palette[x + (16 * y)] = game_data->palette_groups.global_sprites[0][k];
851 k++;
852 }
853 }
854
855 // Sprite Palettes
856 k = 0;
857 for (int y = 13; y < 14; y++) {
858 for (int x = 1; x < 8; x++) {
859 new_palette[x + (16 * y)] = spr[k];
860 k++;
861 }
862 }
863
864 // Sprite Palettes
865 k = 0;
866 for (int y = 14; y < 15; y++) {
867 for (int x = 1; x < 8; x++) {
868 new_palette[x + (16 * y)] = spr2[k];
869 k++;
870 }
871 }
872
873 // Sprite Palettes
874 k = 0;
875 for (int y = 15; y < 16; y++) {
876 for (int x = 1; x < 16; x++) {
877 new_palette[x + (16 * y)] = game_data->palette_groups.armors[0][k];
878 k++;
879 }
880 }
881
882 for (int i = 0; i < 256; i++) {
883 current[i] = new_palette[i];
884 current[(i / 16) * 16].set_transparent(true);
885 }
886
887 current.set_size(256);
888 return absl::OkStatus();
889}
890} // namespace palette_internal
891
892absl::StatusOr<gfx::SnesPalette> OverworldMap::GetPalette(
893 const gfx::PaletteGroup& palette_group, int index, int previous_index,
894 int limit) {
895 if (index == 255) {
897 (previous_index * 4)];
898 }
899 if (index >= limit) {
900 index = limit - 1;
901 }
902 return palette_group[index];
903}
904
906 if (!game_data_) {
908 return absl::OkStatus();
909 }
910
911 uint8_t asm_version = (*rom_)[OverworldCustomASMHasBeenApplied];
913
914 int previous_pal_id = 0;
915 int previous_spr_pal_id = 0;
916
917 if (index_ > 0) {
918 // Load previous palette ID based on ASM version
920 // v3 uses expanded palette table
921 previous_pal_id = (*rom_)[kOverworldPalettesScreenToSetNew + parent_ - 1];
922 } else {
923 previous_pal_id = (*rom_)[kOverworldMapPaletteIds + parent_ - 1];
924 }
925
926 previous_spr_pal_id = (*rom_)[kOverworldSpritePaletteIds + parent_ - 1];
927 }
928
929 area_palette_ = std::min((int)area_palette_, 0xA3);
930
931 uint8_t pal0 = 0;
932 uint8_t pal1 = (*rom_)[version_constants().kOverworldMapPaletteGroup +
933 (area_palette_ * 4)];
934 uint8_t pal2 = (*rom_)[version_constants().kOverworldMapPaletteGroup +
935 (area_palette_ * 4) + 1];
936 uint8_t pal3 = (*rom_)[version_constants().kOverworldMapPaletteGroup +
937 (area_palette_ * 4) + 2];
938 uint8_t pal4 = (*rom_)[kOverworldSpritePaletteGroup +
940 uint8_t pal5 = (*rom_)[kOverworldSpritePaletteGroup +
941 (sprite_palette_[game_state_] * 2) + 1];
942
943 auto& grass_pal_group = game_data_->palette_groups.grass;
944 auto bgr = grass_pal_group[0][0];
945
946 // Handle 0xFF palette references (use previous palette)
947 if (pal1 == 0xFF) {
949 (previous_pal_id * 4)];
950 }
951
952 if (pal2 == 0xFF) {
954 (previous_pal_id * 4) + 1];
955 }
956
957 if (pal3 == 0xFF) {
959 (previous_pal_id * 4) + 2];
960 }
961
962 auto& ow_aux_pal_group = game_data_->palette_groups.overworld_aux;
964 GetPalette(ow_aux_pal_group, pal1, previous_pal_id, 20));
966 GetPalette(ow_aux_pal_group, pal2, previous_pal_id, 20));
967
968 // Set background color based on world type and area-specific settings
969 bool use_area_specific_bg =
972 if (use_area_specific_bg) {
973 // Use area-specific background color from custom array
977 << 8);
978 // Convert 15-bit SNES color to palette color
980 } else {
981 // Use default world-based background colors
983 bgr = grass_pal_group[0][0]; // LW
984 } else if (parent_ >= kDarkWorldMapIdStart &&
986 bgr = grass_pal_group[0][1]; // DW
987 } else if (parent_ >= 128 && parent_ < kNumOverworldMaps) {
988 bgr = grass_pal_group[0][2]; // SW
989 }
990 }
991
992 // Use main palette from the overworld map data (matches ZScream logic)
993 if (version == OverworldVersion::kVanilla) {
994 // Vanilla ROMs never write main_palette_ elsewhere; ensure world defaults
996 }
997 pal0 = main_palette_;
998
999 auto& ow_main_pal_group = game_data_->palette_groups.overworld_main;
1001 GetPalette(ow_main_pal_group, pal0, previous_pal_id, 255));
1002 auto& ow_animated_pal_group = game_data_->palette_groups.overworld_animated;
1004 GetPalette(ow_animated_pal_group, std::min((int)pal3, 13),
1005 previous_pal_id, 14));
1006
1007 auto& hud_pal_group = game_data_->palette_groups.hud;
1008 gfx::SnesPalette hud = hud_pal_group[0];
1009
1010 // Handle 0xFF sprite palette references (use previous sprite palette)
1011 if (pal4 == 0xFF) {
1012 pal4 = (*rom_)[kOverworldSpritePaletteGroup + (previous_spr_pal_id * 2)];
1013 }
1014
1015 if (pal4 == 0xFF) {
1016 pal4 = 0; // Fallback to 0 if still 0xFF
1017 }
1018
1019 if (pal5 == 0xFF) {
1020 pal5 =
1021 (*rom_)[kOverworldSpritePaletteGroup + (previous_spr_pal_id * 2) + 1];
1022 }
1023
1024 if (pal5 == 0xFF) {
1025 pal5 = 0; // Fallback to 0 if still 0xFF
1026 }
1027
1030 previous_spr_pal_id, 24));
1033 previous_spr_pal_id, 24));
1034
1036 *rom_, game_data_, parent_, current_palette_, main, animated, aux1, aux2,
1037 hud, bgr, spr, spr2));
1038
1039 if (palettesets_.count(area_palette_) == 0) {
1041 main, animated, aux1, aux2, bgr, hud, spr, spr2, current_palette_};
1042 }
1043
1044 return absl::OkStatus();
1045}
1046
1048 // Load overlays based on ROM version
1050 // Vanilla ROM - load overlay from overlay pointers
1051 return LoadVanillaOverlayData();
1052 }
1053
1054 // Custom overworld ROM - use overlay from custom data
1056 has_overlay_ = (overlay_id_ != 0x00FF);
1057 overlay_data_.clear();
1058 return absl::OkStatus();
1059}
1060
1062 // Load vanilla overlay for this map (interactive overlays for revealing
1063 // holes/changing elements)
1064 int address = (kOverlayPointersBank << 16) +
1065 ((*rom_)[kOverlayPointers + (index_ * 2) + 1] << 8) +
1066 (*rom_)[kOverlayPointers + (index_ * 2)];
1067
1068 // Convert SNES address to PC address
1069 address = ((address & 0x7F0000) >> 1) | (address & 0x7FFF);
1070
1071 // Check if custom overlay code is present
1072 if ((*rom_)[kOverlayData1] == 0x6B) {
1073 // Use custom overlay data pointer
1074 address = ((*rom_)[kOverlayData2 + 2 + (index_ * 3)] << 16) +
1075 ((*rom_)[kOverlayData2 + 1 + (index_ * 3)] << 8) +
1076 (*rom_)[kOverlayData2 + (index_ * 3)];
1077 address = ((address & 0x7F0000) >> 1) | (address & 0x7FFF);
1078 }
1079
1080 // Validate address
1081 if (address >= rom_->size()) {
1082 has_overlay_ = false;
1083 overlay_id_ = 0;
1084 overlay_data_.clear();
1085 return absl::OkStatus();
1086 }
1087
1088 // Parse overlay data (interactive overlays)
1089 overlay_data_.clear();
1090 uint8_t b = (*rom_)[address];
1091
1092 // Parse overlay commands until we hit END (0x60)
1093 while (b != 0x60 && address < rom_->size()) {
1094 overlay_data_.push_back(b);
1095
1096 // Handle different overlay commands
1097 switch (b) {
1098 case 0xA9: // LDA #$
1099 if (address + 2 < rom_->size()) {
1100 overlay_data_.push_back((*rom_)[address + 1]);
1101 overlay_data_.push_back((*rom_)[address + 2]);
1102 address += 3;
1103 } else {
1104 address++;
1105 }
1106 break;
1107 case 0xA2: // LDX #$
1108 if (address + 2 < rom_->size()) {
1109 overlay_data_.push_back((*rom_)[address + 1]);
1110 overlay_data_.push_back((*rom_)[address + 2]);
1111 address += 3;
1112 } else {
1113 address++;
1114 }
1115 break;
1116 case 0x8D: // STA $xxxx
1117 if (address + 3 < rom_->size()) {
1118 overlay_data_.push_back((*rom_)[address + 1]);
1119 overlay_data_.push_back((*rom_)[address + 2]);
1120 overlay_data_.push_back((*rom_)[address + 3]);
1121 address += 4;
1122 } else {
1123 address++;
1124 }
1125 break;
1126 case 0x9D: // STA $xxxx,x
1127 if (address + 3 < rom_->size()) {
1128 overlay_data_.push_back((*rom_)[address + 1]);
1129 overlay_data_.push_back((*rom_)[address + 2]);
1130 overlay_data_.push_back((*rom_)[address + 3]);
1131 address += 4;
1132 } else {
1133 address++;
1134 }
1135 break;
1136 case 0x8F: // STA $xxxxxx
1137 if (address + 4 < rom_->size()) {
1138 overlay_data_.push_back((*rom_)[address + 1]);
1139 overlay_data_.push_back((*rom_)[address + 2]);
1140 overlay_data_.push_back((*rom_)[address + 3]);
1141 overlay_data_.push_back((*rom_)[address + 4]);
1142 address += 5;
1143 } else {
1144 address++;
1145 }
1146 break;
1147 case 0x1A: // INC A
1148 address++;
1149 break;
1150 case 0x4C: // JMP
1151 if (address + 3 < rom_->size()) {
1152 overlay_data_.push_back((*rom_)[address + 1]);
1153 overlay_data_.push_back((*rom_)[address + 2]);
1154 overlay_data_.push_back((*rom_)[address + 3]);
1155 address += 4;
1156 } else {
1157 address++;
1158 }
1159 break;
1160 default:
1161 address++;
1162 break;
1163 }
1164
1165 if (address < rom_->size()) {
1166 b = (*rom_)[address];
1167 } else {
1168 break;
1169 }
1170 }
1171
1172 // Add the END command if we found it
1173 if (b == 0x60) {
1174 overlay_data_.push_back(0x60);
1175 }
1176
1177 // Set overlay ID based on map index (simplified)
1179 has_overlay_ = !overlay_data_.empty();
1180
1181 return absl::OkStatus();
1182}
1183
1184void OverworldMap::ProcessGraphicsBuffer(int index, int static_graphics_offset,
1185 int size, const uint8_t* all_gfx) {
1186 // Ensure we don't go out of bounds
1187 int max_offset = static_graphics_offset * size + size;
1188 if (!game_data_ || max_offset > game_data_->graphics_buffer.size()) {
1189 // Fill with zeros if out of bounds
1190 for (int i = 0; i < size; i++) {
1191 current_gfx_[(index * size) + i] = 0x00;
1192 }
1193 return;
1194 }
1195
1196 for (int i = 0; i < size; i++) {
1197 auto byte = all_gfx[i + (static_graphics_offset * size)];
1198 switch (index) {
1199 case 0:
1200 case 3:
1201 case 4:
1202 case 5:
1203 byte += 0x88;
1204 break;
1205 }
1206 current_gfx_[(index * size) + i] = byte;
1207 }
1208}
1209
1211 if (current_gfx_.size() == 0)
1212 current_gfx_.resize(0x10000, 0x00);
1213
1214 if (!game_data_) {
1215 // Headless/tests: allow map builds without graphics by keeping zeroed data.
1216 return absl::OkStatus();
1217 }
1218
1219 // Process the 8 main graphics sheets (slots 0-7)
1220 for (int i = 0; i < 8; i++) {
1221 if (static_graphics_[i] != 0) {
1223 game_data_->graphics_buffer.data());
1224 }
1225 }
1226
1227 // Process sprite graphics (slots 8-15)
1228 for (int i = 8; i < 16; i++) {
1229 if (static_graphics_[i] != 0) {
1231 game_data_->graphics_buffer.data());
1232 }
1233 }
1234
1235 // NOTE: Previously there was code here accessing static_graphics_[16], but
1236 // the array is only size 16 (indices 0-15). This was undefined behavior
1237 // that read random memory and sometimes corrupted the animated graphics
1238 // slot (7), causing flaky water/cloud rendering. The animated graphics
1239 // are already correctly set in static_graphics_[7] by LoadDeathMountainGFX().
1240
1241 return absl::OkStatus();
1242}
1243
1244absl::Status OverworldMap::BuildTiles16Gfx(std::vector<gfx::Tile16>& tiles16,
1245 int count) {
1246 if (current_blockset_.size() == 0)
1247 current_blockset_.resize(0x100000, 0x00);
1248
1249 const int offsets[] = {0x00, 0x08, 0x400, 0x408};
1250 auto yy = 0;
1251 auto xx = 0;
1252
1253 for (auto i = 0; i < count; i++) {
1254 for (auto tile = 0; tile < 0x04; tile++) {
1255 gfx::TileInfo info = tiles16[i].tiles_info[tile];
1256 int offset = offsets[tile];
1257 for (auto y = 0; y < 0x08; ++y) {
1258 for (auto x = 0; x < 0x08; ++x) {
1259 int mx = x;
1260 int my = y;
1261
1262 if (info.horizontal_mirror_ != 0) {
1263 mx = 0x07 - x;
1264 }
1265
1266 if (info.vertical_mirror_ != 0) {
1267 my = 0x07 - y;
1268 }
1269
1270 int xpos = ((info.id_ % 0x10) * 0x08);
1271 int ypos = (((info.id_ / 0x10)) * 0x400);
1272 int source = ypos + xpos + (x + (y * 0x80));
1273
1274 auto destination = xx + yy + offset + (mx + (my * 0x80));
1276 (current_gfx_[source] & 0x0F) + (info.palette_ * 0x10);
1277 }
1278 }
1279 }
1280
1281 xx += 0x10;
1282 if (xx >= 0x80) {
1283 yy += 0x800;
1284 xx = 0;
1285 }
1286 }
1287
1288 return absl::OkStatus();
1289}
1290
1291absl::Status OverworldMap::BuildBitmap(OverworldBlockset& world_blockset) {
1292 if (bitmap_data_.size() != 0) {
1293 bitmap_data_.clear();
1294 }
1295 bitmap_data_.reserve(0x40000);
1296 for (int i = 0; i < 0x40000; i++) {
1297 bitmap_data_.push_back(0x00);
1298 }
1299
1300 int superY = ((index_ - (world_ * 0x40)) / 0x08);
1301 int superX = index_ - (world_ * 0x40) - (superY * 0x08);
1302
1303 for (int y = 0; y < 0x20; y++) {
1304 for (int x = 0; x < 0x20; x++) {
1305 auto xt = x + (superX * 0x20);
1306 auto yt = y + (superY * 0x20);
1307 gfx::CopyTile8bpp16((x * 0x10), (y * 0x10), world_blockset[xt][yt],
1309 }
1310 }
1311 return absl::OkStatus();
1312}
1313
1314} // namespace yaze::zelda3
The Rom class is used to load, save, and modify Rom data. This is a generic SNES ROM container and do...
Definition rom.h:28
auto size() const
Definition rom.h:138
static Flags & get()
Definition features.h:118
static std::unordered_map< uint8_t, gfx::Paletteset > palettesets_
SNES Color container.
Definition snes_color.h:110
Represents a palette of colors for the Super Nintendo Entertainment System (SNES).
void set_size(size_t size)
SNES 16-bit tile metadata container.
Definition snes_tile.h:52
void ProcessGraphicsBuffer(int index, int static_graphics_offset, int size, const uint8_t *all_gfx)
zelda3_version_pointers version_constants() const
std::vector< uint8_t > current_gfx_
std::vector< uint8_t > current_blockset_
std::array< bool, 4 > mosaic_expanded_
absl::Status LoadVanillaOverlayData()
std::vector< uint8_t > bitmap_data_
absl::Status BuildMapWithCache(int count, int game_state, int world, std::vector< gfx::Tile16 > &tiles16, OverworldBlockset &world_blockset, const std::vector< uint8_t > *cached_tileset)
Build map with optional cached tileset for performance.
void SetupCustomTileset(uint8_t asm_version)
absl::StatusOr< gfx::SnesPalette > GetPalette(const gfx::PaletteGroup &group, int index, int previous_index, int limit)
absl::Status BuildTiles16Gfx(std::vector< gfx::Tile16 > &tiles16, int count)
absl::Status BuildMap(int count, int game_state, int world, std::vector< gfx::Tile16 > &tiles16, OverworldBlockset &world_blockset)
std::array< uint8_t, 3 > sprite_graphics_
uint8_t ComputeWorldBasedMainPalette() const
absl::Status BuildBitmap(OverworldBlockset &world_blockset)
std::array< uint8_t, 3 > sprite_palette_
std::array< uint8_t, 4 > area_music_
std::array< uint8_t, 16 > static_graphics_
std::array< uint8_t, 8 > custom_gfx_ids_
std::vector< uint8_t > overlay_data_
void UseCachedTileset(const std::vector< uint8_t > &cached_gfx)
Use a pre-computed tileset from cache instead of rebuilding.
gfx::SnesPalette current_palette_
static bool SupportsCustomBGColors(OverworldVersion version)
Check if ROM supports custom background colors per area (v2+)
static OverworldVersion GetVersion(const Rom &rom)
Detect ROM version from ASM marker byte.
static uint8_t GetAsmVersion(const Rom &rom)
Get raw ASM version byte from ROM.
static bool SupportsAreaEnum(OverworldVersion version)
Check if ROM supports area enum system (v3+ only)
static bool SupportsCustomTileGFX(OverworldVersion version)
Check if ROM supports custom tile GFX groups (v3+)
int main(int argc, char **argv)
Definition emu.cc:43
struct destination destination
Room transition destination.
#define ASSIGN_OR_RETURN(type_variable_name, expression)
Definition macro.h:62
void CopyTile8bpp16(int x, int y, int tile, std::vector< uint8_t > &bitmap, std::vector< uint8_t > &blockset)
Definition snes_tile.cc:423
absl::Status SetColorsPalette(Rom &rom, GameData *game_data, int index, gfx::SnesPalette &current, gfx::SnesPalette main, gfx::SnesPalette animated, gfx::SnesPalette aux1, gfx::SnesPalette aux2, gfx::SnesPalette hud, gfx::SnesColor bgrcolor, gfx::SnesPalette spr, gfx::SnesPalette spr2)
Zelda 3 specific classes and functions.
constexpr int kAreaGfxIdPtr
Definition overworld.h:118
int GetOverworldMapParentIdExpanded()
constexpr int OverworldCustomTileGFXGroupEnabled
constexpr int OverworldCustomAreaSpecificBGEnabled
constexpr int kOverworldSpritePaletteGroup
Definition overworld.h:110
constexpr int kOverworldSpriteset
Definition overworld.h:111
constexpr int kOverworldScreenSize
Definition overworld.h:144
constexpr int kOverlayData1
constexpr int kSpecialWorldMapIdStart
constexpr int OverworldCustomMosaicArray
constexpr int OverworldCustomAnimatedGFXEnabled
constexpr int OverworldCustomMainPaletteEnabled
constexpr int kNumOverworldMaps
Definition common.h:85
constexpr int OverworldCustomMainPaletteArray
constexpr int kOverworldSpecialSpritePaletteExpandedTemp
constexpr int kOverworldSpecialSpriteGfxGroupExpandedTemp
constexpr int kOverworldMusicBeginning
Definition overworld.h:121
std::vector< std::vector< uint16_t > > OverworldBlockset
Represents tile32 data for the overworld.
AreaSizeEnum
Area size enumeration for v3+ ROMs.
constexpr int kOverworldMusicDarkWorld
Definition overworld.h:125
constexpr int kOverlayPointersBank
constexpr int kOverworldSpecialPalGroup
Definition overworld.h:113
constexpr int kOverworldSpritePaletteIds
Definition overworld.h:109
constexpr int OverworldCustomASMHasBeenApplied
Definition common.h:89
constexpr int kOverworldMusicAgahnim
Definition overworld.h:124
constexpr int kOverworldMapParentId
Definition overworld.h:141
@ kZSCustomV2
Parent system, BG colors, main palettes.
@ kVanilla
0xFF in ROM, no ZScream ASM applied
@ kZSCustomV3
Area enum, wide/tall areas, all features.
constexpr int kOverworldPalettesScreenToSetNew
constexpr int kOverworldMusicMasterSword
Definition overworld.h:123
constexpr int kOverworldMusicZelda
Definition overworld.h:122
constexpr int kOverworldMessageIds
Definition overworld.h:119
constexpr int kOverlayData2
constexpr int OverworldCustomAnimatedGFXArray
constexpr int kDarkWorldMapIdStart
constexpr int OverworldCustomMosaicEnabled
constexpr int OverworldCustomTileGFXGroupArray
constexpr int OverworldCustomSubscreenOverlayEnabled
constexpr int OverworldCustomAreaSpecificBGPalette
int GetOverworldMessagesExpanded()
constexpr int kOverlayPointers
constexpr int kOverworldMapPaletteIds
Definition overworld.h:108
constexpr int kOverworldSpecialGfxGroup
Definition overworld.h:112
constexpr int OverworldCustomSubscreenOverlayArray
#define RETURN_IF_ERROR(expr)
Definition snes.cc:22
Room transition destination.
Definition zelda.h:448
Represents a group of palettes.
Represents a set of palettes used in a SNES graphics system.
gfx::PaletteGroupMap palette_groups
Definition game_data.h:89
std::vector< uint8_t > graphics_buffer
Definition game_data.h:82
uint32_t kOverworldMapPaletteGroup
Definition zelda.h:98
uint32_t kOverworldGfxGroups1
Definition zelda.h:94
uint32_t kSpriteBlocksetPointer
Definition zelda.h:109
uint32_t kOverworldGfxGroups2
Definition zelda.h:95