9#include "absl/strings/str_cat.h"
35 "Light Torch to See Floor",
42 "NW Kill Enemy to Open",
43 "NE Kill Enemy to Open",
44 "SW Kill Enemy to Open",
45 "SE Kill Enemy to Open",
46 "W Kill Enemy to Open",
47 "E Kill Enemy to Open",
48 "N Kill Enemy to Open",
49 "S Kill Enemy to Open",
50 "Clear Quadrant to Open",
51 "Clear Full Tile to Open",
52 "NW Push Block to Open",
53 "NE Push Block to Open",
54 "SW Push Block to Open",
55 "SE Push Block to Open",
56 "W Push Block to Open",
57 "E Push Block to Open",
58 "N Push Block to Open",
59 "S Push Block to Open",
62 "Collect Prize to Open",
63 "Hold Switch Open Door",
64 "Toggle Switch to Open Door",
73 "Push Switch Exploding Wall",
75 "Open Chest (Holes 0)",
78 "Defeat Boss for Dungeon Prize",
79 "SE Kill Enemy to Push Block",
80 "Trigger Switch Chest",
81 "Pull Lever Exploding Wall",
82 "NW Kill Enemy for Chest",
83 "NE Kill Enemy for Chest",
84 "SW Kill Enemy for Chest",
85 "SE Kill Enemy for Chest",
86 "W Kill Enemy for Chest",
87 "E Kill Enemy for Chest",
88 "N Kill Enemy for Chest",
89 "S Kill Enemy for Chest",
90 "Clear Quadrant for Chest",
91 "Clear Full Tile for Chest",
92 "Light Torches to Open",
100 "Open Chest for Holes 8",
101 "Push Block for Chest",
102 "Clear Room for Triforce Door",
103 "Light Torches for Chest",
128 auto room_size_address = 0xF8000 + (room_id * 3);
131 if (room_size_address < 0 || room_size_address + 2 >=
static_cast<int>(rom->
size())) {
136 uint8_t low = rom->
data()[room_size_address];
137 uint8_t high = rom->
data()[room_size_address + 1];
138 uint8_t bank = rom->
data()[room_size_address + 2];
141 int long_address = (bank << 16) | (high << 8) | low;
144 if (long_address == 0x0A8000) {
152 int next_room_address = 0xF8000 + ((room_id + 1) * 3);
155 if (next_room_address < 0 || next_room_address + 2 >=
static_cast<int>(rom->
size())) {
160 uint8_t next_low = rom->
data()[next_room_address];
161 uint8_t next_high = rom->
data()[next_room_address + 1];
162 uint8_t next_bank = rom->
data()[next_room_address + 2];
165 int next_long_address = (next_bank << 16) | (next_high << 8) | next_low;
168 int actual_room_size = next_long_address - long_address;
185 int room_address = object_pointer + (room_id * 3);
186 int objects_location =
SnesToPc(room_address);
207 Room room(room_id, rom);
214 if (kRoomHeaderPointer < 0 || kRoomHeaderPointer + 2 >=
static_cast<int>(rom->
size())) {
222 header_pointer =
SnesToPc(header_pointer);
225 if (kRoomHeaderPointerBank < 0 || kRoomHeaderPointerBank >=
static_cast<int>(rom->
size())) {
230 int table_offset = (header_pointer) + (room_id * 2);
231 if (table_offset < 0 || table_offset + 1 >=
static_cast<int>(rom->
size())) {
236 (rom->
data()[table_offset + 1] << 8) +
237 rom->
data()[table_offset];
239 auto header_location =
SnesToPc(address);
242 if (header_location < 0 || header_location + 13 >=
static_cast<int>(rom->
size())) {
251 room.
SetBg2(background2::DarkRoom);
275 if (kRoomHeaderPointer < 0 || kRoomHeaderPointer + 2 >=
static_cast<int>(rom->
size())) {
282 header_pointer_2 =
SnesToPc(header_pointer_2);
285 if (kRoomHeaderPointerBank < 0 || kRoomHeaderPointerBank >=
static_cast<int>(rom->
size())) {
290 int table_offset_2 = (header_pointer_2) + (room_id * 2);
291 if (table_offset_2 < 0 || table_offset_2 + 1 >=
static_cast<int>(rom->
size())) {
296 (rom->
data()[table_offset_2 + 1] << 8) +
297 rom->
data()[table_offset_2];
305 if (hpos < 0 || hpos + 14 >=
static_cast<int>(rom->
size())) {
310 uint8_t b = rom->
data()[hpos];
335 b = rom->
data()[hpos];
362 game_data_(game_data),
372 printf(
"[LoadRoomGraphics] GameData not set for room %d\n",
room_id_);
380 printf(
"[LoadRoomGraphics] Room %d: blockset=%d, spriteset=%d, palette=%d\n",
383 for (
int i = 0; i < 8; i++) {
388 if (entrance_blockset != 0xFF &&
389 room_gfx[entrance_blockset][3] != 0) {
390 blocks_[i] = room_gfx[entrance_blockset][3];
399 for (
int i = 0; i < 4; i++) {
404 printf(
"[LoadRoomGraphics] Sheet IDs for blocks 0-15:\n");
405 printf(
" BG blocks 0-7: ");
406 for (
int i = 0; i < 8; i++) printf(
"%3d ",
blocks_[i]);
407 printf(
"\n Sprite blocks 8-15: ");
408 for (
int i = 8; i < 16; i++) printf(
"%3d ",
blocks_[i]);
423 printf(
"[CopyRoomGraphicsToBuffer] ROM not loaded\n");
428 printf(
"[CopyRoomGraphicsToBuffer] GameData not set\n");
432 if (gfx_buffer_data->empty()) {
433 printf(
"[CopyRoomGraphicsToBuffer] Graphics buffer is empty\n");
439 printf(
"[CopyRoomGraphicsToBuffer] Room %d: Copying 8BPP graphics (Linear)\n",
room_id_);
440 printf(
"[CopyRoomGraphicsToBuffer] Buffer size: %zu bytes\n", gfx_buffer_data->size());
446 for (
int block = 0; block < 16; block++) {
450 if (sheet_id >= 223) {
451 LOG_WARN(
"Room",
"Invalid sheet index %d for block %d", sheet_id, block);
457 int src_sheet_offset = sheet_id * 4096;
460 if (src_sheet_offset + 4096 > gfx_buffer_data->size()) {
461 LOG_ERROR(
"Room",
"Graphics offset out of bounds: %d (size: %zu)",
462 src_sheet_offset, gfx_buffer_data->size());
468 printf(
"[CopyRoomGraphicsToBuffer] Block %d (Sheet %d): Offset %d\n",
469 block, sheet_id, src_sheet_offset);
470 printf(
" Bytes: %02X %02X %02X %02X %02X %02X %02X %02X\n",
471 (*gfx_buffer_data)[src_sheet_offset],
472 (*gfx_buffer_data)[src_sheet_offset+1],
473 (*gfx_buffer_data)[src_sheet_offset+2],
474 (*gfx_buffer_data)[src_sheet_offset+3],
475 (*gfx_buffer_data)[src_sheet_offset+4],
476 (*gfx_buffer_data)[src_sheet_offset+5],
477 (*gfx_buffer_data)[src_sheet_offset+6],
478 (*gfx_buffer_data)[src_sheet_offset+7]);
482 int dest_index_base = block * 4096;
485 gfx_buffer_data->data() + src_sheet_offset, 4096);
488 printf(
"[CopyRoomGraphicsToBuffer] Room %d: Copied graphics blocks\n",
492 printf(
"[CopyRoomGraphicsToBuffer] First 16 bytes of current_gfx16_:\n");
493 for (
int i = 0; i < 16; i++) {
503 printf(
"[CopyRoomGraphicsToBuffer] Zero bytes (transparent): %d / %zu (%.1f%%)\n",
520 bool properties_changed =
false;
534 properties_changed =
true;
544 properties_changed =
true;
552 if (bg1_bmp.texture() && bg2_bmp.texture()) {
554 "Room %d: No changes detected, skipping render",
room_id_);
560 "Room %d: Rendering graphics (dirty_flags: g=%d o=%d l=%d t=%d)",
579 "Room %d: floor1=%d, floor2=%d, blocks_size=%zu",
room_id_,
584 bool need_floor_draw = was_graphics_dirty;
589 if (!bg1_bmp.is_active() || bg1_bmp.width() == 0 || !bg2_bmp.is_active() ||
590 bg2_bmp.width() == 0) {
591 need_floor_draw =
true;
593 "Room %d: Bitmaps not created yet, forcing floor draw",
room_id_);
596 if (need_floor_draw) {
605 bool need_bg_draw = was_graphics_dirty || need_floor_draw;
620 if (was_layout_dirty || need_floor_draw) {
628 int num_palettes = dungeon_pal_group.
size();
629 if (num_palettes == 0)
return;
636 if (palette < game_data_->paletteset_ids.size() &&
641 if (palette_word.ok()) {
642 palette_id = palette_word.value() / 180;
645 if (palette_id < 0 || palette_id >= num_palettes) {
649 auto bg1_palette = dungeon_pal_group[palette_id];
653 "Room::RenderRoomGraphics", palette_id, bg1_palette);
655 printf(
"[RenderRoomGraphics] Palette ID: %d, Size: %zu\n", palette_id, bg1_palette.size());
656 if (!bg1_palette.empty()) {
657 printf(
"[RenderRoomGraphics] First color: R=%d G=%d B=%d\n",
658 bg1_palette[0].rom_color().red, bg1_palette[0].rom_color().green, bg1_palette[0].rom_color().blue);
665 if (bg1_palette.size() > 0) {
672 std::vector<SDL_Color> colors(256);
677 for (
size_t i = 0; i < pal.size() && i < 256; ++i) {
678 ImVec4 rgb = pal[i].rgb();
680 static_cast<Uint8
>(rgb.x),
681 static_cast<Uint8
>(rgb.y),
682 static_cast<Uint8
>(rgb.z),
689 colors[255] = {0, 0, 0, 0};
697 SDL_SetColorKey(bmp.
surface(), SDL_TRUE, 255);
698 SDL_SetSurfaceBlendMode(bmp.
surface(), SDL_BLENDMODE_BLEND);
702 set_dungeon_palette(bg1_bmp, bg1_palette);
703 set_dungeon_palette(bg2_bmp, bg1_palette);
708 auto* surface = bg1_bmp.surface();
713 "Room::RenderRoomGraphics (BG1)", palette_id,
true);
717 "Room::RenderRoomGraphics (after SetPalette)", surface);
722 if (c.r != 156 || c.g != 107 || c.b != 107) {
724 "Room::RenderRoomGraphics", palette_id,
false,
725 absl::StrFormat(
"Color 56 mismatch: got R=%d G=%d B=%d", c.r,
731 "Room::RenderRoomGraphics", palette_id,
false,
732 "SDL surface has no palette!");
744 if (bg2_bmp.surface()) {
745 SDL_SetSurfaceAlphaMod(bg2_bmp.surface(), 128);
753 if (bg2_bmp.surface()) {
754 SDL_SetSurfaceBlendMode(bg2_bmp.surface(), SDL_BLENDMODE_ADD);
778 LOG_DEBUG(
"[RenderRoomGraphics]",
"Queueing UPDATE for %s",
name);
782 LOG_DEBUG(
"[RenderRoomGraphics]",
"Queueing CREATE for %s",
name);
788 queue_texture(&bg1_bmp,
"bg1_buffer");
789 queue_texture(&bg2_bmp,
"bg2_buffer");
805 "Texture commands queued for batch processing");
812 printf(
"[LoadLayoutTilesToBuffer] START for room %d, layout=%d\n",
room_id_,
layout);
814 LOG_DEBUG(
"RenderRoomGraphics",
"LoadLayoutTilesToBuffer START for room %d",
818 printf(
"[LoadLayoutTilesToBuffer] ROM not loaded, aborting\n");
819 LOG_DEBUG(
"RenderRoomGraphics",
"ROM not loaded, aborting");
826 if (!layout_status.ok()) {
827 printf(
"[LoadLayoutTilesToBuffer] Failed to load layout %d: %s\n",
828 layout, std::string(layout_status.message()).c_str());
829 LOG_DEBUG(
"RenderRoomGraphics",
"Failed to load layout %d: %s",
830 layout, layout_status.message().data());
835 printf(
"[LoadLayoutTilesToBuffer] Layout %d has %zu objects\n",
layout, layout_objects.size());
837 LOG_DEBUG(
"RenderRoomGraphics",
"Layout has %zu objects",
838 layout_objects.size());
839 if (layout_objects.empty()) {
847 LOG_DEBUG(
"RenderRoomGraphics",
"GameData not set, cannot render layout");
854 int num_palettes = dungeon_pal_group.
size();
855 if (num_palettes == 0)
return;
857 if (palette < game_data_->paletteset_ids.size() &&
862 if (palette_word.ok()) {
863 palette_id = palette_word.value() / 180;
866 if (palette_id < 0 || palette_id >= num_palettes) {
870 auto room_palette = dungeon_pal_group[palette_id];
884 LOG_DEBUG(
"RenderRoomGraphics",
"Layout Draw failed: %s",
885 std::string(status.message().data(), status.message().size())
888 LOG_DEBUG(
"RenderRoomGraphics",
"Layout rendered with %zu objects",
889 layout_objects.size());
895 "Starting object rendering for room %d",
room_id_);
898 LOG_DEBUG(
"[RenderObjectsToBackground]",
"ROM not loaded, aborting");
907 bool bitmaps_exist = bg1_bmp.is_active() && bg1_bmp.width() > 0 &&
908 bg2_bmp.is_active() && bg2_bmp.width() > 0;
912 "Room %d: Objects not dirty, skipping render",
room_id_);
919 "Room %d: Emulator rendering objects",
room_id_);
924 int num_palettes = dungeon_pal_group.
size();
929 if (palette < game_data_->paletteset_ids.size() &&
934 if (palette_word.ok()) {
935 palette_id = palette_word.value() / 180;
938 if (palette_id < 0 || palette_id >= num_palettes) {
942 auto room_palette = dungeon_pal_group[palette_id];
985 for (
int i = 0; i < static_cast<int>(
doors_.size()); ++i) {
986 const auto& door =
doors_[i];
988 door_def.
type = door.type;
1005 if (pot_item.item != 0) {
1007 int tile_x = pot_item.GetTileX();
1008 int tile_y = pot_item.GetTileY();
1015 for (
const auto& sprite :
sprites_) {
1016 if (sprite.key_drop() > 0) {
1022 uint8_t key_item = (sprite.key_drop() == 1) ? 0xFD : 0xFE;
1029 LOG_DEBUG(
"[RenderObjectsToBackground]",
"ObjectDrawer failed: %s - FALLING BACK TO MANUAL",
1030 std::string(status.message().data(), status.message().size()).c_str());
1032 LOG_DEBUG(
"[RenderObjectsToBackground]",
1033 "Room %d: Manual rendering objects (fallback)",
room_id_);
1037 int x = obj.x() * 8;
1038 int y = obj.y() * 8;
1043 uint8_t color_idx = 0;
1044 if (obj.GetLayerValue() == 0) {
1046 }
else if (obj.GetLayerValue() == 1) {
1052 for (
int py = y; py < y + height && py < bg1_bmp.height(); ++py) {
1053 for (
int px = x; px < x + width && px < bg1_bmp.width(); ++px) {
1054 int pixel_offset = (py * bg1_bmp.width()) + px;
1055 bg1_bmp.WriteToPixel(pixel_offset, color_idx);
1063 LOG_DEBUG(
"[RenderObjectsToBackground]",
1064 "Room %d: Objects rendered successfully",
room_id_);
1080 if (gfx_buffer_data->empty()) {
1084 auto rom_data =
rom()->vector();
1085 if (rom_data.empty()) {
1090 if (animated_frame_ < 0 || animated_frame_ > 10) {
1095 if (background_tileset_ < 0 || background_tileset_ > 255) {
1100 if (gfx_ptr < 0 || gfx_ptr >=
static_cast<int>(rom_data.size())) {
1105 while (data < 1024) {
1109 if (first_offset >= 0 &&
1110 first_offset <
static_cast<int>(gfx_buffer_data->size())) {
1111 uint8_t map_byte = (*gfx_buffer_data)[first_offset];
1114 int gfx_offset = data + (7 * 4096);
1115 if (gfx_offset >= 0 &&
1123 int second_offset = data + (tileset_index * 4096) + (1024 *
animated_frame_);
1124 if (second_offset >= 0 &&
1125 second_offset <
static_cast<int>(gfx_buffer_data->size())) {
1126 uint8_t map_byte = (*gfx_buffer_data)[second_offset];
1129 int gfx_offset = data + (7 * 4096) - 1024;
1130 if (gfx_offset >= 0 &&
1142 auto rom_data =
rom()->vector();
1148 object_pointer =
SnesToPc(object_pointer);
1151 if (object_pointer < 0 || object_pointer >= (
int)
rom_->
size()) {
1155 int room_address = object_pointer + (
room_id_ * 3);
1158 if (room_address < 0 || room_address + 2 >= (
int)
rom_->
size()) {
1162 int tile_address = (rom_data[room_address + 2] << 16) +
1163 (rom_data[room_address + 1] << 8) + rom_data[room_address];
1168 if (objects_location < 0 || objects_location >= (
int)
rom_->
size()) {
1173 if (objects_location + 1 < (
int)
rom_->
size()) {
1176 static_cast<uint8_t
>(rom_data[objects_location] & 0x0F);
1178 static_cast<uint8_t
>((rom_data[objects_location] >> 4) & 0x0F);
1180 "Room %d: Set floor1_graphics_=%d, floor2_graphics_=%d",
1184 layout =
static_cast<uint8_t
>((rom_data[objects_location + 1] >> 2) & 0x07);
1194 auto rom_data =
rom()->vector();
1200 int nbr_of_staircase = 0;
1202 int pos = objects_location;
1208 bool end_read =
false;
1212 while (!end_read && pos < (
int)
rom_->
size()) {
1214 if (pos + 1 >= (
int)
rom_->
size()) {
1219 b2 = rom_data[pos + 1];
1226 if (b1 == 0xFF && b2 == 0xFF) {
1238 if (b1 == 0xF0 && b2 == 0xFF) {
1245 if (pos + 2 >= (
int)
rom_->
size()) {
1249 b3 = rom_data[pos + 2];
1260 b1, b2, b3,
static_cast<uint8_t
>(layer));
1264 if (r.
id_ >= 0 && r.
id_ <= 0xFFF) {
1277 printf(
"[ParseDoor] room=%d b1=0x%02X b2=0x%02X -> pos=%d dir=%d type=%d\n",
1278 room_id_, b1, b2, door.position,
static_cast<int>(door.direction),
1279 static_cast<int>(door.type));
1290 std::vector<uint8_t> bytes;
1293 std::vector<RoomObject> layer0_objects;
1294 std::vector<RoomObject> layer1_objects;
1295 std::vector<RoomObject> layer2_objects;
1298 switch (obj.GetLayerValue()) {
1300 layer0_objects.push_back(obj);
1303 layer1_objects.push_back(obj);
1306 layer2_objects.push_back(obj);
1312 for (
const auto& obj : layer0_objects) {
1313 auto encoded = obj.EncodeObjectToBytes();
1314 bytes.push_back(encoded.b1);
1315 bytes.push_back(encoded.b2);
1316 bytes.push_back(encoded.b3);
1318 bytes.push_back(0xFF);
1319 bytes.push_back(0xFF);
1322 for (
const auto& obj : layer1_objects) {
1323 auto encoded = obj.EncodeObjectToBytes();
1324 bytes.push_back(encoded.b1);
1325 bytes.push_back(encoded.b2);
1326 bytes.push_back(encoded.b3);
1328 bytes.push_back(0xFF);
1329 bytes.push_back(0xFF);
1332 for (
const auto& obj : layer2_objects) {
1333 auto encoded = obj.EncodeObjectToBytes();
1334 bytes.push_back(encoded.b1);
1335 bytes.push_back(encoded.b2);
1336 bytes.push_back(encoded.b3);
1340 bytes.push_back(0xFF);
1341 bytes.push_back(0xFF);
1347 std::vector<uint8_t> bytes;
1349 for (
const auto& sprite :
sprites_) {
1357 b2 = (sprite.x() & 0x1F) | ((sprite.subtype() & 0x07) << 5);
1362 b1 = (sprite.y() & 0x1F) | ((sprite.subtype() & 0x18) << 2) |
1363 ((sprite.layer() & 0x01) << 7);
1365 bytes.push_back(b1);
1366 bytes.push_back(b2);
1367 bytes.push_back(b3);
1371 bytes.push_back(0xFF);
1377 if (
rom_ ==
nullptr) {
1378 return absl::InvalidArgumentError(
"ROM pointer is null");
1381 auto rom_data =
rom()->vector();
1387 object_pointer =
SnesToPc(object_pointer);
1389 if (object_pointer < 0 || object_pointer >= (
int)
rom_->
size()) {
1390 return absl::OutOfRangeError(
"Object pointer out of range");
1393 int room_address = object_pointer + (
room_id_ * 3);
1395 if (room_address < 0 || room_address + 2 >= (
int)
rom_->
size()) {
1396 return absl::OutOfRangeError(
"Room address out of range");
1399 int tile_address = (rom_data[room_address + 2] << 16) +
1400 (rom_data[room_address + 1] << 8) + rom_data[room_address];
1404 if (objects_location < 0 || objects_location >= (
int)
rom_->
size()) {
1405 return absl::OutOfRangeError(
"Objects location out of range");
1410 int available_size = room_size_info.
room_size;
1413 int write_pos = objects_location + 2;
1420 if (encoded_bytes.size() > available_size - 2) {
1421 return absl::OutOfRangeError(absl::StrFormat(
1422 "Room %d object data too large! Size: %d, Available: %d",
room_id_,
1423 encoded_bytes.size(), available_size - 2));
1431 if (
rom_ ==
nullptr) {
1432 return absl::InvalidArgumentError(
"ROM pointer is null");
1435 auto rom_data =
rom()->vector();
1439 int sprite_pointer = (0x09 << 16) +
1442 sprite_pointer =
SnesToPc(sprite_pointer);
1444 if (sprite_pointer < 0 || sprite_pointer + (
room_id_ * 2) + 1 >= (
int)
rom_->
size()) {
1445 return absl::OutOfRangeError(
"Sprite table pointer out of range");
1449 int sprite_address_snes =
1450 (0x09 << 16) + (rom_data[sprite_pointer + (
room_id_ * 2) + 1] << 8) +
1451 rom_data[sprite_pointer + (
room_id_ * 2)];
1453 int sprite_address =
SnesToPc(sprite_address_snes);
1455 if (sprite_address < 0 || sprite_address >= (
int)
rom_->
size()) {
1456 return absl::OutOfRangeError(
"Sprite address out of range");
1461 int next_sprite_address_snes =
1462 (0x09 << 16) + (rom_data[sprite_pointer + ((
room_id_ + 1) * 2) + 1] << 8) +
1463 rom_data[sprite_pointer + ((
room_id_ + 1) * 2)];
1465 int next_sprite_address =
SnesToPc(next_sprite_address_snes);
1468 int available_size = next_sprite_address - sprite_address;
1471 if (available_size <= 0 || available_size > 0x1000) {
1475 available_size = 0x100;
1481 return absl::InternalError(absl::StrFormat(
"Cannot determine available sprite space for room %d",
room_id_));
1486 bool has_sort_sprite =
false;
1487 if (rom_data[sprite_address] == 1) {
1488 has_sort_sprite =
true;
1489 sprite_address += 1;
1490 available_size -= 1;
1496 if (encoded_bytes.size() > available_size) {
1497 return absl::OutOfRangeError(absl::StrFormat(
1498 "Room %d sprite data too large! Size: %d, Available: %d",
room_id_,
1499 encoded_bytes.size(), available_size));
1512 return absl::InvalidArgumentError(
"Invalid object parameters");
1519 return absl::OkStatus();
1524 return absl::OutOfRangeError(
"Object index out of range");
1530 return absl::OkStatus();
1535 return absl::OutOfRangeError(
"Object index out of range");
1539 return absl::InvalidArgumentError(
"Invalid object parameters");
1545 return absl::OkStatus();
1551 if (obj.x() == x && obj.y() == y && obj.GetLayerValue() == layer) {
1555 return absl::NotFoundError(
"No object found at position");
1560 if (
object.x() < 0 ||
object.x() > 63)
1562 if (
object.y() < 0 ||
object.y() > 63)
1566 if (
object.GetLayerValue() < 0 ||
object.GetLayerValue() > 2)
1570 if (
object.id_ < 0 || object.id_ > 0xFFF)
1574 if (
object.id_ < 0x100 &&
object.size() > 15)
1581 int& nbr_of_staircase) {
1585 if (nbr_of_staircase < 4) {
1608 }
else if (oid == 0xFB1) {
1618 auto rom_data =
rom()->vector();
1619 int sprite_pointer = (0x04 << 16) +
1622 int sprite_address_snes =
1623 (0x09 << 16) + (rom_data[sprite_pointer + (
room_id_ * 2) + 1] << 8) +
1624 rom_data[sprite_pointer + (
room_id_ * 2)];
1626 int sprite_address =
SnesToPc(sprite_address_snes);
1627 if (rom_data[sprite_address] == 1) {
1630 sprite_address += 1;
1633 uint8_t b1 = rom_data[sprite_address];
1634 uint8_t b2 = rom_data[sprite_address + 1];
1635 uint8_t b3 = rom_data[sprite_address + 2];
1641 sprites_.emplace_back(b3, (b2 & 0x1F), (b1 & 0x1F),
1642 ((b2 & 0xE0) >> 5) + ((b1 & 0x60) >> 2),
1649 if (spr.
id() == 0xE4 && spr.
x() == 0x00 && spr.
y() == 0x1E &&
1655 if (spr.
id() == 0xE4 && spr.
x() == 0x00 && spr.
y() == 0x1D &&
1662 sprite_address += 3;
1667 auto rom_data =
rom()->vector();
1674 for (
size_t i = 0; i < clength; i++) {
1675 if ((((rom_data[cpos + (i * 3) + 1] << 8) + (rom_data[cpos + (i * 3)])) &
1679 if ((((rom_data[cpos + (i * 3) + 1] << 8) + (rom_data[cpos + (i * 3)])) &
1680 0x8000) == 0x8000) {
1685 chest_data{rom_data[cpos + (i * 3) + 2], big});
1691 auto rom_data =
rom()->vector();
1702 "LoadDoors for room %d - doors are loaded via object stream",
1707 auto rom_data =
rom()->vector();
1717 for (
int i = 0; i < bytes_count; i += 2) {
1718 if (i + 1 >= bytes_count)
1725 if (b1 == 0xFF && b2 == 0xFF) {
1730 uint16_t torch_room_id = (b2 << 8) | b1;
1734 while (i < bytes_count) {
1735 if (i + 1 >= bytes_count)
1742 if (b1 == 0xFF && b2 == 0xFF) {
1747 int address = ((b2 & 0x1F) << 8 | b1) >> 1;
1748 uint8_t px = address % 64;
1749 uint8_t py = address >> 6;
1750 uint8_t layer = (b2 & 0x20) >> 5;
1751 bool lit = (b2 & 0x80) == 0x80;
1754 RoomObject torch_obj(0x150, px, py, 0, layer);
1761 LOG_DEBUG(
"Room",
"Loaded torch at (%d,%d) layer=%d lit=%d", px, py,
1770 while (i < bytes_count) {
1771 if (i + 1 >= bytes_count)
1775 if (b1 == 0xFF && b2 == 0xFF) {
1785 auto rom_data =
rom()->vector();
1795 std::vector<uint8_t> blocks_data(blocks_count);
1803 for (
int i = 0; i < 0x80 && i < blocks_count; i++) {
1804 blocks_data[i] = rom_data[pos1 + i];
1806 if (i + 0x80 < blocks_count) {
1807 blocks_data[i + 0x80] = rom_data[pos2 + i];
1809 if (i + 0x100 < blocks_count) {
1810 blocks_data[i + 0x100] = rom_data[pos3 + i];
1812 if (i + 0x180 < blocks_count) {
1813 blocks_data[i + 0x180] = rom_data[pos4 + i];
1818 for (
int i = 0; i < blocks_count; i += 4) {
1819 if (i + 3 >= blocks_count)
1822 uint8_t b1 = blocks_data[i];
1823 uint8_t b2 = blocks_data[i + 1];
1824 uint8_t b3 = blocks_data[i + 2];
1825 uint8_t b4 = blocks_data[i + 3];
1828 uint16_t block_room_id = (b2 << 8) | b1;
1831 if (b3 == 0xFF && b4 == 0xFF) {
1836 int address = ((b4 & 0x1F) << 8 | b3) >> 1;
1837 uint8_t px = address % 64;
1838 uint8_t py = address >> 6;
1839 uint8_t layer = (b4 & 0x20) >> 5;
1842 RoomObject block_obj(0x0E00, px, py, 0, layer);
1848 LOG_DEBUG(
"Room",
"Loaded block at (%d,%d) layer=%d", px, py, layer);
1855 auto rom_data =
rom()->vector();
1869 int ptr_addr = table_addr + (
room_id_ * 2);
1870 if (ptr_addr + 1 >=
static_cast<int>(rom_data.size()))
return;
1872 uint16_t item_ptr = (rom_data[ptr_addr + 1] << 8) | rom_data[ptr_addr];
1875 int item_addr =
SnesToPc(0x010000 | item_ptr);
1880 while (item_addr + 2 <
static_cast<int>(rom_data.size())) {
1882 uint16_t position = (rom_data[item_addr + 1] << 8) | rom_data[item_addr];
1885 if (position == 0xFFFF)
break;
1888 uint8_t item_type = rom_data[item_addr + 2];
1892 pot_item.
item = item_type;
1900 auto rom_data =
rom()->vector();
1903 int pit_entries = rom_data[
pit_count] / 2;
1908 int pit_data_addr =
SnesToPc(pit_ptr);
1910 LOG_DEBUG(
"Room",
"LoadPits: room_id=%d, pit_entries=%d, pit_ptr=0x%06X",
1920 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.