9#include "absl/strings/str_cat.h"
10#include "absl/strings/str_format.h"
35 "Light Torch to See Floor",
39const std::string
RoomTag[65] = {
"Nothing",
40 "NW Kill Enemy to Open",
41 "NE Kill Enemy to Open",
42 "SW Kill Enemy to Open",
43 "SE Kill Enemy to Open",
44 "W Kill Enemy to Open",
45 "E Kill Enemy to Open",
46 "N Kill Enemy to Open",
47 "S Kill Enemy to Open",
48 "Clear Quadrant to Open",
49 "Clear Full Tile to Open",
50 "NW Push Block to Open",
51 "NE Push Block to Open",
52 "SW Push Block to Open",
53 "SE Push Block to Open",
54 "W Push Block to Open",
55 "E Push Block to Open",
56 "N Push Block to Open",
57 "S Push Block to Open",
60 "Collect Prize to Open",
61 "Hold Switch Open Door",
62 "Toggle Switch to Open Door",
71 "Push Switch Exploding Wall",
73 "Open Chest (Holes 0)",
76 "Defeat Boss for Dungeon Prize",
77 "SE Kill Enemy to Push Block",
78 "Trigger Switch Chest",
79 "Pull Lever Exploding Wall",
80 "NW Kill Enemy for Chest",
81 "NE Kill Enemy for Chest",
82 "SW Kill Enemy for Chest",
83 "SE Kill Enemy for Chest",
84 "W Kill Enemy for Chest",
85 "E Kill Enemy for Chest",
86 "N Kill Enemy for Chest",
87 "S Kill Enemy for Chest",
88 "Clear Quadrant for Chest",
89 "Clear Full Tile for Chest",
90 "Light Torches to Open",
98 "Open Chest for Holes 8",
99 "Push Block for Chest",
100 "Clear Room for Triforce Door",
101 "Light Torches for Chest",
125 auto room_size_address = 0xF8000 + (room_id * 3);
128 if (room_size_address < 0 ||
129 room_size_address + 2 >=
static_cast<int>(rom->
size())) {
134 uint8_t low = rom->
data()[room_size_address];
135 uint8_t high = rom->
data()[room_size_address + 1];
136 uint8_t bank = rom->
data()[room_size_address + 2];
139 int long_address = (bank << 16) | (high << 8) | low;
142 if (long_address == 0x0A8000) {
150 int next_room_address = 0xF8000 + ((room_id + 1) * 3);
153 if (next_room_address < 0 ||
154 next_room_address + 2 >=
static_cast<int>(rom->
size())) {
159 uint8_t next_low = rom->
data()[next_room_address];
160 uint8_t next_high = rom->
data()[next_room_address + 1];
161 uint8_t next_bank = rom->
data()[next_room_address + 2];
164 int next_long_address = (next_bank << 16) | (next_high << 8) | next_low;
167 int actual_room_size = next_long_address - long_address;
184 int room_address = object_pointer + (room_id * 3);
185 int objects_location =
SnesToPc(room_address);
206 Room room(room_id, rom);
222 header_pointer =
SnesToPc(header_pointer);
231 int table_offset = (header_pointer) + (room_id * 2);
232 if (table_offset < 0 || table_offset + 1 >=
static_cast<int>(rom->
size())) {
237 (rom->
data()[table_offset + 1] << 8) +
238 rom->
data()[table_offset];
240 auto header_location =
SnesToPc(address);
243 if (header_location < 0 ||
244 header_location + 13 >=
static_cast<int>(rom->
size())) {
253 room.
SetBg2(background2::DarkRoom);
285 header_pointer_2 =
SnesToPc(header_pointer_2);
294 int table_offset_2 = (header_pointer_2) + (room_id * 2);
295 if (table_offset_2 < 0 ||
296 table_offset_2 + 1 >=
static_cast<int>(rom->
size())) {
301 (rom->
data()[table_offset_2 + 1] << 8) +
302 rom->
data()[table_offset_2];
310 if (hpos < 0 || hpos + 14 >=
static_cast<int>(rom->
size())) {
315 uint8_t b = rom->
data()[hpos];
340 b = rom->
data()[hpos];
367 game_data_(game_data),
387 for (
int i = 0; i < 8; i++) {
392 if (entrance_blockset != 0xFF && room_gfx[entrance_blockset][3] != 0) {
393 blocks_[i] = room_gfx[entrance_blockset][3];
402 for (
int i = 0; i < 4; i++) {
422 LOG_DEBUG(
"Room",
"CopyRoomGraphicsToBuffer: ROM not loaded");
427 LOG_DEBUG(
"Room",
"CopyRoomGraphicsToBuffer: GameData not set");
431 if (gfx_buffer_data->empty()) {
432 LOG_DEBUG(
"Room",
"CopyRoomGraphicsToBuffer: Graphics buffer is empty");
436 LOG_DEBUG(
"Room",
"Room %d: Copying 8BPP graphics (buffer size: %zu)",
443 for (
int block = 0; block < 16; block++) {
447 if (sheet_id >= 223) {
448 LOG_WARN(
"Room",
"Invalid sheet index %d for block %d", sheet_id, block);
454 int src_sheet_offset = sheet_id * 4096;
457 if (src_sheet_offset + 4096 > gfx_buffer_data->size()) {
458 LOG_ERROR(
"Room",
"Graphics offset out of bounds: %d (size: %zu)",
459 src_sheet_offset, gfx_buffer_data->size());
464 int dest_index_base = block * 4096;
467 gfx_buffer_data->data() + src_sheet_offset, 4096);
485 bool properties_changed =
false;
499 properties_changed =
true;
509 properties_changed =
true;
517 if (bg1_bmp.texture() && bg2_bmp.texture()) {
519 "Room %d: No changes detected, skipping render",
room_id_);
525 "Room %d: Rendering graphics (dirty_flags: g=%d o=%d l=%d t=%d)",
544 "Room %d: floor1=%d, floor2=%d, blocks_size=%zu",
room_id_,
549 bool need_floor_draw = was_graphics_dirty;
554 if (!bg1_bmp.is_active() || bg1_bmp.width() == 0 || !bg2_bmp.is_active() ||
555 bg2_bmp.width() == 0) {
556 need_floor_draw =
true;
558 "Room %d: Bitmaps not created yet, forcing floor draw",
room_id_);
561 if (need_floor_draw) {
570 bool need_bg_draw = was_graphics_dirty || need_floor_draw;
585 if (was_layout_dirty || need_floor_draw) {
594 int num_palettes = dungeon_pal_group.
size();
595 if (num_palettes == 0)
603 if (palette < game_data_->paletteset_ids.size() &&
608 if (palette_word.ok()) {
609 palette_id = palette_word.value() / 180;
612 if (palette_id < 0 || palette_id >= num_palettes) {
616 auto bg1_palette = dungeon_pal_group[palette_id];
622 LOG_DEBUG(
"Room",
"RenderRoomGraphics: Palette ID=%d, Size=%zu", palette_id,
624 if (!bg1_palette.empty()) {
625 LOG_DEBUG(
"Room",
"RenderRoomGraphics: First color: R=%d G=%d B=%d",
626 bg1_palette[0].rom_color().red, bg1_palette[0].rom_color().green,
627 bg1_palette[0].rom_color().blue);
634 if (bg1_palette.size() > 0) {
652 std::vector<SDL_Color> colors(
658 constexpr int kColorsPerRomBank = 15;
659 constexpr int kIndicesPerSdlBank = 16;
660 constexpr int kNumBanks = 6;
662 for (
int bank = 0; bank < kNumBanks; bank++) {
665 for (
int color = 0; color < kColorsPerRomBank; color++) {
666 size_t rom_index = bank * kColorsPerRomBank + color;
667 if (rom_index >= pal.size())
671 bank * kIndicesPerSdlBank + color + 1;
672 ImVec4 rgb = pal[rom_index].rgb();
673 colors[sdl_index] = {
674 static_cast<Uint8
>(rgb.x),
static_cast<Uint8
>(rgb.y),
675 static_cast<Uint8
>(rgb.z),
682 colors[255] = {0, 0, 0, 0};
687 SDL_SetColorKey(bmp.
surface(), SDL_TRUE, 255);
688 SDL_SetSurfaceBlendMode(bmp.
surface(), SDL_BLENDMODE_BLEND);
692 set_dungeon_palette(bg1_bmp, bg1_palette);
693 set_dungeon_palette(bg2_bmp, bg1_palette);
698 auto* surface = bg1_bmp.surface();
703 "Room::RenderRoomGraphics (BG1)", palette_id,
true);
707 "Room::RenderRoomGraphics (after SetPalette)", surface);
710 "Room::RenderRoomGraphics", palette_id,
false,
711 "SDL surface has no palette!");
723 if (bg2_bmp.surface()) {
724 SDL_SetSurfaceAlphaMod(bg2_bmp.surface(), 128);
732 if (bg2_bmp.surface()) {
733 SDL_SetSurfaceBlendMode(bg2_bmp.surface(), SDL_BLENDMODE_ADD);
757 LOG_DEBUG(
"[RenderRoomGraphics]",
"Queueing UPDATE for %s",
name);
761 LOG_DEBUG(
"[RenderRoomGraphics]",
"Queueing CREATE for %s",
name);
767 queue_texture(&bg1_bmp,
"bg1_buffer");
768 queue_texture(&bg2_bmp,
"bg2_buffer");
784 "Texture commands queued for batch processing");
792 LOG_DEBUG(
"Room",
"ROM not loaded, aborting");
799 if (!layout_status.ok()) {
801 layout_status.message().data());
806 LOG_DEBUG(
"Room",
"Layout %d has %zu objects",
layout, layout_objects.size());
807 if (layout_objects.empty()) {
815 LOG_DEBUG(
"RenderRoomGraphics",
"GameData not set, cannot render layout");
822 int num_palettes = dungeon_pal_group.
size();
823 if (num_palettes == 0)
826 if (palette < game_data_->paletteset_ids.size() &&
831 if (palette_word.ok()) {
832 palette_id = palette_word.value() / 180;
835 if (palette_id < 0 || palette_id >= num_palettes) {
839 auto room_palette = dungeon_pal_group[palette_id];
854 "RenderRoomGraphics",
"Layout Draw failed: %s",
855 std::string(status.message().data(), status.message().size()).c_str());
857 LOG_DEBUG(
"RenderRoomGraphics",
"Layout rendered with %zu objects",
858 layout_objects.size());
864 "Starting object rendering for room %d",
room_id_);
867 LOG_DEBUG(
"[RenderObjectsToBackground]",
"ROM not loaded, aborting");
876 bool bitmaps_exist = bg1_bmp.is_active() && bg1_bmp.width() > 0 &&
877 bg2_bmp.is_active() && bg2_bmp.width() > 0;
881 "Room %d: Objects not dirty, skipping render",
room_id_);
888 "Room %d: Emulator rendering objects",
room_id_);
894 int num_palettes = dungeon_pal_group.
size();
899 if (palette < game_data_->paletteset_ids.size() &&
904 if (palette_word.ok()) {
905 palette_id = palette_word.value() / 180;
908 if (palette_id < 0 || palette_id >= num_palettes) {
912 auto room_palette = dungeon_pal_group[palette_id];
944 int layer0_count = 0, layer1_count = 0, layer2_count = 0;
946 switch (obj.GetLayerValue()) {
960 "Room %03X Object Layer Summary: L0(BG1)=%d, L1(BG2)=%d, L2(BG3)=%d",
961 room_id_, layer0_count, layer1_count, layer2_count);
975 for (
int i = 0; i < static_cast<int>(
doors_.size()); ++i) {
976 const auto& door =
doors_[i];
978 door_def.
type = door.type;
995 if (pot_item.item != 0) {
997 int tile_x = pot_item.GetTileX();
998 int tile_y = pot_item.GetTileY();
1005 for (
const auto& sprite :
sprites_) {
1006 if (sprite.key_drop() > 0) {
1012 uint8_t key_item = (sprite.key_drop() == 1) ? 0xFD : 0xFE;
1020 "[RenderObjectsToBackground]",
1021 "ObjectDrawer failed: %s - FALLING BACK TO MANUAL",
1022 std::string(status.message().data(), status.message().size()).c_str());
1024 LOG_DEBUG(
"[RenderObjectsToBackground]",
1025 "Room %d: Manual rendering objects (fallback)",
room_id_);
1029 int x = obj.x() * 8;
1030 int y = obj.y() * 8;
1035 uint8_t color_idx = 0;
1036 if (obj.GetLayerValue() == 0) {
1038 }
else if (obj.GetLayerValue() == 1) {
1044 for (
int py = y; py < y + height && py < bg1_bmp.height(); ++py) {
1045 for (
int px = x; px < x + width && px < bg1_bmp.width(); ++px) {
1046 int pixel_offset = (py * bg1_bmp.width()) + px;
1047 bg1_bmp.WriteToPixel(pixel_offset, color_idx);
1055 LOG_DEBUG(
"[RenderObjectsToBackground]",
1056 "Room %d: Objects rendered successfully",
room_id_);
1072 if (gfx_buffer_data->empty()) {
1076 auto rom_data =
rom()->vector();
1077 if (rom_data.empty()) {
1082 if (animated_frame_ < 0 || animated_frame_ > 10) {
1087 if (background_tileset_ < 0 || background_tileset_ > 255) {
1092 if (gfx_ptr < 0 || gfx_ptr >=
static_cast<int>(rom_data.size())) {
1097 while (data < 1024) {
1101 if (first_offset >= 0 &&
1102 first_offset <
static_cast<int>(gfx_buffer_data->size())) {
1103 uint8_t map_byte = (*gfx_buffer_data)[first_offset];
1106 int gfx_offset = data + (7 * 4096);
1107 if (gfx_offset >= 0 &&
1117 if (second_offset >= 0 &&
1118 second_offset <
static_cast<int>(gfx_buffer_data->size())) {
1119 uint8_t map_byte = (*gfx_buffer_data)[second_offset];
1122 int gfx_offset = data + (7 * 4096) - 1024;
1123 if (gfx_offset >= 0 &&
1135 auto rom_data =
rom()->vector();
1141 object_pointer =
SnesToPc(object_pointer);
1144 if (object_pointer < 0 || object_pointer >= (
int)
rom_->
size()) {
1148 int room_address = object_pointer + (
room_id_ * 3);
1151 if (room_address < 0 || room_address + 2 >= (
int)
rom_->
size()) {
1155 int tile_address = (rom_data[room_address + 2] << 16) +
1156 (rom_data[room_address + 1] << 8) + rom_data[room_address];
1161 if (objects_location < 0 || objects_location >= (
int)
rom_->
size()) {
1166 if (objects_location + 1 < (
int)
rom_->
size()) {
1169 static_cast<uint8_t
>(rom_data[objects_location] & 0x0F);
1171 static_cast<uint8_t
>((rom_data[objects_location] >> 4) & 0x0F);
1173 "Room %d: Set floor1_graphics_=%d, floor2_graphics_=%d",
1177 layout =
static_cast<uint8_t
>((rom_data[objects_location + 1] >> 2) & 0x07);
1187 auto rom_data =
rom()->vector();
1193 int nbr_of_staircase = 0;
1195 int pos = objects_location;
1201 bool end_read =
false;
1205 while (!end_read && pos < (
int)
rom_->
size()) {
1207 if (pos + 1 >= (
int)
rom_->
size()) {
1212 b2 = rom_data[pos + 1];
1219 if (b1 == 0xFF && b2 == 0xFF) {
1222 LOG_DEBUG(
"Room",
"Room %03X: Layer transition to layer %d (%s)",
1224 layer == 1 ?
"BG2" : (layer == 2 ?
"BG3" :
"END"));
1234 if (b1 == 0xF0 && b2 == 0xFF) {
1241 if (pos + 2 >= (
int)
rom_->
size()) {
1245 b3 = rom_data[pos + 2];
1256 b1, b2, b3,
static_cast<uint8_t
>(layer));
1258 LOG_DEBUG(
"Room",
"Room %03X: Object 0x%03X at (%d,%d) layer=%d (%s)",
1260 layer == 0 ?
"BG1" : (layer == 1 ?
"BG2" :
"BG3"));
1264 if (r.
id_ >= 0 && r.
id_ <= 0xFFF) {
1278 "ParseDoor: room=%d b1=0x%02X b2=0x%02X pos=%d dir=%d type=%d",
1280 static_cast<int>(door.direction),
static_cast<int>(door.type));
1291 std::vector<uint8_t> bytes;
1294 std::vector<RoomObject> layer0_objects;
1295 std::vector<RoomObject> layer1_objects;
1296 std::vector<RoomObject> layer2_objects;
1299 switch (obj.GetLayerValue()) {
1301 layer0_objects.push_back(obj);
1304 layer1_objects.push_back(obj);
1307 layer2_objects.push_back(obj);
1313 for (
const auto& obj : layer0_objects) {
1314 auto encoded = obj.EncodeObjectToBytes();
1315 bytes.push_back(encoded.b1);
1316 bytes.push_back(encoded.b2);
1317 bytes.push_back(encoded.b3);
1319 bytes.push_back(0xFF);
1320 bytes.push_back(0xFF);
1323 for (
const auto& obj : layer1_objects) {
1324 auto encoded = obj.EncodeObjectToBytes();
1325 bytes.push_back(encoded.b1);
1326 bytes.push_back(encoded.b2);
1327 bytes.push_back(encoded.b3);
1329 bytes.push_back(0xFF);
1330 bytes.push_back(0xFF);
1333 for (
const auto& obj : layer2_objects) {
1334 auto encoded = obj.EncodeObjectToBytes();
1335 bytes.push_back(encoded.b1);
1336 bytes.push_back(encoded.b2);
1337 bytes.push_back(encoded.b3);
1341 bytes.push_back(0xFF);
1342 bytes.push_back(0xFF);
1348 std::vector<uint8_t> bytes;
1350 for (
const auto& sprite :
sprites_) {
1358 b2 = (sprite.x() & 0x1F) | ((sprite.subtype() & 0x07) << 5);
1363 b1 = (sprite.y() & 0x1F) | ((sprite.subtype() & 0x18) << 2) |
1364 ((sprite.layer() & 0x01) << 7);
1366 bytes.push_back(b1);
1367 bytes.push_back(b2);
1368 bytes.push_back(b3);
1372 bytes.push_back(0xFF);
1378 if (
rom_ ==
nullptr) {
1379 return absl::InvalidArgumentError(
"ROM pointer is null");
1382 auto rom_data =
rom()->vector();
1388 object_pointer =
SnesToPc(object_pointer);
1390 if (object_pointer < 0 || object_pointer >= (
int)
rom_->
size()) {
1391 return absl::OutOfRangeError(
"Object pointer out of range");
1394 int room_address = object_pointer + (
room_id_ * 3);
1396 if (room_address < 0 || room_address + 2 >= (
int)
rom_->
size()) {
1397 return absl::OutOfRangeError(
"Room address out of range");
1400 int tile_address = (rom_data[room_address + 2] << 16) +
1401 (rom_data[room_address + 1] << 8) + rom_data[room_address];
1405 if (objects_location < 0 || objects_location >= (
int)
rom_->
size()) {
1406 return absl::OutOfRangeError(
"Objects location out of range");
1411 int available_size = room_size_info.
room_size;
1414 int write_pos = objects_location + 2;
1421 if (encoded_bytes.size() > available_size - 2) {
1422 return absl::OutOfRangeError(absl::StrFormat(
1423 "Room %d object data too large! Size: %d, Available: %d",
room_id_,
1424 encoded_bytes.size(), available_size - 2));
1432 if (
rom_ ==
nullptr) {
1433 return absl::InvalidArgumentError(
"ROM pointer is null");
1436 auto rom_data =
rom()->vector();
1440 int sprite_pointer = (0x09 << 16) +
1443 sprite_pointer =
SnesToPc(sprite_pointer);
1445 if (sprite_pointer < 0 ||
1447 return absl::OutOfRangeError(
"Sprite table pointer out of range");
1451 int sprite_address_snes =
1452 (0x09 << 16) + (rom_data[sprite_pointer + (
room_id_ * 2) + 1] << 8) +
1453 rom_data[sprite_pointer + (
room_id_ * 2)];
1455 int sprite_address =
SnesToPc(sprite_address_snes);
1457 if (sprite_address < 0 || sprite_address >= (
int)
rom_->
size()) {
1458 return absl::OutOfRangeError(
"Sprite address out of range");
1463 int next_sprite_address_snes =
1465 (rom_data[sprite_pointer + ((
room_id_ + 1) * 2) + 1] << 8) +
1466 rom_data[sprite_pointer + ((
room_id_ + 1) * 2)];
1468 int next_sprite_address =
SnesToPc(next_sprite_address_snes);
1471 int available_size = next_sprite_address - sprite_address;
1474 if (available_size <= 0 || available_size > 0x1000) {
1478 available_size = 0x100;
1484 return absl::InternalError(absl::StrFormat(
1485 "Cannot determine available sprite space for room %d",
room_id_));
1490 bool has_sort_sprite =
false;
1491 if (rom_data[sprite_address] == 1) {
1492 has_sort_sprite =
true;
1493 sprite_address += 1;
1494 available_size -= 1;
1500 if (encoded_bytes.size() > available_size) {
1501 return absl::OutOfRangeError(absl::StrFormat(
1502 "Room %d sprite data too large! Size: %d, Available: %d",
room_id_,
1503 encoded_bytes.size(), available_size));
1516 return absl::InvalidArgumentError(
"Invalid object parameters");
1523 return absl::OkStatus();
1528 return absl::OutOfRangeError(
"Object index out of range");
1534 return absl::OkStatus();
1539 return absl::OutOfRangeError(
"Object index out of range");
1543 return absl::InvalidArgumentError(
"Invalid object parameters");
1549 return absl::OkStatus();
1555 if (obj.x() == x && obj.y() == y && obj.GetLayerValue() == layer) {
1559 return absl::NotFoundError(
"No object found at position");
1564 if (
object.x() < 0 ||
object.x() > 63)
1566 if (
object.y() < 0 ||
object.y() > 63)
1570 if (
object.GetLayerValue() < 0 ||
object.GetLayerValue() > 2)
1574 if (
object.id_ < 0 || object.id_ > 0xFFF)
1578 if (
object.id_ < 0x100 &&
object.size() > 15)
1585 int& nbr_of_staircase) {
1589 if (nbr_of_staircase < 4) {
1612 }
else if (oid == 0xFB1) {
1622 auto rom_data =
rom()->vector();
1623 int sprite_pointer = (0x04 << 16) +
1626 int sprite_address_snes =
1627 (0x09 << 16) + (rom_data[sprite_pointer + (
room_id_ * 2) + 1] << 8) +
1628 rom_data[sprite_pointer + (
room_id_ * 2)];
1630 int sprite_address =
SnesToPc(sprite_address_snes);
1631 if (rom_data[sprite_address] == 1) {
1634 sprite_address += 1;
1637 uint8_t b1 = rom_data[sprite_address];
1638 uint8_t b2 = rom_data[sprite_address + 1];
1639 uint8_t b3 = rom_data[sprite_address + 2];
1645 sprites_.emplace_back(b3, (b2 & 0x1F), (b1 & 0x1F),
1646 ((b2 & 0xE0) >> 5) + ((b1 & 0x60) >> 2),
1653 if (spr.
id() == 0xE4 && spr.
x() == 0x00 && spr.
y() == 0x1E &&
1659 if (spr.
id() == 0xE4 && spr.
x() == 0x00 && spr.
y() == 0x1D &&
1666 sprite_address += 3;
1671 auto rom_data =
rom()->vector();
1678 for (
size_t i = 0; i < clength; i++) {
1679 if ((((rom_data[cpos + (i * 3) + 1] << 8) + (rom_data[cpos + (i * 3)])) &
1683 if ((((rom_data[cpos + (i * 3) + 1] << 8) + (rom_data[cpos + (i * 3)])) &
1684 0x8000) == 0x8000) {
1689 chest_data{rom_data[cpos + (i * 3) + 2], big});
1695 auto rom_data =
rom()->vector();
1706 "LoadDoors for room %d - doors are loaded via object stream",
1711 auto rom_data =
rom()->vector();
1721 for (
int i = 0; i < bytes_count; i += 2) {
1722 if (i + 1 >= bytes_count)
1729 if (b1 == 0xFF && b2 == 0xFF) {
1734 uint16_t torch_room_id = (b2 << 8) | b1;
1738 while (i < bytes_count) {
1739 if (i + 1 >= bytes_count)
1746 if (b1 == 0xFF && b2 == 0xFF) {
1751 int address = ((b2 & 0x1F) << 8 | b1) >> 1;
1752 uint8_t px = address % 64;
1753 uint8_t py = address >> 6;
1754 uint8_t layer = (b2 & 0x20) >> 5;
1755 bool lit = (b2 & 0x80) == 0x80;
1758 RoomObject torch_obj(0x150, px, py, 0, layer);
1765 LOG_DEBUG(
"Room",
"Loaded torch at (%d,%d) layer=%d lit=%d", px, py,
1774 while (i < bytes_count) {
1775 if (i + 1 >= bytes_count)
1779 if (b1 == 0xFF && b2 == 0xFF) {
1789 auto rom_data =
rom()->vector();
1799 std::vector<uint8_t> blocks_data(blocks_count);
1807 for (
int i = 0; i < 0x80 && i < blocks_count; i++) {
1808 blocks_data[i] = rom_data[pos1 + i];
1810 if (i + 0x80 < blocks_count) {
1811 blocks_data[i + 0x80] = rom_data[pos2 + i];
1813 if (i + 0x100 < blocks_count) {
1814 blocks_data[i + 0x100] = rom_data[pos3 + i];
1816 if (i + 0x180 < blocks_count) {
1817 blocks_data[i + 0x180] = rom_data[pos4 + i];
1822 for (
int i = 0; i < blocks_count; i += 4) {
1823 if (i + 3 >= blocks_count)
1826 uint8_t b1 = blocks_data[i];
1827 uint8_t b2 = blocks_data[i + 1];
1828 uint8_t b3 = blocks_data[i + 2];
1829 uint8_t b4 = blocks_data[i + 3];
1832 uint16_t block_room_id = (b2 << 8) | b1;
1835 if (b3 == 0xFF && b4 == 0xFF) {
1840 int address = ((b4 & 0x1F) << 8 | b3) >> 1;
1841 uint8_t px = address % 64;
1842 uint8_t py = address >> 6;
1843 uint8_t layer = (b4 & 0x20) >> 5;
1846 RoomObject block_obj(0x0E00, px, py, 0, layer);
1852 LOG_DEBUG(
"Room",
"Loaded block at (%d,%d) layer=%d", px, py, layer);
1860 auto rom_data =
rom()->vector();
1874 int ptr_addr = table_addr + (
room_id_ * 2);
1875 if (ptr_addr + 1 >=
static_cast<int>(rom_data.size()))
1878 uint16_t item_ptr = (rom_data[ptr_addr + 1] << 8) | rom_data[ptr_addr];
1881 int item_addr =
SnesToPc(0x010000 | item_ptr);
1886 while (item_addr + 2 <
static_cast<int>(rom_data.size())) {
1888 uint16_t position = (rom_data[item_addr + 1] << 8) | rom_data[item_addr];
1891 if (position == 0xFFFF)
1895 uint8_t item_type = rom_data[item_addr + 2];
1899 pot_item.
item = item_type;
1907 auto rom_data =
rom()->vector();
1910 int pit_entries = rom_data[
pit_count] / 2;
1915 int pit_data_addr =
SnesToPc(pit_ptr);
1917 LOG_DEBUG(
"Room",
"LoadPits: room_id=%d, pit_entries=%d, pit_ptr=0x%06X",
1927 LOG_DEBUG(
"Room",
"Pit destination - target=%d, target_layer=%d",
The Rom class is used to load, save, and modify Rom data. This is a generic SNES ROM container and do...
absl::Status WriteVector(int addr, std::vector< uint8_t > data)
void QueueTextureCommand(TextureCommandType type, Bitmap *bitmap)
void DrawBackground(std::span< uint8_t > gfx16_data)
void DrawFloor(const std::vector< uint8_t > &rom_data, int tile_address, int tile_address_floor, uint8_t floor_graphics)
void EnsureBitmapInitialized()
void ClearPriorityBuffer()
Represents a bitmap image optimized for SNES ROM hacking.
TextureHandle texture() const
void SetPalette(const SnesPalette &palette)
Set the palette for the bitmap using SNES palette format.
SDL_Surface * surface() const
Represents a palette of colors for the Super Nintendo Entertainment System (SNES).
Editor implementation of DungeonState.
Draws dungeon objects to background buffers using game patterns.
absl::Status DrawObjectList(const std::vector< RoomObject > &objects, gfx::BackgroundBuffer &bg1, gfx::BackgroundBuffer &bg2, const gfx::PaletteGroup &palette_group, const DungeonState *state=nullptr, gfx::BackgroundBuffer *layout_bg1=nullptr)
Draw all objects in a room.
void DrawDoor(const DoorDef &door, int door_index, gfx::BackgroundBuffer &bg1, gfx::BackgroundBuffer &bg2, const DungeonState *state=nullptr)
Draw a door to background buffers.
void DrawPotItem(uint8_t item_id, int x, int y, gfx::BackgroundBuffer &bg)
Draw a pot item visualization.
void SetCurrentBitmap(gfx::Bitmap *bitmap)
void LogPaletteLoad(const std::string &location, int palette_id, const gfx::SnesPalette &palette)
static PaletteDebugger & Get()
void LogPaletteApplication(const std::string &location, int palette_id, bool success, const std::string &reason="")
void SetCurrentPalette(const gfx::SnesPalette &palette)
void LogSurfaceState(const std::string &location, SDL_Surface *surface)
RoomLayerManager - Manages layer visibility and compositing.
void CompositeToOutput(Room &room, gfx::Bitmap &output) const
Composite all visible layers into a single output bitmap.
const std::vector< RoomObject > & GetObjects() const
absl::Status Draw(int room_id, const uint8_t *gfx_data, gfx::BackgroundBuffer &bg1, gfx::BackgroundBuffer &bg2, const gfx::PaletteGroup &palette_group, DungeonState *state) const
absl::Status LoadLayout(int layout_id)
static RoomObject DecodeObjectFromBytes(uint8_t b1, uint8_t b2, uint8_t b3, uint8_t layer)
void set_options(ObjectOption options)
void SetBlockset(uint8_t blockset)
bool ValidateObject(const RoomObject &object) const
std::vector< RoomObject > tile_objects_
void SetSpriteset(uint8_t spriteset)
gfx::BackgroundBuffer object_bg1_buffer_
void SetTag2Direct(TagKey tag2)
absl::Status UpdateObject(size_t index, const RoomObject &object)
void SetStair4Target(uint8_t target)
void SetPitsTarget(uint8_t target)
void SetIsLight(bool is_light)
gfx::BackgroundBuffer bg2_buffer_
uint8_t cached_floor2_graphics_
std::vector< zelda3::Sprite > sprites_
void SetLoaded(bool loaded)
void CopyRoomGraphicsToBuffer()
zelda3_version_pointers version_constants() const
void LoadRoomGraphics(uint8_t entrance_blockset=0xFF)
std::vector< Door > doors_
void SetStaircaseRoom(int index, uint8_t room)
absl::Status RemoveObject(size_t index)
void SetStair1TargetLayer(uint8_t layer)
void SetLayer2Mode(uint8_t mode)
void LoadLayoutTilesToBuffer()
void SetTag2(TagKey tag2)
void ParseObjectsFromLocation(int objects_location)
uint8_t cached_floor1_graphics_
void SetTag1Direct(TagKey tag1)
void SetPaletteDirect(uint8_t palette)
void SetStair2Target(uint8_t target)
void SetCollision(CollisionKey collision)
gfx::BackgroundBuffer bg1_buffer_
absl::Status SaveObjects()
void SetStaircasePlane(int index, uint8_t plane)
void SetIsDark(bool is_dark)
void RenderRoomGraphics()
gfx::Bitmap composite_bitmap_
Room & operator=(Room &&)
uint8_t staircase_rooms_[4]
gfx::Bitmap & GetCompositeBitmap(RoomLayerManager &layer_mgr)
Get a composite bitmap of all layers merged.
std::vector< uint8_t > EncodeObjects() const
void SetEffect(EffectKey effect)
gfx::BackgroundBuffer object_bg2_buffer_
std::array< uint8_t, 16 > blocks_
void SetTag1(TagKey tag1)
void SetBackgroundTileset(uint8_t tileset)
void SetStair3TargetLayer(uint8_t layer)
void SetMessageIdDirect(uint16_t message_id)
absl::Status SaveSprites()
void SetLayerMerging(LayerMergeType merging)
void SetPitsTargetLayer(uint8_t layer)
void SetSpriteTileset(uint8_t tileset)
void SetStair1Target(uint8_t target)
void SetBg2(background2 bg2)
std::unique_ptr< DungeonState > dungeon_state_
void LoadAnimatedGraphics()
std::vector< uint8_t > EncodeSprites() const
std::vector< chest_data > chests_in_room_
LayerMergeType layer_merging_
uint8_t cached_spriteset_
std::array< uint8_t, 0x10000 > current_gfx16_
std::vector< staircase > z3_staircases_
void SetPalette(uint8_t palette)
std::vector< PotItem > pot_items_
void SetHolewarp(uint8_t holewarp)
uint8_t background_tileset_
void SetStair3Target(uint8_t target)
void HandleSpecialObjects(short oid, uint8_t posX, uint8_t posY, int &nbr_of_staircase)
void SetStair4TargetLayer(uint8_t layer)
absl::Status AddObject(const RoomObject &object)
absl::StatusOr< size_t > FindObjectAt(int x, int y, int layer) const
void SetStair2TargetLayer(uint8_t layer)
void RenderObjectsToBackground()
void SetLayer2Behavior(uint8_t behavior)
A class for managing sprites in the overworld and underworld.
auto set_key_drop(int key)
zelda3_bg2_effect
Background layer 2 effects.
#define LOG_DEBUG(category, format,...)
#define LOG_ERROR(category, format,...)
#define LOG_WARN(category, format,...)
constexpr int kGfxBufferAnimatedFrameStride
const std::string RoomTag[65]
constexpr int kGfxBufferAnimatedFrameOffset
constexpr int chests_length_pointer
Room LoadRoomHeaderFromRom(Rom *rom, int room_id)
constexpr int chests_data_pointer1
constexpr int blocks_pointer3
constexpr int blocks_pointer4
constexpr int NumberOfRooms
constexpr int kGfxBufferRoomOffset
constexpr int rooms_sprite_pointer
constexpr int kGfxBufferRoomSpriteOffset
RoomSize CalculateRoomSize(Rom *rom, int room_id)
constexpr int messages_id_dungeon
constexpr int blocks_length
constexpr int tile_address_floor
constexpr int blocks_pointer2
constexpr int kRoomItemsPointers
constexpr int kGfxBufferRoomSpriteStride
constexpr uint16_t stairsObjects[]
Room LoadRoomFromRom(Rom *rom, int room_id)
constexpr uint32_t kDungeonPalettePointerTable
constexpr int blocks_pointer1
constexpr int torches_length_pointer
const std::string RoomEffect[8]
constexpr int kRoomHeaderPointer
constexpr int kRoomHeaderPointerBank
constexpr int kGfxBufferStride
constexpr int room_object_pointer
constexpr int kGfxBufferRoomSpriteLastLineOffset
constexpr int tile_address
constexpr int pit_pointer
constexpr int dungeon_spr_ptrs
constexpr int kGfxBufferOffset
uint32_t SnesToPc(uint32_t addr) noexcept
SDL2/SDL3 compatibility layer.
Legacy chest data structure.
PaletteGroup dungeon_main
Represents a group of palettes.
void AddPalette(SnesPalette pal)
std::array< std::array< uint8_t, 4 >, kNumSpritesets > spriteset_ids
std::array< std::array< uint8_t, 4 >, kNumRoomBlocksets > room_blockset_ids
std::array< std::array< uint8_t, 4 >, kNumPalettesets > paletteset_ids
gfx::PaletteGroupMap palette_groups
std::array< std::array< uint8_t, 8 >, kNumMainBlocksets > main_blockset_ids
std::vector< uint8_t > graphics_buffer
int64_t room_size_pointer
static Door FromRomBytes(uint8_t b1, uint8_t b2)
Yet Another Zelda3 Editor (YAZE) - Public C API.