8#include <unordered_map>
11#include "absl/status/status.h"
12#include "absl/status/statusor.h"
28 return area == 0x03 || area == 0x05 || area == 0x07 || area == 0x43 ||
29 area == 0x45 || area == 0x47;
85 bool seed_area_parent,
bool skip_legacy_data)
86 : index_(index), parent_(index), rom_(rom), game_data_(game_data) {
87 if (skip_legacy_data) {
152 if (rom ==
nullptr || layout ==
nullptr ||
155 return absl::InvalidArgumentError(
"Invalid Lyra preview map");
165 auto read = [
this](uint16_t id,
166 size_t index) -> absl::StatusOr<std::span<const uint8_t>> {
170 return absl::FailedPreconditionError(
"Lyra preview property unavailable");
177 return absl::FailedPreconditionError(
"Lyra preview parent out of range");
181 return absl::FailedPreconditionError(
"Lyra preview area size invalid");
191 for (
int i = 0; i < 4; ++i) {
211 return absl::OkStatus();
228 if (&area_parent ==
this || area_parent.
index_ ==
index_) {
243 for (
int i = 0; i < 16; ++i) {
251 std::vector<gfx::Tile16>& tiles16,
259 int count,
int game_state,
int world, std::vector<gfx::Tile16>& tiles16,
261 const std::vector<uint8_t>* cached_tileset,
262 const std::vector<uint8_t>* cached_tile16_blockset) {
283 }
else if (
index_ == 0x88) {
301 if (cached_tileset && !cached_tileset->empty()) {
311 cached_tile16_blockset && !cached_tile16_blockset->empty()) {
320 return absl::OkStatus();
505 }
else if (
index_ == 0x80) {
521 }
else if (
index_ == 0x94) {
526 }
else if (
index_ == 0x95) {
530 }
else if (
index_ == 0x96) {
534 }
else if (
index_ == 0x9C) {
554 const int palette_table =
560 for (
int state = 0; state < 3; ++state) {
569 const int gfx_table = expanded
572 const int palette_table = expanded
636 int index_world = 0x20;
647 for (
int i = 0; i < 8; i++) {
649 (uint8_t)(*
rom_)[overworld_gfx_groups2 + (index_world * 8) + i];
655 uint8_t temp = (*rom_)[overworldgfxGroups + (
area_graphics_ * 4)];
714 }
else if (
index_ == 0x80) {
771 (mosaicByte & 0x08) != 0x00, (mosaicByte & 0x04) != 0x00,
772 (mosaicByte & 0x02) != 0x00, (mosaicByte & 0x01) != 0x00};
820 for (
int i = 0; i < 8; i++) {
826 int index_world = 0x20;
835 for (
int i = 0; i < 8; i++) {
838 (index_world * 8) + i];
846 uint8_t temp = (*rom_)[overworldgfxGroups + (
area_graphics_ * 4)];
900 }
else if (
index_ == 0x80) {
903 }
else if (
index_ == 0x88) {
973 std::array<uint8_t, 8> sheets{};
974 for (
int i = 0; i < 8; ++i) {
976 main_group_id * 8 + i];
983 if (slot < 0 || slot >= 8) {
992 }
else if (slot >= 3 && slot <= 6 &&
1007 std::array<uint8_t, 4> sheets{};
1008 for (
int i = 0; i < 4; ++i) {
1020 std::array<uint8_t, 4> sheets{};
1021 for (
int i = 0; i < 4; ++i) {
1029 std::array<uint8_t, 16> sheets{};
1033 std::copy(
main.begin(),
main.end(), sheets.begin());
1034 for (
int i = 0; i < 4; ++i) {
1036 sheets[3 + i] = area[i];
1038 sheets[12 + i] =
static_cast<uint8_t
>(sprites[i] + 0x73);
1049 const int world_offset = (
parent_ & 0xC0) >> 3;
1050 for (
int i = 0; i < 8; ++i) {
1051 const uint8_t custom_sheet = area_props.custom_gfx_ids[i];
1053 custom_sheet == 0xFF
1087 const bool custom_animated =
1091 if (custom_animated) {
1092 if (sheet == 0x00 || sheet == 0xFF) {
1093 const int world_offset = (
parent_ & 0xC0) >> 3;
1096 }
else if (sheet == 0x00 || sheet == 0xFF) {
1097 sheet = IsDeathMountainArea(
parent_) ? 0x59 : 0x5B;
1108namespace palette_internal {
1117 std::array<int, 3> palette_ids) {
1122 std::array<gfx::SnesColor, 256> new_palette = {};
1127 for (
int y = 2; y < 7; y++) {
1128 for (
int x = 1; x < 8; x++) {
1129 new_palette[x + (16 * y)] =
main[k];
1137 for (
int x = 1; x < 8; x++) {
1138 new_palette[(16 * 7) + (x)] = animated[(x - 1)];
1145 for (
int y = 2; y < 5; y++) {
1146 for (
int x = 9; x < 16; x++) {
1147 new_palette[x + (16 * y)] = aux1[k];
1156 for (
int y = 5; y < 8; y++) {
1157 for (
int x = 9; x < 16; x++) {
1158 new_palette[x + (16 * y)] = aux2[k];
1166 for (
int i = 0; i < 32; i++) {
1167 new_palette[i] = hud[i];
1171 for (
int i = 0; i < 8; i++) {
1172 new_palette[(i * 16)] = bgrcolor;
1173 new_palette[(i * 16) + 8] = bgrcolor;
1178 for (
int y = 8; y < 9; y++) {
1179 for (
int x = 1; x < 8; x++) {
1187 for (
int y = 8; y < 9; y++) {
1188 for (
int x = 9; x < 16; x++) {
1196 for (
int y = 9; y < 13; y++) {
1197 for (
int x = 1; x < 16; x++) {
1198 new_palette[x + (16 * y)] =
1206 for (
int y = 13; y < 14; y++) {
1207 for (
int x = 1; x < 8; x++) {
1208 new_palette[x + (16 * y)] = spr[k];
1215 for (
int y = 14; y < 15; y++) {
1216 for (
int x = 1; x < 8; x++) {
1217 new_palette[x + (16 * y)] = spr2[k];
1224 for (
int y = 15; y < 16; y++) {
1225 for (
int x = 1; x < 16; x++) {
1231 for (
int i = 0; i < 256; i++) {
1232 current[i] = new_palette[i];
1233 current[(i / 16) * 16].set_transparent(
true);
1237 return absl::OkStatus();
1243 int limit,
int* resolved_index) {
1246 (previous_index * 4)];
1248 if (
index >= limit) {
1252 *resolved_index =
index;
1253 return palette_group[
index];
1260 return absl::OkStatus();
1266 int previous_pal_id = 0;
1267 int previous_spr_pal_id = 0;
1275 return absl::FailedPreconditionError(
1276 "Lyra preview previous palette unavailable");
1278 previous_pal_id = (*previous)[0];
1283 if (!previous_sprite) {
1284 return absl::FailedPreconditionError(
1285 "Lyra preview previous sprite palette unavailable");
1287 previous_spr_pal_id = (*previous_sprite)[0];
1301 const int render_area_palette = std::min((
int)
area_palette_, 0xA3);
1305 (render_area_palette * 4)];
1307 (render_area_palette * 4) + 1];
1309 (render_area_palette * 4) + 2];
1316 auto bgr = grass_pal_group[0][0];
1321 (previous_pal_id * 4)];
1326 (previous_pal_id * 4) + 1];
1331 (previous_pal_id * 4) + 2];
1339 GetPalette(ow_aux_pal_group, pal1, previous_pal_id, 20, &palette_ids[1]));
1342 GetPalette(ow_aux_pal_group, pal2, previous_pal_id, 20, &palette_ids[2]));
1345 bool use_area_specific_bg =
1349 if (use_area_specific_bg) {
1355 bgr = grass_pal_group[0][0];
1358 bgr = grass_pal_group[0][1];
1360 bgr = grass_pal_group[0][2];
1376 GetPalette(ow_main_pal_group, pal0, previous_pal_id, 255,
1380 GetPalette(ow_animated_pal_group, std::min((
int)pal3, 13),
1381 previous_pal_id, 14));
1406 previous_spr_pal_id, 24));
1409 previous_spr_pal_id, 24));
1418 return absl::OkStatus();
1433 return absl::OkStatus();
1444 address = ((address & 0x7F0000) >> 1) | (address & 0x7FFF);
1452 address = ((address & 0x7F0000) >> 1) | (address & 0x7FFF);
1460 return absl::OkStatus();
1465 uint8_t b = (*rom_)[address];
1468 while (b != 0x60 && address < rom_->size()) {
1540 if (address < rom_->size()) {
1541 b = (*rom_)[address];
1556 return absl::OkStatus();
1597 int size,
const uint8_t* all_gfx) {
1599 static_cast<uint16_t
>(static_graphics_offset));
1601 it->second.size() ==
static_cast<size_t>(size)) {
1602 all_gfx = it->second.data();
1603 static_graphics_offset = 0;
1604 }
else if (static_graphics_offset * size + size >
1608 for (
int i = 0; i < size; i++) {
1613 for (
int i = 0; i < size; i++) {
1614 auto byte = all_gfx[i + (static_graphics_offset * size)];
1634 return absl::OkStatus();
1638 for (
int i = 0; i < 8; i++) {
1646 for (
int i = 8; i < 16; i++) {
1663 return absl::OkStatus();
1670 const size_t dst = 7 * 0x1000;
1692 constexpr size_t kBlocksetBytes = 0x100000;
1693 constexpr size_t kTilesetBytes = 0x10000;
1701 count = std::min({count,
static_cast<int>(tiles16.size()), capacity});
1703 const int offsets[] = {0x00, 0x08, 0x400, 0x408};
1708 for (
int i = 0; i < count; i++) {
1709 const size_t tile_base =
1710 static_cast<size_t>(i / 8) * 0x800 +
static_cast<size_t>(i % 8) * 0x10;
1711 for (
int tile = 0; tile < 4; tile++) {
1713 const size_t src_base =
static_cast<size_t>(info.
id_ / 0x10) * 0x400 +
1714 static_cast<size_t>(info.
id_ % 0x10) * 0x08;
1715 uint8_t* dst_tile = blockset + tile_base + offsets[tile];
1716 if (src_base + 7 * 0x80 + 8 > gfx_size) {
1717 for (
int y = 0; y < 8; ++y) {
1718 std::fill_n(dst_tile + y * 0x80, 8, 0x00);
1722 const uint8_t palette =
static_cast<uint8_t
>(info.
palette_ * 0x10);
1723 for (
int y = 0; y < 8; ++y) {
1725 const uint8_t* src = gfx + src_base + y * 0x80;
1726 uint8_t* dst = dst_tile + my * 0x80;
1728 for (
int x = 0; x < 8; ++x) {
1729 dst[7 - x] =
static_cast<uint8_t
>((src[x] & 0x0F) + palette);
1732 for (
int x = 0; x < 8; ++x) {
1733 dst[x] =
static_cast<uint8_t
>((src[x] & 0x0F) + palette);
1740 return absl::OkStatus();
1752 if (local_index < 0) {
1753 return absl::InvalidArgumentError(
"Invalid overworld map index");
1756 int superY = local_index / 0x08;
1757 int superX = local_index - (superY * 0x08);
1759 for (
int y = 0; y < 0x20; y++) {
1760 for (
int x = 0; x < 0x20; x++) {
1761 auto xt = x + (superX * 0x20);
1762 auto yt = y + (superY * 0x20);
1763 if (xt < 0 || yt < 0 || xt >=
static_cast<int>(world_blockset.size()) ||
1764 yt >=
static_cast<int>(world_blockset[xt].size())) {
1765 return absl::InvalidArgumentError(
1766 "Overworld blockset is too small for map bitmap build");
1772 static std::atomic<uint64_t> next_bitmap_serial{1};
1774 return absl::OkStatus();
1779 bool background, std::vector<uint8_t>* out)
const {
1780 if (out ==
nullptr) {
1781 return absl::InvalidArgumentError(
"Overlay layer output is null");
1783 constexpr int kSize = 0x200;
1784 if (
bitmap_data_.size() <
static_cast<size_t>(kSize) * kSize ||
1786 return absl::FailedPreconditionError(
1787 "Map must be built before its overlay layer");
1789 const int local = overlay_screen & 0x3F;
1790 const int super_x = local % 8;
1791 const int super_y = local / 8;
1793 out->assign(
static_cast<size_t>(kSize) * kSize, 0);
1794 std::vector<uint8_t> tile_pixels(
static_cast<size_t>(kSize) * kSize, 0);
1795 for (
int y = 0; y < 0x20; ++y) {
1796 for (
int x = 0; x < 0x20; ++x) {
1797 const int xt = x + super_x * 0x20;
1798 const int yt = y + super_y * 0x20;
1799 if (xt >=
static_cast<int>(overlay_world_blockset.size()) ||
1800 yt >=
static_cast<int>(overlay_world_blockset[xt].size())) {
1801 return absl::InvalidArgumentError(
1802 "Overlay blockset is too small for the overlay screen");
1808 for (
size_t i = 0; i < tile_pixels.size(); ++i) {
1809 const uint8_t overlay = tile_pixels[i];
1816 (*out)[i] = overlay;
1818 return absl::OkStatus();
The Rom class is used to load, save, and modify Rom data. This is a generic SNES ROM container and do...
Represents a palette of colors for the Super Nintendo Entertainment System (SNES).
void set_size(size_t size)
SNES 16-bit tile metadata container.
uint64_t Revision(uint16_t sheet) const
Represents a single Overworld map screen.
void CopyAnimatedSheetIntoSlot7()
OverworldMainSlotProvenance MainSlotProvenance(int slot) const
void ProcessGraphicsBuffer(int index, int static_graphics_offset, int size, const uint8_t *all_gfx)
zelda3_version_pointers version_constants() const
int ComputeMainBlocksetId() const
const rom::LyraLayoutInspection * descriptor_layout_
std::array< uint8_t, 8 > raw_area_info_
std::vector< uint8_t > current_gfx_
std::array< uint64_t, kNumSourceSheets > source_sheet_revisions_
std::vector< uint8_t > current_blockset_
std::array< bool, 4 > mosaic_expanded_
void RecordSourceSheetRevisions()
absl::Status LoadVanillaOverlayData()
std::vector< uint8_t > bitmap_data_
bool UsesSessionAreaGraphicsGroup() const
std::array< uint8_t, 8 > loaded_area_info_
bool HasGraphicsPreview() const
std::map< uint16_t, std::vector< uint8_t > > graphics_sheet_overrides_
std::array< uint8_t, 16 > ResolveStaticGraphics() const
absl::Status LoadOverlay()
std::array< uint8_t, 4 > ResolveSpriteGraphicsGroup() const
void SetupCustomTileset(uint8_t asm_version)
absl::Status BuildTiles16Gfx(std::vector< gfx::Tile16 > &tiles16, int count)
bool UsesSessionSpriteGraphicsGroup() const
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_
bool UsesSessionMainGraphicsGroup() const
absl::Status BuildTileset()
bool source_assignments_recorded_
static constexpr int kNumSourceSheets
void LoadDeathMountainGFX()
OverworldRoomsetSource roomset_source() const
uint8_t ComputeWorldBasedMainPalette() const
absl::Status BuildBitmap(OverworldBlockset &world_blockset)
std::array< uint8_t, 3 > sprite_palette_
std::array< uint8_t, 4 > area_music_
static absl::StatusOr< OverworldMap > CreateLyraPreview(int index, Rom *rom, GameData *game_data, const rom::LyraLayoutInspection *layout)
std::array< uint8_t, 16 > static_graphics_
uint16_t subscreen_overlay_
std::array< uint8_t, 8 > custom_gfx_ids_
const GameData * game_data() const
absl::Status LoadPalette()
void LoadMainBlocksetId()
bool area_info_baseline_valid_
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, const std::vector< uint8_t > *cached_tile16_blockset=nullptr)
Build map with optional cached tileset for performance.
absl::Status LoadLyraPreviewProperties()
PaletteAddressMap palette_addresses_
int loaded_area_info_parent_
std::array< uint8_t, 4 > ResolveAreaGraphicsGroup() const
void InheritAreaProperties(const OverworldMap &area_parent)
OverworldTilesetKey tileset_key() const
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.
std::optional< AreaRenderProperties > inherited_area_
auto area_specific_bg_color() const
auto sprite_palette(int i) const
bool SourceSheetsChanged() const
gfx::SnesPalette current_palette_
uint16_t SourceSheetForRevision(int i) const
void LoadCustomOverworldData()
void CaptureAreaInfoBaseline()
absl::Status BuildSubscreenOverlayLayer(const OverworldBlockset &overlay_world_blockset, int overlay_screen, bool background, std::vector< uint8_t > *out) const
Build this area's subscreen overlay layer (512x512, 8bpp).
absl::StatusOr< gfx::SnesPalette > GetPalette(const gfx::PaletteGroup &group, int index, int previous_index, int limit, int *resolved_index=nullptr)
AreaGraphicsPreview graphics_preview_
uint16_t area_specific_bg_color_
std::array< uint8_t, 8 > ResolveMainGraphicsGroup() const
AreaRenderProperties area_render_properties() const
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 SupportsExpandedSpace(OverworldVersion version)
Check if ROM uses expanded ROM space for overworld data.
static bool SupportsCustomTileGFX(OverworldVersion version)
Check if ROM supports custom tile GFX groups (v3+)
int main(int argc, char **argv)
#define ASSIGN_OR_RETURN(type_variable_name, expression)
void CopyTile8bpp16(int x, int y, int tile, std::vector< uint8_t > &bitmap, const std::vector< uint8_t > &blockset)
std::optional< std::span< const uint8_t > > LyraLayoutRecord(const LyraLayoutInspection &layout, const uint8_t *image, size_t size, uint16_t table_id, size_t index)
bool IsDeathMountainArea(int area)
uint16_t VanillaSubscreenOverlay(int index)
absl::Status SetColorsPalette(Rom &rom, GameData *game_data, int index, gfx::SnesPalette ¤t, gfx::SnesPalette main, gfx::SnesPalette animated, gfx::SnesPalette aux1, gfx::SnesPalette aux2, gfx::SnesPalette hud, gfx::SnesColor bgrcolor, gfx::SnesPalette spr, gfx::SnesPalette spr2, PaletteAddressMap *addresses, std::array< int, 3 > palette_ids)
Zelda 3 specific classes and functions.
constexpr int kAreaGfxIdPtr
int GetOverworldMapParentIdExpanded()
constexpr int OverworldCustomTileGFXGroupEnabled
constexpr int OverworldCustomAreaSpecificBGEnabled
constexpr int kOverworldSpritePaletteGroup
constexpr int kNumMapsPerWorld
constexpr int kOverworldSpriteset
constexpr int kOverworldScreenSize
constexpr int kOverlayData1
constexpr int kSpecialWorldMapIdStart
constexpr int OverworldCustomMosaicArray
void ApplyGraphicsPreview(const AreaGraphicsPreview &preview, std::vector< uint8_t > &graphics)
constexpr int OverworldCustomDefaultGFXGroups
constexpr int OverworldCustomAnimatedGFXEnabled
constexpr int OverworldCustomMainPaletteEnabled
constexpr int kNumOverworldMaps
constexpr int OverworldCustomMainPaletteArray
constexpr int kAnimatedSheetSlotBytes
constexpr int kOverworldSpecialSpritePaletteExpandedTemp
constexpr int kOverworldSpecialSpriteGfxGroupExpandedTemp
constexpr int kOverworldMusicBeginning
std::vector< std::vector< uint16_t > > OverworldBlockset
Represents tile32 data for the overworld.
AreaSizeEnum
Area size enumeration for v3+ ROMs.
constexpr int kOverworldMusicDarkWorld
constexpr int kOverlayPointersBank
std::array< uint8_t, 17 > OverworldTilesetKey
constexpr int kOverworldSpecialPalGroup
constexpr int kOverworldSpritePaletteIds
constexpr uint32_t kEntranceGfxGroup
constexpr int kOverworldMusicAgahnim
bool IsOverworldBackdropPixel(uint8_t index)
constexpr int kOverworldMapParentId
@ 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
constexpr int kOverworldMusicZelda
constexpr int kOverworldMessageIds
constexpr int kOverlayData2
constexpr int kOverworldSpecialSpritePalette
constexpr int OverworldCustomAnimatedGFXArray
constexpr int kDarkWorldMapIdStart
constexpr int OverworldCustomMosaicEnabled
int WorldForOverworldMap(int map_index)
constexpr int kOverworldSpecialSpriteGFXGroup
constexpr int OverworldCustomTileGFXGroupArray
constexpr int OverworldCustomSubscreenOverlayEnabled
constexpr int OverworldCustomAreaSpecificBGPalette
int GetOverworldMessagesExpanded()
constexpr int kOverlayPointers
int LegacyScreenSizeTableIndexForMap(int map_index)
constexpr int kOverworldMapPaletteIds
constexpr int kOverworldSpecialGfxGroup
constexpr int OverworldCustomSubscreenOverlayArray
std::array< std::optional< PaletteColorAddress >, 128 > PaletteAddressMap
#define RETURN_IF_ERROR(expr)
PaletteGroup overworld_main
PaletteGroup overworld_aux
PaletteGroup sprites_aux1
PaletteGroup overworld_animated
PaletteGroup sprites_aux3
PaletteGroup global_sprites
Represents a group of palettes.
LyraLayoutPresence presence
Per-area render settings the game reads with the area id ($8A).
std::array< uint8_t, 8 > custom_gfx_ids
uint16_t subscreen_overlay
uint16_t area_specific_bg_color
std::array< std::array< uint8_t, 4 >, kNumSpritesets > spriteset_ids
std::array< std::array< uint8_t, 4 >, kNumRoomBlocksets > room_blockset_ids
gfx::PaletteGroupMap palette_groups
std::array< std::array< uint8_t, 8 >, kNumMainBlocksets > main_blockset_ids
uint32_t gfx_main_group_address
std::vector< uint8_t > & graphics_buffer
Legacy name for sheet_store.mutable_pixels().
GraphicsSheetStore sheet_store
OverworldMainSlotSource source
uint32_t kOverworldMapPaletteGroup
uint32_t kOverworldGfxGroups1
uint32_t kSpriteBlocksetPointer
uint32_t kOverworldGfxGroups2