- Deleted references to `system_editor.cc` and `system_editor.h` from YAZE.vcxproj and YAZE.vcxproj.filters to clean up the project structure. - Updated `room_object.cc` to use a direct assignment for `gfx::TileInfo` and commented out unused code in `room.cc` and `tracker.cc` for clarity and future reference.
692 lines
22 KiB
C++
692 lines
22 KiB
C++
#include "room.h"
|
|
|
|
#include <yaze.h>
|
|
|
|
#include <cstdint>
|
|
#include <vector>
|
|
|
|
#include "absl/strings/str_cat.h"
|
|
#include "app/core/window.h"
|
|
#include "app/gfx/arena.h"
|
|
#include "app/rom.h"
|
|
#include "app/snes.h"
|
|
#include "app/zelda3/dungeon/room_object.h"
|
|
#include "app/zelda3/sprite/sprite.h"
|
|
#include "util/log.h"
|
|
|
|
namespace yaze {
|
|
namespace zelda3 {
|
|
|
|
RoomSize CalculateRoomSize(Rom *rom, int room_id) {
|
|
// Calculate the size of the room based on how many objects are used per room
|
|
// Some notes from hacker Zarby89
|
|
// vanilla rooms are using in average ~0x80 bytes
|
|
// a "normal" person who wants more details than vanilla will use around 0x100
|
|
// bytes per rooms you could fit 128 rooms like that in 1 bank
|
|
// F8000 I don't remember if that's PC or snes tho
|
|
// Check last rooms
|
|
// F8000+(roomid*3)
|
|
// So we want to search the rom() object at this addressed based on the room
|
|
// ID since it's the roomid * 3 we will by pulling 3 bytes at a time We can do
|
|
// this with the rom()->ReadByteVector(addr, size)
|
|
// Existing room size address calculation...
|
|
RoomSize room_size;
|
|
room_size.room_size_pointer = 0;
|
|
room_size.room_size = 0;
|
|
|
|
auto room_size_address = 0xF8000 + (room_id * 3);
|
|
util::logf("Room #%#03X Addresss: %#06X", room_id, room_size_address);
|
|
|
|
// Reading bytes for long address construction
|
|
uint8_t low = rom->data()[room_size_address];
|
|
uint8_t high = rom->data()[room_size_address + 1];
|
|
uint8_t bank = rom->data()[room_size_address + 2];
|
|
|
|
// Constructing the long address
|
|
int long_address = (bank << 16) | (high << 8) | low;
|
|
util::logf("%#06X", long_address);
|
|
room_size.room_size_pointer = long_address;
|
|
|
|
if (long_address == 0x0A8000) {
|
|
// Blank room disregard in size calculation
|
|
util::logf("Size of Room #%#03X: 0 bytes", room_id);
|
|
room_size.room_size = 0;
|
|
} else {
|
|
// use the long address to calculate the size of the room
|
|
// we will use the room_id_ to calculate the next room's address
|
|
// and subtract the two to get the size of the room
|
|
|
|
int next_room_address = 0xF8000 + ((room_id + 1) * 3);
|
|
util::logf("Next Room Address: %#06X", next_room_address);
|
|
|
|
// Reading bytes for long address construction
|
|
uint8_t next_low = rom->data()[next_room_address];
|
|
uint8_t next_high = rom->data()[next_room_address + 1];
|
|
uint8_t next_bank = rom->data()[next_room_address + 2];
|
|
|
|
// Constructing the long address
|
|
int next_long_address = (next_bank << 16) | (next_high << 8) | next_low;
|
|
util::logf("%#06X", next_long_address);
|
|
|
|
// Calculate the size of the room
|
|
int actual_room_size = next_long_address - long_address;
|
|
room_size.room_size = actual_room_size;
|
|
util::logf("Size of Room #%#03X: %d bytes", room_id, actual_room_size);
|
|
}
|
|
|
|
return room_size;
|
|
}
|
|
|
|
Room LoadRoomFromRom(Rom *rom, int room_id) {
|
|
Room room(room_id, rom);
|
|
|
|
int header_pointer = (rom->data()[kRoomHeaderPointer + 2] << 16) +
|
|
(rom->data()[kRoomHeaderPointer + 1] << 8) +
|
|
(rom->data()[kRoomHeaderPointer]);
|
|
header_pointer = SnesToPc(header_pointer);
|
|
|
|
int address = (rom->data()[kRoomHeaderPointerBank] << 16) +
|
|
(rom->data()[(header_pointer + 1) + (room_id * 2)] << 8) +
|
|
rom->data()[(header_pointer) + (room_id * 2)];
|
|
|
|
auto header_location = SnesToPc(address);
|
|
|
|
room.SetBg2((background2)((rom->data()[header_location] >> 5) & 0x07));
|
|
room.SetCollision((CollisionKey)((rom->data()[header_location] >> 2) & 0x07));
|
|
room.SetIsLight(((rom->data()[header_location]) & 0x01) == 1);
|
|
|
|
if (room.IsLight()) {
|
|
room.SetBg2(background2::DarkRoom);
|
|
}
|
|
|
|
room.SetPalette(((rom->data()[header_location + 1] & 0x3F)));
|
|
room.SetBlockset((rom->data()[header_location + 2]));
|
|
room.SetSpriteset((rom->data()[header_location + 3]));
|
|
room.SetEffect((EffectKey)((rom->data()[header_location + 4])));
|
|
room.SetTag1((TagKey)((rom->data()[header_location + 5])));
|
|
room.SetTag2((TagKey)((rom->data()[header_location + 6])));
|
|
|
|
room.SetStaircasePlane(0, ((rom->data()[header_location + 7] >> 2) & 0x03));
|
|
room.SetStaircasePlane(1, ((rom->data()[header_location + 7] >> 4) & 0x03));
|
|
room.SetStaircasePlane(2, ((rom->data()[header_location + 7] >> 6) & 0x03));
|
|
room.SetStaircasePlane(3, ((rom->data()[header_location + 8]) & 0x03));
|
|
|
|
room.SetHolewarp((rom->data()[header_location + 9]));
|
|
room.SetStaircaseRoom(0, (rom->data()[header_location + 10]));
|
|
room.SetStaircaseRoom(1, (rom->data()[header_location + 11]));
|
|
room.SetStaircaseRoom(2, (rom->data()[header_location + 12]));
|
|
room.SetStaircaseRoom(3, (rom->data()[header_location + 13]));
|
|
|
|
// =====
|
|
|
|
int header_pointer_2 = (rom->data()[kRoomHeaderPointer + 2] << 16) +
|
|
(rom->data()[kRoomHeaderPointer + 1] << 8) +
|
|
(rom->data()[kRoomHeaderPointer]);
|
|
header_pointer_2 = SnesToPc(header_pointer_2);
|
|
|
|
int address_2 = (rom->data()[kRoomHeaderPointerBank] << 16) +
|
|
(rom->data()[(header_pointer_2 + 1) + (room_id * 2)] << 8) +
|
|
rom->data()[(header_pointer_2) + (room_id * 2)];
|
|
|
|
room.SetMessageIdDirect(messages_id_dungeon + (room_id * 2));
|
|
|
|
auto hpos = SnesToPc(address_2);
|
|
hpos++;
|
|
uint8_t b = rom->data()[hpos];
|
|
|
|
room.SetLayer2Mode((b >> 5));
|
|
room.SetLayerMerging(kLayerMergeTypeList[(b & 0x0C) >> 2]);
|
|
|
|
room.SetIsDark((b & 0x01) == 0x01);
|
|
hpos++;
|
|
room.SetPaletteDirect(rom->data()[hpos]);
|
|
hpos++;
|
|
|
|
room.SetBackgroundTileset(rom->data()[hpos]);
|
|
hpos++;
|
|
|
|
room.SetSpriteTileset(rom->data()[hpos]);
|
|
hpos++;
|
|
|
|
room.SetLayer2Behavior(rom->data()[hpos]);
|
|
hpos++;
|
|
|
|
room.SetTag1Direct((TagKey)rom->data()[hpos]);
|
|
hpos++;
|
|
|
|
room.SetTag2Direct((TagKey)rom->data()[hpos]);
|
|
hpos++;
|
|
|
|
b = rom->data()[hpos];
|
|
|
|
room.SetPitsTargetLayer((uint8_t)(b & 0x03));
|
|
room.SetStair1TargetLayer((uint8_t)((b >> 2) & 0x03));
|
|
room.SetStair2TargetLayer((uint8_t)((b >> 4) & 0x03));
|
|
room.SetStair3TargetLayer((uint8_t)((b >> 6) & 0x03));
|
|
hpos++;
|
|
room.SetStair4TargetLayer((uint8_t)(rom->data()[hpos] & 0x03));
|
|
hpos++;
|
|
|
|
room.SetPitsTarget(rom->data()[hpos]);
|
|
hpos++;
|
|
room.SetStair1Target(rom->data()[hpos]);
|
|
hpos++;
|
|
room.SetStair2Target(rom->data()[hpos]);
|
|
hpos++;
|
|
room.SetStair3Target(rom->data()[hpos]);
|
|
hpos++;
|
|
room.SetStair4Target(rom->data()[hpos]);
|
|
hpos++;
|
|
|
|
// Load room objects
|
|
int object_pointer = SnesToPc(room_object_pointer);
|
|
int room_address = object_pointer + (room_id * 3);
|
|
int objects_location = SnesToPc(room_address);
|
|
|
|
// Load sprites
|
|
int spr_ptr = 0x040000 | rooms_sprite_pointer;
|
|
int sprite_address = SnesToPc(dungeon_spr_ptrs | spr_ptr + (room_id * 2));
|
|
|
|
// Load room layout
|
|
room.LoadRoomLayout();
|
|
|
|
// Load additional room features
|
|
room.LoadDoors();
|
|
room.LoadTorches();
|
|
room.LoadBlocks();
|
|
room.LoadPits();
|
|
|
|
return room;
|
|
}
|
|
|
|
void Room::LoadRoomGraphics(uint8_t entrance_blockset) {
|
|
const auto &room_gfx = rom()->room_blockset_ids;
|
|
const auto &sprite_gfx = rom()->spriteset_ids;
|
|
|
|
for (int i = 0; i < 8; i++) {
|
|
blocks_[i] = rom()->main_blockset_ids[blockset][i];
|
|
if (i >= 6 && i <= 6) {
|
|
// 3-6
|
|
if (entrance_blockset != 0xFF &&
|
|
room_gfx[entrance_blockset][i - 3] != 0) {
|
|
blocks_[i] = room_gfx[entrance_blockset][i - 3];
|
|
}
|
|
}
|
|
}
|
|
|
|
blocks_[8] = 115 + 0; // Static Sprites Blocksets (fairy,pot,ect...)
|
|
blocks_[9] = 115 + 10;
|
|
blocks_[10] = 115 + 6;
|
|
blocks_[11] = 115 + 7;
|
|
for (int i = 0; i < 4; i++) {
|
|
blocks_[12 + i] = (uint8_t)(sprite_gfx[spriteset + 64][i] + 115);
|
|
} // 12-16 sprites
|
|
}
|
|
|
|
constexpr int kGfxBufferOffset = 92 * 2048;
|
|
constexpr int kGfxBufferStride = 512;
|
|
constexpr int kGfxBufferAnimatedFrameOffset = 7 * 2048;
|
|
constexpr int kGfxBufferAnimatedFrameStride = 512;
|
|
constexpr int kGfxBufferRoomOffset = 2048;
|
|
constexpr int kGfxBufferRoomSpriteOffset = 512;
|
|
constexpr int kGfxBufferRoomSpriteStride = 2048;
|
|
constexpr int kGfxBufferRoomSpriteLastLineOffset = 0x88;
|
|
|
|
void Room::CopyRoomGraphicsToBuffer() {
|
|
if (!rom_ || !rom_->is_loaded()) {
|
|
return;
|
|
}
|
|
|
|
auto gfx_buffer_data = rom()->mutable_graphics_buffer();
|
|
if (!gfx_buffer_data || gfx_buffer_data->empty()) {
|
|
return;
|
|
}
|
|
|
|
// Copy room graphics to buffer
|
|
int sheet_pos = 0;
|
|
for (int i = 0; i < 16; i++) {
|
|
// Validate block index
|
|
if (blocks_[i] < 0 || blocks_[i] > 255) {
|
|
sheet_pos += kGfxBufferRoomOffset;
|
|
continue;
|
|
}
|
|
|
|
int data = 0;
|
|
int block_offset = blocks_[i] * kGfxBufferRoomOffset;
|
|
|
|
// Validate block_offset bounds
|
|
if (block_offset < 0 || block_offset >= static_cast<int>(gfx_buffer_data->size())) {
|
|
sheet_pos += kGfxBufferRoomOffset;
|
|
continue;
|
|
}
|
|
|
|
while (data < kGfxBufferRoomOffset) {
|
|
int buffer_index = data + block_offset;
|
|
if (buffer_index >= 0 && buffer_index < static_cast<int>(gfx_buffer_data->size())) {
|
|
uint8_t map_byte = (*gfx_buffer_data)[buffer_index];
|
|
if (i < 4) {
|
|
map_byte += kGfxBufferRoomSpriteLastLineOffset;
|
|
}
|
|
|
|
// Validate current_gfx16_ access
|
|
int gfx_index = data + sheet_pos;
|
|
if (gfx_index >= 0 && gfx_index < static_cast<int>(sizeof(current_gfx16_))) {
|
|
current_gfx16_[gfx_index] = map_byte;
|
|
}
|
|
}
|
|
data++;
|
|
}
|
|
|
|
sheet_pos += kGfxBufferRoomOffset;
|
|
}
|
|
|
|
LoadAnimatedGraphics();
|
|
}
|
|
|
|
void Room::RenderRoomGraphics() {
|
|
CopyRoomGraphicsToBuffer();
|
|
|
|
gfx::Arena::Get().bg1().DrawFloor(rom()->vector(), tile_address,
|
|
tile_address_floor, floor1_graphics_);
|
|
gfx::Arena::Get().bg2().DrawFloor(rom()->vector(), tile_address,
|
|
tile_address_floor, floor2_graphics_);
|
|
|
|
gfx::Arena::Get().bg1().DrawBackground(std::span<uint8_t>(current_gfx16_));
|
|
gfx::Arena::Get().bg2().DrawBackground(std::span<uint8_t>(current_gfx16_));
|
|
|
|
auto bg1_palette =
|
|
rom()->mutable_palette_group()->get_group("dungeon_main")[0].palette(0);
|
|
|
|
if (!gfx::Arena::Get().bg1().bitmap().is_active()) {
|
|
core::Renderer::Get().CreateAndRenderBitmap(
|
|
0x200, 0x200, 0x200, gfx::Arena::Get().bg1().bitmap().vector(),
|
|
gfx::Arena::Get().bg1().bitmap(), bg1_palette);
|
|
core::Renderer::Get().CreateAndRenderBitmap(
|
|
0x200, 0x200, 0x200, gfx::Arena::Get().bg2().bitmap().vector(),
|
|
gfx::Arena::Get().bg2().bitmap(), bg1_palette);
|
|
} else {
|
|
// Update the bitmap
|
|
core::Renderer::Get().UpdateBitmap(&gfx::Arena::Get().bg1().bitmap());
|
|
core::Renderer::Get().UpdateBitmap(&gfx::Arena::Get().bg2().bitmap());
|
|
}
|
|
}
|
|
|
|
void Room::LoadAnimatedGraphics() {
|
|
if (!rom_ || !rom_->is_loaded()) {
|
|
return;
|
|
}
|
|
|
|
auto gfx_buffer_data = rom()->mutable_graphics_buffer();
|
|
if (!gfx_buffer_data || gfx_buffer_data->empty()) {
|
|
return;
|
|
}
|
|
|
|
auto rom_data = rom()->vector();
|
|
if (rom_data.empty()) {
|
|
return;
|
|
}
|
|
|
|
// Validate animated_frame_ bounds
|
|
if (animated_frame_ < 0 || animated_frame_ > 10) {
|
|
return;
|
|
}
|
|
|
|
// Validate background_tileset_ bounds
|
|
if (background_tileset_ < 0 || background_tileset_ > 255) {
|
|
return;
|
|
}
|
|
|
|
int gfx_ptr = SnesToPc(rom()->version_constants().kGfxAnimatedPointer);
|
|
if (gfx_ptr < 0 || gfx_ptr >= static_cast<int>(rom_data.size())) {
|
|
return;
|
|
}
|
|
|
|
int data = 0;
|
|
while (data < 512) {
|
|
// Validate buffer access for first operation
|
|
int first_offset = data + (92 * 2048) + (512 * animated_frame_);
|
|
if (first_offset >= 0 && first_offset < static_cast<int>(gfx_buffer_data->size())) {
|
|
uint8_t map_byte = (*gfx_buffer_data)[first_offset];
|
|
|
|
// Validate current_gfx16_ access
|
|
int gfx_offset = data + (7 * 2048);
|
|
if (gfx_offset >= 0 && gfx_offset < static_cast<int>(sizeof(current_gfx16_))) {
|
|
current_gfx16_[gfx_offset] = map_byte;
|
|
}
|
|
}
|
|
|
|
// Validate buffer access for second operation
|
|
int tileset_index = rom_data[gfx_ptr + background_tileset_];
|
|
int second_offset = data + (tileset_index * 2048) + (512 * animated_frame_);
|
|
if (second_offset >= 0 && second_offset < static_cast<int>(gfx_buffer_data->size())) {
|
|
uint8_t map_byte = (*gfx_buffer_data)[second_offset];
|
|
|
|
// Validate current_gfx16_ access
|
|
int gfx_offset = data + (7 * 2048) - 512;
|
|
if (gfx_offset >= 0 && gfx_offset < static_cast<int>(sizeof(current_gfx16_))) {
|
|
current_gfx16_[gfx_offset] = map_byte;
|
|
}
|
|
}
|
|
|
|
data++;
|
|
}
|
|
}
|
|
|
|
void Room::LoadObjects() {
|
|
auto rom_data = rom()->vector();
|
|
|
|
// Enhanced object loading with comprehensive validation
|
|
int object_pointer = (rom_data[room_object_pointer + 2] << 16) +
|
|
(rom_data[room_object_pointer + 1] << 8) +
|
|
(rom_data[room_object_pointer]);
|
|
object_pointer = SnesToPc(object_pointer);
|
|
|
|
// Enhanced bounds checking for object pointer
|
|
if (object_pointer < 0 || object_pointer >= (int)rom_->size()) {
|
|
util::logf("Object pointer out of range for room %d: %#06x", room_id_, object_pointer);
|
|
return;
|
|
}
|
|
|
|
int room_address = object_pointer + (room_id_ * 3);
|
|
|
|
// Enhanced bounds checking for room address
|
|
if (room_address < 0 || room_address + 2 >= (int)rom_->size()) {
|
|
util::logf("Room address out of range for room %d: %#06x", room_id_, room_address);
|
|
return;
|
|
}
|
|
|
|
int tile_address = (rom_data[room_address + 2] << 16) +
|
|
(rom_data[room_address + 1] << 8) + rom_data[room_address];
|
|
|
|
int objects_location = SnesToPc(tile_address);
|
|
|
|
// Enhanced bounds checking for objects location
|
|
if (objects_location < 0 || objects_location >= (int)rom_->size()) {
|
|
util::logf("Objects location out of range for room %d: %#06x", room_id_, objects_location);
|
|
return;
|
|
}
|
|
|
|
// Parse floor graphics and layout with validation
|
|
if (objects_location + 1 < (int)rom_->size()) {
|
|
if (is_floor_) {
|
|
floor1_graphics_ = static_cast<uint8_t>(rom_data[objects_location] & 0x0F);
|
|
floor2_graphics_ = static_cast<uint8_t>((rom_data[objects_location] >> 4) & 0x0F);
|
|
}
|
|
|
|
layout = static_cast<uint8_t>((rom_data[objects_location + 1] >> 2) & 0x07);
|
|
}
|
|
|
|
LoadChests();
|
|
|
|
// Parse objects with enhanced error handling
|
|
ParseObjectsFromLocation(objects_location + 2);
|
|
}
|
|
|
|
void Room::ParseObjectsFromLocation(int objects_location) {
|
|
auto rom_data = rom()->vector();
|
|
|
|
z3_staircases_.clear();
|
|
int nbr_of_staircase = 0;
|
|
|
|
int pos = objects_location;
|
|
uint8_t b1 = 0;
|
|
uint8_t b2 = 0;
|
|
uint8_t b3 = 0;
|
|
uint8_t posX = 0;
|
|
uint8_t posY = 0;
|
|
uint8_t sizeX = 0;
|
|
uint8_t sizeY = 0;
|
|
uint8_t sizeXY = 0;
|
|
short oid = 0;
|
|
int layer = 0;
|
|
bool door = false;
|
|
bool end_read = false;
|
|
|
|
// Enhanced parsing loop with bounds checking
|
|
while (!end_read && pos < (int)rom_->size()) {
|
|
// Check if we have enough bytes to read
|
|
if (pos + 1 >= (int)rom_->size()) {
|
|
break;
|
|
}
|
|
|
|
b1 = rom_data[pos];
|
|
b2 = rom_data[pos + 1];
|
|
|
|
if (b1 == 0xFF && b2 == 0xFF) {
|
|
pos += 2; // Jump to next layer
|
|
layer++;
|
|
door = false;
|
|
if (layer == 3) {
|
|
break;
|
|
}
|
|
continue;
|
|
}
|
|
|
|
if (b1 == 0xF0 && b2 == 0xFF) {
|
|
pos += 2; // Jump to door section
|
|
door = true;
|
|
continue;
|
|
}
|
|
|
|
// Check if we have enough bytes for object data
|
|
if (pos + 2 >= (int)rom_->size()) {
|
|
break;
|
|
}
|
|
|
|
b3 = rom_data[pos + 2];
|
|
if (door) {
|
|
pos += 2;
|
|
} else {
|
|
pos += 3;
|
|
}
|
|
|
|
if (!door) {
|
|
// Parse object with enhanced validation
|
|
if (b3 >= 0xF8) {
|
|
oid = static_cast<short>((b3 << 4) |
|
|
0x80 + (((b2 & 0x03) << 2) + ((b1 & 0x03))));
|
|
posX = static_cast<uint8_t>((b1 & 0xFC) >> 2);
|
|
posY = static_cast<uint8_t>((b2 & 0xFC) >> 2);
|
|
sizeXY = static_cast<uint8_t>((((b1 & 0x03) << 2) + (b2 & 0x03)));
|
|
} else {
|
|
oid = b3;
|
|
posX = static_cast<uint8_t>((b1 & 0xFC) >> 2);
|
|
posY = static_cast<uint8_t>((b2 & 0xFC) >> 2);
|
|
sizeX = static_cast<uint8_t>((b1 & 0x03));
|
|
sizeY = static_cast<uint8_t>((b2 & 0x03));
|
|
sizeXY = static_cast<uint8_t>(((sizeX << 2) + sizeY));
|
|
}
|
|
|
|
if (b1 >= 0xFC) {
|
|
oid = static_cast<short>((b3 & 0x3F) + 0x100);
|
|
posX = static_cast<uint8_t>(((b2 & 0xF0) >> 4) + ((b1 & 0x3) << 4));
|
|
posY = static_cast<uint8_t>(((b2 & 0x0F) << 2) + ((b3 & 0xC0) >> 6));
|
|
sizeXY = 0;
|
|
}
|
|
|
|
// Validate object ID before creating object
|
|
if (oid >= 0 && oid <= 0x3FF) {
|
|
RoomObject r(oid, posX, posY, sizeXY, static_cast<uint8_t>(layer));
|
|
r.set_rom(rom_);
|
|
tile_objects_.push_back(r);
|
|
|
|
// Handle special object types
|
|
HandleSpecialObjects(oid, posX, posY, nbr_of_staircase);
|
|
}
|
|
} else {
|
|
// Handle door objects (placeholder for future implementation)
|
|
// tile_objects_.push_back(z3_object_door(static_cast<short>((b2 << 8) + b1),
|
|
// 0, 0, 0, static_cast<uint8_t>(layer)));
|
|
}
|
|
}
|
|
}
|
|
|
|
void Room::HandleSpecialObjects(short oid, uint8_t posX, uint8_t posY, int& nbr_of_staircase) {
|
|
// Handle staircase objects
|
|
for (short stair : stairsObjects) {
|
|
if (stair == oid) {
|
|
if (nbr_of_staircase < 4) {
|
|
tile_objects_.back().set_options(ObjectOption::Stairs |
|
|
tile_objects_.back().options());
|
|
z3_staircases_.push_back({
|
|
posX, posY,
|
|
absl::StrCat("To ", staircase_rooms_[nbr_of_staircase])
|
|
.data()});
|
|
nbr_of_staircase++;
|
|
} else {
|
|
tile_objects_.back().set_options(ObjectOption::Stairs |
|
|
tile_objects_.back().options());
|
|
z3_staircases_.push_back({posX, posY, "To ???"});
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Handle chest objects
|
|
if (oid == 0xF99) {
|
|
if (chests_in_room_.size() > 0) {
|
|
tile_objects_.back().set_options(ObjectOption::Chest |
|
|
tile_objects_.back().options());
|
|
chests_in_room_.erase(chests_in_room_.begin());
|
|
}
|
|
} else if (oid == 0xFB1) {
|
|
if (chests_in_room_.size() > 0) {
|
|
tile_objects_.back().set_options(ObjectOption::Chest |
|
|
tile_objects_.back().options());
|
|
chests_in_room_.erase(chests_in_room_.begin());
|
|
}
|
|
}
|
|
}
|
|
|
|
void Room::LoadSprites() {
|
|
auto rom_data = rom()->vector();
|
|
int sprite_pointer = (0x04 << 16) +
|
|
(rom_data[rooms_sprite_pointer + 1] << 8) +
|
|
(rom_data[rooms_sprite_pointer]);
|
|
int sprite_address_snes =
|
|
(0x09 << 16) + (rom_data[sprite_pointer + (room_id_ * 2) + 1] << 8) +
|
|
rom_data[sprite_pointer + (room_id_ * 2)];
|
|
|
|
int sprite_address = SnesToPc(sprite_address_snes);
|
|
bool sortsprites = rom_data[sprite_address] == 1;
|
|
sprite_address += 1;
|
|
|
|
while (true) {
|
|
uint8_t b1 = rom_data[sprite_address];
|
|
uint8_t b2 = rom_data[sprite_address + 1];
|
|
uint8_t b3 = rom_data[sprite_address + 2];
|
|
|
|
if (b1 == 0xFF) {
|
|
break;
|
|
}
|
|
|
|
sprites_.emplace_back(b3, (b2 & 0x1F), (b1 & 0x1F),
|
|
((b2 & 0xE0) >> 5) + ((b1 & 0x60) >> 2),
|
|
(b1 & 0x80) >> 7);
|
|
|
|
if (sprites_.size() > 1) {
|
|
Sprite &spr = sprites_.back();
|
|
Sprite &prevSprite = sprites_[sprites_.size() - 2];
|
|
|
|
if (spr.id() == 0xE4 && spr.x() == 0x00 && spr.y() == 0x1E &&
|
|
spr.layer() == 1 && spr.subtype() == 0x18) {
|
|
prevSprite.set_key_drop(1);
|
|
sprites_.pop_back();
|
|
}
|
|
|
|
if (spr.id() == 0xE4 && spr.x() == 0x00 && spr.y() == 0x1D &&
|
|
spr.layer() == 1 && spr.subtype() == 0x18) {
|
|
prevSprite.set_key_drop(2);
|
|
sprites_.pop_back();
|
|
}
|
|
}
|
|
|
|
sprite_address += 3;
|
|
}
|
|
}
|
|
|
|
void Room::LoadChests() {
|
|
auto rom_data = rom()->vector();
|
|
uint32_t cpos = SnesToPc((rom_data[chests_data_pointer1 + 2] << 16) +
|
|
(rom_data[chests_data_pointer1 + 1] << 8) +
|
|
(rom_data[chests_data_pointer1]));
|
|
size_t clength = (rom_data[chests_length_pointer + 1] << 8) +
|
|
(rom_data[chests_length_pointer]);
|
|
|
|
for (int i = 0; i < clength; i++) {
|
|
if ((((rom_data[cpos + (i * 3) + 1] << 8) + (rom_data[cpos + (i * 3)])) &
|
|
0x7FFF) == room_id_) {
|
|
// There's a chest in that room !
|
|
bool big = false;
|
|
if ((((rom_data[cpos + (i * 3) + 1] << 8) + (rom_data[cpos + (i * 3)])) &
|
|
0x8000) == 0x8000) {
|
|
big = true;
|
|
}
|
|
|
|
chests_in_room_.emplace_back(
|
|
chest_data{rom_data[cpos + (i * 3) + 2], big});
|
|
}
|
|
}
|
|
}
|
|
|
|
void Room::LoadRoomLayout() {
|
|
// Use the new RoomLayout system to load walls, floors, and structural elements
|
|
auto status = layout_.LoadLayout(room_id_);
|
|
if (!status.ok()) {
|
|
// Log error but don't fail - some rooms might not have layout data
|
|
util::logf("Failed to load room layout for room %d: %s",
|
|
room_id_, status.message().data());
|
|
return;
|
|
}
|
|
|
|
// Store the layout ID for compatibility with existing code
|
|
layout = static_cast<uint8_t>(room_id_ & 0xFF);
|
|
|
|
util::logf("Loaded room layout for room %d with %zu objects",
|
|
room_id_, layout_.GetObjects().size());
|
|
}
|
|
|
|
void Room::LoadDoors() {
|
|
auto rom_data = rom()->vector();
|
|
|
|
// Load door graphics and positions
|
|
// Door graphics are stored at door_gfx_* addresses
|
|
// Door positions are stored at door_pos_* addresses
|
|
|
|
// For now, create placeholder door objects
|
|
// TODO: Implement full door loading from ROM data
|
|
}
|
|
|
|
void Room::LoadTorches() {
|
|
auto rom_data = rom()->vector();
|
|
|
|
// Load torch data from torch_data address
|
|
int torch_count = rom_data[torches_length_pointer + 1] << 8 | rom_data[torches_length_pointer];
|
|
|
|
// For now, create placeholder torch objects
|
|
// TODO: Implement full torch loading from ROM data
|
|
}
|
|
|
|
void Room::LoadBlocks() {
|
|
auto rom_data = rom()->vector();
|
|
|
|
// Load block data from blocks_* addresses
|
|
int block_count = rom_data[blocks_length + 1] << 8 | rom_data[blocks_length];
|
|
|
|
// For now, create placeholder block objects
|
|
// TODO: Implement full block loading from ROM data
|
|
}
|
|
|
|
void Room::LoadPits() {
|
|
auto rom_data = rom()->vector();
|
|
|
|
// Load pit data from pit_pointer
|
|
// int pit_count = rom_data[pit_count + 1] << 8 | rom_data[pit_count];
|
|
|
|
// For now, create placeholder pit objects
|
|
// TODO: Implement full pit loading from ROM data
|
|
}
|
|
|
|
} // namespace zelda3
|
|
} // namespace yaze
|