yaze 0.3.2
Link to the Past ROM Editor
 
Loading...
Searching...
No Matches
room.cc
Go to the documentation of this file.
1#include "room.h"
2
3#include <yaze.h>
4
5#include <cstdint>
6#include <vector>
7
8#include "absl/strings/str_cat.h"
9#include "app/gfx/arena.h"
11#include "app/rom.h"
12#include "app/snes.h"
16#include "util/log.h"
17
18namespace yaze {
19namespace zelda3 {
20
21RoomSize CalculateRoomSize(Rom *rom, int room_id) {
22 // Calculate the size of the room based on how many objects are used per room
23 // Some notes from hacker Zarby89
24 // vanilla rooms are using in average ~0x80 bytes
25 // a "normal" person who wants more details than vanilla will use around 0x100
26 // bytes per rooms you could fit 128 rooms like that in 1 bank
27 // F8000 I don't remember if that's PC or snes tho
28 // Check last rooms
29 // F8000+(roomid*3)
30 // So we want to search the rom() object at this addressed based on the room
31 // ID since it's the roomid * 3 we will by pulling 3 bytes at a time We can do
32 // this with the rom()->ReadByteVector(addr, size)
33 // Existing room size address calculation...
34 RoomSize room_size;
35 room_size.room_size_pointer = 0;
36 room_size.room_size = 0;
37
38 auto room_size_address = 0xF8000 + (room_id * 3);
39
40 // Reading bytes for long address construction
41 uint8_t low = rom->data()[room_size_address];
42 uint8_t high = rom->data()[room_size_address + 1];
43 uint8_t bank = rom->data()[room_size_address + 2];
44
45 // Constructing the long address
46 int long_address = (bank << 16) | (high << 8) | low;
47 room_size.room_size_pointer = long_address;
48
49 if (long_address == 0x0A8000) {
50 // Blank room disregard in size calculation
51 room_size.room_size = 0;
52 } else {
53 // use the long address to calculate the size of the room
54 // we will use the room_id_ to calculate the next room's address
55 // and subtract the two to get the size of the room
56
57 int next_room_address = 0xF8000 + ((room_id + 1) * 3);
58
59 // Reading bytes for long address construction
60 uint8_t next_low = rom->data()[next_room_address];
61 uint8_t next_high = rom->data()[next_room_address + 1];
62 uint8_t next_bank = rom->data()[next_room_address + 2];
63
64 // Constructing the long address
65 int next_long_address = (next_bank << 16) | (next_high << 8) | next_low;
66
67 // Calculate the size of the room
68 int actual_room_size = next_long_address - long_address;
69 room_size.room_size = actual_room_size;
70 }
71
72 return room_size;
73}
74
75Room LoadRoomFromRom(Rom *rom, int room_id) {
76 Room room(room_id, rom);
77
78 int header_pointer = (rom->data()[kRoomHeaderPointer + 2] << 16) +
79 (rom->data()[kRoomHeaderPointer + 1] << 8) +
80 (rom->data()[kRoomHeaderPointer]);
81 header_pointer = SnesToPc(header_pointer);
82
83 int address = (rom->data()[kRoomHeaderPointerBank] << 16) +
84 (rom->data()[(header_pointer + 1) + (room_id * 2)] << 8) +
85 rom->data()[(header_pointer) + (room_id * 2)];
86
87 auto header_location = SnesToPc(address);
88
89 room.SetBg2((background2)((rom->data()[header_location] >> 5) & 0x07));
90 room.SetCollision((CollisionKey)((rom->data()[header_location] >> 2) & 0x07));
91 room.SetIsLight(((rom->data()[header_location]) & 0x01) == 1);
92
93 if (room.IsLight()) {
94 room.SetBg2(background2::DarkRoom);
95 }
96
97 room.SetPalette(((rom->data()[header_location + 1] & 0x3F)));
98 room.SetBlockset((rom->data()[header_location + 2]));
99 room.SetSpriteset((rom->data()[header_location + 3]));
100 room.SetEffect((EffectKey)((rom->data()[header_location + 4])));
101 room.SetTag1((TagKey)((rom->data()[header_location + 5])));
102 room.SetTag2((TagKey)((rom->data()[header_location + 6])));
103
104 room.SetStaircasePlane(0, ((rom->data()[header_location + 7] >> 2) & 0x03));
105 room.SetStaircasePlane(1, ((rom->data()[header_location + 7] >> 4) & 0x03));
106 room.SetStaircasePlane(2, ((rom->data()[header_location + 7] >> 6) & 0x03));
107 room.SetStaircasePlane(3, ((rom->data()[header_location + 8]) & 0x03));
108
109 room.SetHolewarp((rom->data()[header_location + 9]));
110 room.SetStaircaseRoom(0, (rom->data()[header_location + 10]));
111 room.SetStaircaseRoom(1, (rom->data()[header_location + 11]));
112 room.SetStaircaseRoom(2, (rom->data()[header_location + 12]));
113 room.SetStaircaseRoom(3, (rom->data()[header_location + 13]));
114
115 // =====
116
117 int header_pointer_2 = (rom->data()[kRoomHeaderPointer + 2] << 16) +
118 (rom->data()[kRoomHeaderPointer + 1] << 8) +
119 (rom->data()[kRoomHeaderPointer]);
120 header_pointer_2 = SnesToPc(header_pointer_2);
121
122 int address_2 = (rom->data()[kRoomHeaderPointerBank] << 16) +
123 (rom->data()[(header_pointer_2 + 1) + (room_id * 2)] << 8) +
124 rom->data()[(header_pointer_2) + (room_id * 2)];
125
126 room.SetMessageIdDirect(messages_id_dungeon + (room_id * 2));
127
128 auto hpos = SnesToPc(address_2);
129 hpos++;
130 uint8_t b = rom->data()[hpos];
131
132 room.SetLayer2Mode((b >> 5));
133 room.SetLayerMerging(kLayerMergeTypeList[(b & 0x0C) >> 2]);
134
135 room.SetIsDark((b & 0x01) == 0x01);
136 hpos++;
137 room.SetPaletteDirect(rom->data()[hpos]);
138 hpos++;
139
140 room.SetBackgroundTileset(rom->data()[hpos]);
141 hpos++;
142
143 room.SetSpriteTileset(rom->data()[hpos]);
144 hpos++;
145
146 room.SetLayer2Behavior(rom->data()[hpos]);
147 hpos++;
148
149 room.SetTag1Direct((TagKey)rom->data()[hpos]);
150 hpos++;
151
152 room.SetTag2Direct((TagKey)rom->data()[hpos]);
153 hpos++;
154
155 b = rom->data()[hpos];
156
157 room.SetPitsTargetLayer((uint8_t)(b & 0x03));
158 room.SetStair1TargetLayer((uint8_t)((b >> 2) & 0x03));
159 room.SetStair2TargetLayer((uint8_t)((b >> 4) & 0x03));
160 room.SetStair3TargetLayer((uint8_t)((b >> 6) & 0x03));
161 hpos++;
162 room.SetStair4TargetLayer((uint8_t)(rom->data()[hpos] & 0x03));
163 hpos++;
164
165 room.SetPitsTarget(rom->data()[hpos]);
166 hpos++;
167 room.SetStair1Target(rom->data()[hpos]);
168 hpos++;
169 room.SetStair2Target(rom->data()[hpos]);
170 hpos++;
171 room.SetStair3Target(rom->data()[hpos]);
172 hpos++;
173 room.SetStair4Target(rom->data()[hpos]);
174 hpos++;
175
176 // Load room objects
177 int object_pointer = SnesToPc(room_object_pointer);
178 int room_address = object_pointer + (room_id * 3);
179 int objects_location = SnesToPc(room_address);
180
181 // Load sprites
182 int spr_ptr = 0x040000 | rooms_sprite_pointer;
183 int sprite_address = SnesToPc(dungeon_spr_ptrs | spr_ptr + (room_id * 2));
184
185 // Load additional room features
186 room.LoadDoors();
187 room.LoadTorches();
188 room.LoadBlocks();
189 room.LoadPits();
190
191 room.SetLoaded(true);
192 return room;
193}
194
195void Room::LoadRoomGraphics(uint8_t entrance_blockset) {
196 const auto &room_gfx = rom()->room_blockset_ids;
197 const auto &sprite_gfx = rom()->spriteset_ids;
198
199 for (int i = 0; i < 8; i++) {
200 blocks_[i] = rom()->main_blockset_ids[blockset][i];
201 if (i >= 6 && i <= 6) {
202 // 3-6
203 if (entrance_blockset != 0xFF &&
204 room_gfx[entrance_blockset][i - 3] != 0) {
205 blocks_[i] = room_gfx[entrance_blockset][i - 3];
206 }
207 }
208 }
209
210 blocks_[8] = 115 + 0; // Static Sprites Blocksets (fairy,pot,ect...)
211 blocks_[9] = 115 + 10;
212 blocks_[10] = 115 + 6;
213 blocks_[11] = 115 + 7;
214 for (int i = 0; i < 4; i++) {
215 blocks_[12 + i] = (uint8_t)(sprite_gfx[spriteset + 64][i] + 115);
216 } // 12-16 sprites
217}
218
219constexpr int kGfxBufferOffset = 92 * 2048;
220constexpr int kGfxBufferStride = 512;
221constexpr int kGfxBufferAnimatedFrameOffset = 7 * 2048;
223constexpr int kGfxBufferRoomOffset = 2048;
224constexpr int kGfxBufferRoomSpriteOffset = 512;
225constexpr int kGfxBufferRoomSpriteStride = 2048;
227
229 if (!rom_ || !rom_->is_loaded()) {
230 printf("[CopyRoomGraphicsToBuffer] ROM not loaded\n");
231 return;
232 }
233
234 auto gfx_buffer_data = rom()->mutable_graphics_buffer();
235 if (!gfx_buffer_data || gfx_buffer_data->empty()) {
236 printf("[CopyRoomGraphicsToBuffer] Graphics buffer is null or empty\n");
237 return;
238 }
239
240 printf("[CopyRoomGraphicsToBuffer] Room %d: Copying graphics from blocks\n", room_id_);
241
242 // Copy room graphics to buffer
243 int sheet_pos = 0;
244 int bytes_copied = 0;
245 for (int i = 0; i < 16; i++) {
246 // Validate block index
247 if (blocks_[i] < 0 || blocks_[i] > 255) {
248 sheet_pos += kGfxBufferRoomOffset;
249 continue;
250 }
251
252 int data = 0;
253 int block_offset = blocks_[i] * kGfxBufferRoomOffset;
254
255 // Validate block_offset bounds
256 if (block_offset < 0 || block_offset >= static_cast<int>(gfx_buffer_data->size())) {
257 sheet_pos += kGfxBufferRoomOffset;
258 continue;
259 }
260
261 while (data < kGfxBufferRoomOffset) {
262 int buffer_index = data + block_offset;
263 if (buffer_index >= 0 && buffer_index < static_cast<int>(gfx_buffer_data->size())) {
264 uint8_t map_byte = (*gfx_buffer_data)[buffer_index];
265 // NOTE: DO NOT apply sprite offset here!
266 // current_gfx16_ holds pixel data (palette indices 0-7), not tile IDs.
267 // The 0x88 offset is for tile IDs in tilemaps, not raw pixel data.
268 // if (i < 4) {
269 // map_byte += kGfxBufferRoomSpriteLastLineOffset;
270 // }
271
272 // Validate current_gfx16_ access
273 int gfx_index = data + sheet_pos;
274 if (gfx_index >= 0 && gfx_index < static_cast<int>(sizeof(current_gfx16_))) {
275 current_gfx16_[gfx_index] = map_byte;
276 if (map_byte != 0) bytes_copied++;
277 }
278 }
279 data++;
280 }
281
282 sheet_pos += kGfxBufferRoomOffset;
283 }
284
285 printf("[CopyRoomGraphicsToBuffer] Room %d: Copied %d non-zero bytes to current_gfx16_\n", room_id_, bytes_copied);
287}
288
290 // PERFORMANCE OPTIMIZATION: Check if room properties have changed
291 bool properties_changed = false;
292
293 // Check if graphics properties changed
303 graphics_dirty_ = true;
304 properties_changed = true;
305 }
306
307 // Check if effect/tags changed
308 if (cached_effect_ != static_cast<uint8_t>(effect_) ||
310 cached_effect_ = static_cast<uint8_t>(effect_);
313 objects_dirty_ = true;
314 properties_changed = true;
315 }
316
317 // If nothing changed and textures exist, skip rendering
318 if (!properties_changed && !graphics_dirty_ && !objects_dirty_ && !layout_dirty_ && !textures_dirty_) {
319 auto& bg1_bmp = bg1_buffer_.bitmap();
320 auto& bg2_bmp = bg2_buffer_.bitmap();
321 if (bg1_bmp.texture() && bg2_bmp.texture()) {
322 LOG_DEBUG("[RenderRoomGraphics]", "Room %d: No changes detected, skipping render", room_id_);
323 return;
324 }
325 }
326
327 LOG_DEBUG("[RenderRoomGraphics]", "Room %d: Rendering graphics (dirty_flags: g=%d o=%d l=%d t=%d)",
329
330 // STEP 0: Load graphics if needed
331 if (graphics_dirty_) {
333 graphics_dirty_ = false;
334 }
335
336 // STEP 1: Load layout tiles if needed
337 if (layout_dirty_) {
339 layout_dirty_ = false;
340 }
341
342 // Debug: Log floor graphics values
343 LOG_DEBUG("[RenderRoomGraphics]", "Room %d: floor1=%d, floor2=%d, blocks_size=%zu",
345
346 // LoadGraphicsSheetsIntoArena() removed - using per-room graphics instead
347 // Arena sheets are optional and not needed for room rendering
348
349 // STEP 2: Draw floor tiles to bitmaps (base layer) - if graphics changed OR bitmaps not created yet
350 bool need_floor_draw = graphics_dirty_;
351 auto& bg1_bmp = bg1_buffer_.bitmap();
352 auto& bg2_bmp = bg2_buffer_.bitmap();
353
354 // Always draw floor if bitmaps don't exist yet (first time rendering)
355 if (!bg1_bmp.is_active() || bg1_bmp.width() == 0 || !bg2_bmp.is_active() || bg2_bmp.width() == 0) {
356 need_floor_draw = true;
357 LOG_DEBUG("[RenderRoomGraphics]", "Room %d: Bitmaps not created yet, forcing floor draw", room_id_);
358 }
359
360 if (need_floor_draw) {
365 }
366
367 // STEP 3: Draw background tiles (walls/structure) to buffers - if graphics changed OR bitmaps just created
368 bool need_bg_draw = graphics_dirty_ || need_floor_draw;
369 if (need_bg_draw) {
370 bg1_buffer_.DrawBackground(std::span<uint8_t>(current_gfx16_));
371 bg2_buffer_.DrawBackground(std::span<uint8_t>(current_gfx16_));
372 }
373
374 // Get and apply palette BEFORE rendering objects (so objects use correct colors)
375 auto& dungeon_pal_group = rom()->mutable_palette_group()->dungeon_main;
376 int num_palettes = dungeon_pal_group.size();
377
378 // Use palette indirection table lookup (same as dungeon_canvas_viewer.cc line 854)
379 int palette_id = palette; // Default fallback
380 if (palette < rom()->paletteset_ids.size() && !rom()->paletteset_ids[palette].empty()) {
381 auto dungeon_palette_ptr = rom()->paletteset_ids[palette][0];
382 auto palette_word = rom()->ReadWord(0xDEC4B + dungeon_palette_ptr);
383 if (palette_word.ok()) {
384 palette_id = palette_word.value() / 180; // Divide by 180 to get group index
385 LOG_DEBUG("[RenderRoomGraphics]", "Palette lookup: byte=0x%02X → group_id=%d", palette, palette_id);
386 }
387 }
388
389 // Clamp to valid range
390 if (palette_id < 0 || palette_id >= num_palettes) {
391 palette_id = palette_id % num_palettes;
392 }
393
394 auto bg1_palette = dungeon_pal_group[palette_id];
395
396 if (bg1_palette.size() > 0) {
397 // Apply FULL 90-color dungeon palette
398 bg1_bmp.SetPalette(bg1_palette);
399 bg2_bmp.SetPalette(bg1_palette);
400 }
401
402 // Render objects ON TOP of background tiles (AFTER palette is set)
403 // ObjectDrawer will write indexed pixel data that uses the palette we just set
405
406 // PERFORMANCE OPTIMIZATION: Queue texture commands but DON'T process immediately
407 // This allows multiple rooms to batch their texture updates together
408 // The dungeon_canvas_viewer.cc:552 will process all queued textures once per frame
409 if (bg1_bmp.texture()) {
410 // Texture exists - UPDATE it with new object data
411 LOG_DEBUG("[RenderRoomGraphics]", "Queueing UPDATE for existing textures (deferred)");
416 } else {
417 // No texture yet - CREATE it
418 LOG_DEBUG("[RenderRoomGraphics]", "Queueing CREATE for new textures (deferred)");
423 }
424
425 // Mark textures as clean after successful queuing
426 textures_dirty_ = false;
427
428 // REMOVED: Don't process texture queue here - let it be batched!
429 // Processing happens once per frame in DrawDungeonCanvas()
430 // This dramatically improves performance when multiple rooms are open
431 // gfx::Arena::Get().ProcessTextureQueue(nullptr); // OLD: Caused slowdown!
432 LOG_DEBUG("[RenderRoomGraphics]", "Texture commands queued for batch processing");
433}
434
436 LOG_DEBUG("RenderRoomGraphics", "LoadLayoutTilesToBuffer START for room %d", room_id_);
437
438 if (!rom_ || !rom_->is_loaded()) {
439 LOG_DEBUG("RenderRoomGraphics", "ROM not loaded, aborting");
440 return;
441 }
442
443 const auto& layout_objects = layout_.GetObjects();
444 LOG_DEBUG("RenderRoomGraphics", "Layout has %zu objects", layout_objects.size());
445 if (layout_objects.empty()) {
446 return;
447 }
448
449 int tiles_written_bg1 = 0;
450 int tiles_written_bg2 = 0;
451 int tiles_skipped = 0;
452
453 for (const auto& layout_obj : layout_objects) {
454 uint8_t x = layout_obj.x();
455 uint8_t y = layout_obj.y();
456
457 auto tile_result = layout_obj.GetTile(0);
458 if (!tile_result.ok()) {
459 tiles_skipped++;
460 continue;
461 }
462 const auto* tile_info = tile_result.value();
463 uint16_t tile_word = gfx::TileInfoToWord(*tile_info);
464
465 if (layout_obj.GetLayerValue() == 1) {
466 bg2_buffer_.SetTileAt(x, y, tile_word);
467 tiles_written_bg2++;
468 } else {
469 bg1_buffer_.SetTileAt(x, y, tile_word);
470 tiles_written_bg1++;
471 }
472 }
473
474 LOG_DEBUG("RenderRoomGraphics", "Layout tiles: BG1=%d BG2=%d skipped=%d", tiles_written_bg1, tiles_written_bg2, tiles_skipped);
475}
476
478 LOG_DEBUG("[RenderObjectsToBackground]", "Starting object rendering for room %d", room_id_);
479
480 if (!rom_ || !rom_->is_loaded()) {
481 LOG_DEBUG("[RenderObjectsToBackground]", "ROM not loaded, aborting");
482 return;
483 }
484
485 // PERFORMANCE OPTIMIZATION: Only render objects if they have changed or if graphics changed
486 // Also render if bitmaps were just created (need_floor_draw was true in RenderRoomGraphics)
487 auto& bg1_bmp = bg1_buffer_.bitmap();
488 auto& bg2_bmp = bg2_buffer_.bitmap();
489 bool bitmaps_exist = bg1_bmp.is_active() && bg1_bmp.width() > 0 && bg2_bmp.is_active() && bg2_bmp.width() > 0;
490
491 if (!objects_dirty_ && !graphics_dirty_ && bitmaps_exist) {
492 LOG_DEBUG("[RenderObjectsToBackground]", "Room %d: Objects not dirty, skipping render", room_id_);
493 return;
494 }
495
496 // Get palette group for object rendering (use SAME lookup as RenderRoomGraphics)
497 auto& dungeon_pal_group = rom()->mutable_palette_group()->dungeon_main;
498 int num_palettes = dungeon_pal_group.size();
499
500 // Use palette indirection table lookup
501 int palette_id = palette;
502 if (palette < rom()->paletteset_ids.size() && !rom()->paletteset_ids[palette].empty()) {
503 auto dungeon_palette_ptr = rom()->paletteset_ids[palette][0];
504 auto palette_word = rom()->ReadWord(0xDEC4B + dungeon_palette_ptr);
505 if (palette_word.ok()) {
506 palette_id = palette_word.value() / 180;
507 }
508 }
509
510 if (palette_id < 0 || palette_id >= num_palettes) {
511 palette_id = 0;
512 }
513
514 auto room_palette = dungeon_pal_group[palette_id];
515 // Dungeon palettes are 16-color sub-palettes. Split the 90-color palette into 16-color groups.
516 auto palette_group_result = gfx::CreatePaletteGroupFromLargePalette(room_palette, 16);
517 if (!palette_group_result.ok()) {
518 // Fallback to empty palette group
519 gfx::PaletteGroup empty_group;
522 return;
523 }
524 auto palette_group = palette_group_result.value();
525
526 // Use ObjectDrawer for pattern-based object rendering
527 // This provides proper wall/object drawing patterns
528 // Pass the room-specific graphics buffer (current_gfx16_) so objects use correct tiles
530 auto status = drawer.DrawObjectList(tile_objects_, bg1_buffer_, bg2_buffer_, palette_group);
531
532 // Log only failures, not successes
533 if (!status.ok()) {
534 LOG_DEBUG("[RenderObjectsToBackground]", "ObjectDrawer failed: %s", std::string(status.message()).c_str());
535 } else {
536 // Mark objects as clean after successful render
537 objects_dirty_ = false;
538 LOG_DEBUG("[RenderObjectsToBackground]", "Room %d: Objects rendered successfully", room_id_);
539 }
540}
541
542// LoadGraphicsSheetsIntoArena() removed - using per-room graphics instead
543// Room rendering no longer depends on Arena graphics sheets
544
546 if (!rom_ || !rom_->is_loaded()) {
547 return;
548 }
549
550 auto gfx_buffer_data = rom()->mutable_graphics_buffer();
551 if (!gfx_buffer_data || gfx_buffer_data->empty()) {
552 return;
553 }
554
555 auto rom_data = rom()->vector();
556 if (rom_data.empty()) {
557 return;
558 }
559
560 // Validate animated_frame_ bounds
561 if (animated_frame_ < 0 || animated_frame_ > 10) {
562 return;
563 }
564
565 // Validate background_tileset_ bounds
566 if (background_tileset_ < 0 || background_tileset_ > 255) {
567 return;
568 }
569
570 int gfx_ptr = SnesToPc(rom()->version_constants().kGfxAnimatedPointer);
571 if (gfx_ptr < 0 || gfx_ptr >= static_cast<int>(rom_data.size())) {
572 return;
573 }
574
575 int data = 0;
576 while (data < 512) {
577 // Validate buffer access for first operation
578 int first_offset = data + (92 * 2048) + (512 * animated_frame_);
579 if (first_offset >= 0 && first_offset < static_cast<int>(gfx_buffer_data->size())) {
580 uint8_t map_byte = (*gfx_buffer_data)[first_offset];
581
582 // Validate current_gfx16_ access
583 int gfx_offset = data + (7 * 2048);
584 if (gfx_offset >= 0 && gfx_offset < static_cast<int>(sizeof(current_gfx16_))) {
585 current_gfx16_[gfx_offset] = map_byte;
586 }
587 }
588
589 // Validate buffer access for second operation
590 int tileset_index = rom_data[gfx_ptr + background_tileset_];
591 int second_offset = data + (tileset_index * 2048) + (512 * animated_frame_);
592 if (second_offset >= 0 && second_offset < static_cast<int>(gfx_buffer_data->size())) {
593 uint8_t map_byte = (*gfx_buffer_data)[second_offset];
594
595 // Validate current_gfx16_ access
596 int gfx_offset = data + (7 * 2048) - 512;
597 if (gfx_offset >= 0 && gfx_offset < static_cast<int>(sizeof(current_gfx16_))) {
598 current_gfx16_[gfx_offset] = map_byte;
599 }
600 }
601
602 data++;
603 }
604}
605
607 LOG_DEBUG("[LoadObjects]", "Starting LoadObjects for room %d", room_id_);
608 auto rom_data = rom()->vector();
609
610 // Enhanced object loading with comprehensive validation
611 int object_pointer = (rom_data[room_object_pointer + 2] << 16) +
612 (rom_data[room_object_pointer + 1] << 8) +
613 (rom_data[room_object_pointer]);
614 object_pointer = SnesToPc(object_pointer);
615
616 // Enhanced bounds checking for object pointer
617 if (object_pointer < 0 || object_pointer >= (int)rom_->size()) {
618 return;
619 }
620
621 int room_address = object_pointer + (room_id_ * 3);
622
623 // Enhanced bounds checking for room address
624 if (room_address < 0 || room_address + 2 >= (int)rom_->size()) {
625 return;
626 }
627
628 int tile_address = (rom_data[room_address + 2] << 16) +
629 (rom_data[room_address + 1] << 8) + rom_data[room_address];
630
631 int objects_location = SnesToPc(tile_address);
632
633 // Enhanced bounds checking for objects location
634 if (objects_location < 0 || objects_location >= (int)rom_->size()) {
635 return;
636 }
637
638 // Parse floor graphics and layout with validation
639 if (objects_location + 1 < (int)rom_->size()) {
640 if (is_floor_) {
641 floor1_graphics_ = static_cast<uint8_t>(rom_data[objects_location] & 0x0F);
642 floor2_graphics_ = static_cast<uint8_t>((rom_data[objects_location] >> 4) & 0x0F);
643 LOG_DEBUG("[LoadObjects]", "Room %d: Set floor1_graphics_=%d, floor2_graphics_=%d",
645 }
646
647 layout = static_cast<uint8_t>((rom_data[objects_location + 1] >> 2) & 0x07);
648 }
649
650 LoadChests();
651
652 // Parse objects with enhanced error handling
653 ParseObjectsFromLocation(objects_location + 2);
654}
655
656void Room::ParseObjectsFromLocation(int objects_location) {
657 auto rom_data = rom()->vector();
658
659 z3_staircases_.clear();
660 int nbr_of_staircase = 0;
661
662 int pos = objects_location;
663 uint8_t b1 = 0;
664 uint8_t b2 = 0;
665 uint8_t b3 = 0;
666 int layer = 0;
667 bool door = false;
668 bool end_read = false;
669
670 // Enhanced parsing loop with bounds checking
671 while (!end_read && pos < (int)rom_->size()) {
672 // Check if we have enough bytes to read
673 if (pos + 1 >= (int)rom_->size()) {
674 break;
675 }
676
677 b1 = rom_data[pos];
678 b2 = rom_data[pos + 1];
679
680 if (b1 == 0xFF && b2 == 0xFF) {
681 pos += 2; // Jump to next layer
682 layer++;
683 door = false;
684 if (layer == 3) {
685 break;
686 }
687 continue;
688 }
689
690 if (b1 == 0xF0 && b2 == 0xFF) {
691 pos += 2; // Jump to door section
692 door = true;
693 continue;
694 }
695
696 // Check if we have enough bytes for object data
697 if (pos + 2 >= (int)rom_->size()) {
698 break;
699 }
700
701 b3 = rom_data[pos + 2];
702 if (door) {
703 pos += 2;
704 } else {
705 pos += 3;
706 }
707
708 if (!door) {
709 // Use the refactored encoding/decoding functions (Phase 1, Task 1.2)
710 RoomObject r = RoomObject::DecodeObjectFromBytes(b1, b2, b3, static_cast<uint8_t>(layer));
711
712 // Validate object ID before adding to the room
713 if (r.id_ >= 0 && r.id_ <= 0x3FF) {
714 r.set_rom(rom_);
715 tile_objects_.push_back(r);
716
717 // Handle special object types (staircases, chests, etc.)
718 HandleSpecialObjects(r.id_, r.x(), r.y(), nbr_of_staircase);
719 }
720 } else {
721 // Handle door objects (placeholder for future implementation)
722 // tile_objects_.push_back(z3_object_door(static_cast<short>((b2 << 8) + b1),
723 // 0, 0, 0, static_cast<uint8_t>(layer)));
724 }
725 }
726}
727
728// ============================================================================
729// Object Saving Implementation (Phase 1, Task 1.3)
730// ============================================================================
731
732std::vector<uint8_t> Room::EncodeObjects() const {
733 std::vector<uint8_t> bytes;
734
735 // Organize objects by layer
736 std::vector<RoomObject> layer0_objects;
737 std::vector<RoomObject> layer1_objects;
738 std::vector<RoomObject> layer2_objects;
739
740 for (const auto& obj : tile_objects_) {
741 switch (obj.GetLayerValue()) {
742 case 0: layer0_objects.push_back(obj); break;
743 case 1: layer1_objects.push_back(obj); break;
744 case 2: layer2_objects.push_back(obj); break;
745 }
746 }
747
748 // Encode Layer 1 (BG2)
749 for (const auto& obj : layer0_objects) {
750 auto encoded = obj.EncodeObjectToBytes();
751 bytes.push_back(encoded.b1);
752 bytes.push_back(encoded.b2);
753 bytes.push_back(encoded.b3);
754 }
755 bytes.push_back(0xFF);
756 bytes.push_back(0xFF);
757
758 // Encode Layer 2 (BG1)
759 for (const auto& obj : layer1_objects) {
760 auto encoded = obj.EncodeObjectToBytes();
761 bytes.push_back(encoded.b1);
762 bytes.push_back(encoded.b2);
763 bytes.push_back(encoded.b3);
764 }
765 bytes.push_back(0xFF);
766 bytes.push_back(0xFF);
767
768 // Encode Layer 3
769 for (const auto& obj : layer2_objects) {
770 auto encoded = obj.EncodeObjectToBytes();
771 bytes.push_back(encoded.b1);
772 bytes.push_back(encoded.b2);
773 bytes.push_back(encoded.b3);
774 }
775
776 // Final terminator
777 bytes.push_back(0xFF);
778 bytes.push_back(0xFF);
779
780 return bytes;
781}
782
783absl::Status Room::SaveObjects() {
784 if (rom_ == nullptr) {
785 return absl::InvalidArgumentError("ROM pointer is null");
786 }
787
788 auto rom_data = rom()->vector();
789
790 // Get object pointer
791 int object_pointer = (rom_data[room_object_pointer + 2] << 16) +
792 (rom_data[room_object_pointer + 1] << 8) +
793 (rom_data[room_object_pointer]);
794 object_pointer = SnesToPc(object_pointer);
795
796 if (object_pointer < 0 || object_pointer >= (int)rom_->size()) {
797 return absl::OutOfRangeError("Object pointer out of range");
798 }
799
800 int room_address = object_pointer + (room_id_ * 3);
801
802 if (room_address < 0 || room_address + 2 >= (int)rom_->size()) {
803 return absl::OutOfRangeError("Room address out of range");
804 }
805
806 int tile_address = (rom_data[room_address + 2] << 16) +
807 (rom_data[room_address + 1] << 8) + rom_data[room_address];
808
809 int objects_location = SnesToPc(tile_address);
810
811 if (objects_location < 0 || objects_location >= (int)rom_->size()) {
812 return absl::OutOfRangeError("Objects location out of range");
813 }
814
815 // Skip graphics/layout header (2 bytes)
816 int write_pos = objects_location + 2;
817
818 // Encode all objects
819 auto encoded_bytes = EncodeObjects();
820
821 // Write encoded bytes to ROM using WriteVector
822 return rom_->WriteVector(write_pos, encoded_bytes);
823}
824
825// ============================================================================
826// Object Manipulation Methods (Phase 3)
827// ============================================================================
828
829absl::Status Room::AddObject(const RoomObject& object) {
830 // Validate object
831 if (!ValidateObject(object)) {
832 return absl::InvalidArgumentError("Invalid object parameters");
833 }
834
835 // Add to internal list
836 tile_objects_.push_back(object);
837
838 return absl::OkStatus();
839}
840
841absl::Status Room::RemoveObject(size_t index) {
842 if (index >= tile_objects_.size()) {
843 return absl::OutOfRangeError("Object index out of range");
844 }
845
846 tile_objects_.erase(tile_objects_.begin() + index);
847
848 return absl::OkStatus();
849}
850
851absl::Status Room::UpdateObject(size_t index, const RoomObject& object) {
852 if (index >= tile_objects_.size()) {
853 return absl::OutOfRangeError("Object index out of range");
854 }
855
856 if (!ValidateObject(object)) {
857 return absl::InvalidArgumentError("Invalid object parameters");
858 }
859
860 tile_objects_[index] = object;
861
862 return absl::OkStatus();
863}
864
865absl::StatusOr<size_t> Room::FindObjectAt(int x, int y, int layer) const {
866 for (size_t i = 0; i < tile_objects_.size(); i++) {
867 const auto& obj = tile_objects_[i];
868 if (obj.x() == x && obj.y() == y && obj.GetLayerValue() == layer) {
869 return i;
870 }
871 }
872 return absl::NotFoundError("No object found at position");
873}
874
875bool Room::ValidateObject(const RoomObject& object) const {
876 // Validate position (0-63 for both X and Y)
877 if (object.x() < 0 || object.x() > 63) return false;
878 if (object.y() < 0 || object.y() > 63) return false;
879
880 // Validate layer (0-2)
881 if (object.GetLayerValue() < 0 || object.GetLayerValue() > 2) return false;
882
883 // Validate object ID range
884 if (object.id_ < 0 || object.id_ > 0xFFF) return false;
885
886 // Validate size for Type 1 objects
887 if (object.id_ < 0x100 && object.size() > 15) return false;
888
889 return true;
890}
891
892void Room::HandleSpecialObjects(short oid, uint8_t posX, uint8_t posY, int& nbr_of_staircase) {
893 // Handle staircase objects
894 for (short stair : stairsObjects) {
895 if (stair == oid) {
896 if (nbr_of_staircase < 4) {
897 tile_objects_.back().set_options(ObjectOption::Stairs |
898 tile_objects_.back().options());
899 z3_staircases_.push_back({
900 posX, posY,
901 absl::StrCat("To ", staircase_rooms_[nbr_of_staircase])
902 .data()});
903 nbr_of_staircase++;
904 } else {
905 tile_objects_.back().set_options(ObjectOption::Stairs |
906 tile_objects_.back().options());
907 z3_staircases_.push_back({posX, posY, "To ???"});
908 }
909 break;
910 }
911 }
912
913 // Handle chest objects
914 if (oid == 0xF99) {
915 if (chests_in_room_.size() > 0) {
916 tile_objects_.back().set_options(ObjectOption::Chest |
917 tile_objects_.back().options());
918 chests_in_room_.erase(chests_in_room_.begin());
919 }
920 } else if (oid == 0xFB1) {
921 if (chests_in_room_.size() > 0) {
922 tile_objects_.back().set_options(ObjectOption::Chest |
923 tile_objects_.back().options());
924 chests_in_room_.erase(chests_in_room_.begin());
925 }
926 }
927}
928
930 auto rom_data = rom()->vector();
931 int sprite_pointer = (0x04 << 16) +
932 (rom_data[rooms_sprite_pointer + 1] << 8) +
933 (rom_data[rooms_sprite_pointer]);
934 int sprite_address_snes =
935 (0x09 << 16) + (rom_data[sprite_pointer + (room_id_ * 2) + 1] << 8) +
936 rom_data[sprite_pointer + (room_id_ * 2)];
937
938 int sprite_address = SnesToPc(sprite_address_snes);
939 if (rom_data[sprite_address] == 1) {
940 // sortsprites is unused
941 }
942 sprite_address += 1;
943
944 while (true) {
945 uint8_t b1 = rom_data[sprite_address];
946 uint8_t b2 = rom_data[sprite_address + 1];
947 uint8_t b3 = rom_data[sprite_address + 2];
948
949 if (b1 == 0xFF) {
950 break;
951 }
952
953 sprites_.emplace_back(b3, (b2 & 0x1F), (b1 & 0x1F),
954 ((b2 & 0xE0) >> 5) + ((b1 & 0x60) >> 2),
955 (b1 & 0x80) >> 7);
956
957 if (sprites_.size() > 1) {
958 Sprite &spr = sprites_.back();
959 Sprite &prevSprite = sprites_[sprites_.size() - 2];
960
961 if (spr.id() == 0xE4 && spr.x() == 0x00 && spr.y() == 0x1E &&
962 spr.layer() == 1 && spr.subtype() == 0x18) {
963 prevSprite.set_key_drop(1);
964 sprites_.pop_back();
965 }
966
967 if (spr.id() == 0xE4 && spr.x() == 0x00 && spr.y() == 0x1D &&
968 spr.layer() == 1 && spr.subtype() == 0x18) {
969 prevSprite.set_key_drop(2);
970 sprites_.pop_back();
971 }
972 }
973
974 sprite_address += 3;
975 }
976}
977
979 auto rom_data = rom()->vector();
980 uint32_t cpos = SnesToPc((rom_data[chests_data_pointer1 + 2] << 16) +
981 (rom_data[chests_data_pointer1 + 1] << 8) +
982 (rom_data[chests_data_pointer1]));
983 size_t clength = (rom_data[chests_length_pointer + 1] << 8) +
984 (rom_data[chests_length_pointer]);
985
986 for (size_t i = 0; i < clength; i++) {
987 if ((((rom_data[cpos + (i * 3) + 1] << 8) + (rom_data[cpos + (i * 3)])) &
988 0x7FFF) == room_id_) {
989 // There's a chest in that room !
990 bool big = false;
991 if ((((rom_data[cpos + (i * 3) + 1] << 8) + (rom_data[cpos + (i * 3)])) &
992 0x8000) == 0x8000) {
993 big = true;
994 }
995
996 chests_in_room_.emplace_back(
997 chest_data{rom_data[cpos + (i * 3) + 2], big});
998 }
999 }
1000}
1001
1003 auto rom_data = rom()->vector();
1004
1005 // Doors are loaded as part of the object stream in LoadObjects()
1006 // When the parser encounters 0xF0 0xFF, it enters door mode
1007 // Door objects have format: b1 (position/direction), b2 (type)
1008 // Door encoding: b1 = (door_pos << 3) + door_dir
1009 // b2 = door_type
1010 // This is already handled in ParseObjectsFromLocation()
1011
1012 LOG_DEBUG("Room", "LoadDoors for room %d - doors are loaded via object stream", room_id_);
1013}
1014
1016 auto rom_data = rom()->vector();
1017
1018 // Read torch data length
1019 int bytes_count = (rom_data[torches_length_pointer + 1] << 8) |
1020 rom_data[torches_length_pointer];
1021
1022 LOG_DEBUG("Room", "LoadTorches: room_id=%d, bytes_count=%d", room_id_, bytes_count);
1023
1024 // Iterate through torch data to find torches for this room
1025 for (int i = 0; i < bytes_count; i += 2) {
1026 if (i + 1 >= bytes_count) break;
1027
1028 uint8_t b1 = rom_data[torch_data + i];
1029 uint8_t b2 = rom_data[torch_data + i + 1];
1030
1031 // Skip 0xFFFF markers
1032 if (b1 == 0xFF && b2 == 0xFF) {
1033 continue;
1034 }
1035
1036 // Check if this entry is for our room
1037 uint16_t torch_room_id = (b2 << 8) | b1;
1038 if (torch_room_id == room_id_) {
1039 // Found torches for this room, read them
1040 i += 2;
1041 while (i < bytes_count) {
1042 if (i + 1 >= bytes_count) break;
1043
1044 b1 = rom_data[torch_data + i];
1045 b2 = rom_data[torch_data + i + 1];
1046
1047 // End of torch list for this room
1048 if (b1 == 0xFF && b2 == 0xFF) {
1049 break;
1050 }
1051
1052 // Decode torch position and properties
1053 int address = ((b2 & 0x1F) << 8 | b1) >> 1;
1054 uint8_t px = address % 64;
1055 uint8_t py = address >> 6;
1056 uint8_t layer = (b2 & 0x20) >> 5;
1057 bool lit = (b2 & 0x80) == 0x80;
1058
1059 // Create torch object (ID 0x150)
1060 RoomObject torch_obj(0x150, px, py, 0, layer);
1061 torch_obj.set_rom(rom_);
1063 // Store lit state if needed (may require adding a field to RoomObject)
1064
1065 tile_objects_.push_back(torch_obj);
1066
1067 LOG_DEBUG("Room", "Loaded torch at (%d,%d) layer=%d lit=%d",
1068 px, py, layer, lit);
1069
1070 i += 2;
1071 }
1072 break; // Found and processed our room's torches
1073 } else {
1074 // Skip to next room's torches
1075 i += 2;
1076 while (i < bytes_count) {
1077 if (i + 1 >= bytes_count) break;
1078 b1 = rom_data[torch_data + i];
1079 b2 = rom_data[torch_data + i + 1];
1080 if (b1 == 0xFF && b2 == 0xFF) {
1081 break;
1082 }
1083 i += 2;
1084 }
1085 }
1086 }
1087}
1088
1090 auto rom_data = rom()->vector();
1091
1092 // Read blocks length
1093 int blocks_count = (rom_data[blocks_length + 1] << 8) | rom_data[blocks_length];
1094
1095 LOG_DEBUG("Room", "LoadBlocks: room_id=%d, blocks_count=%d", room_id_, blocks_count);
1096
1097 // Load block data from multiple pointers
1098 std::vector<uint8_t> blocks_data(blocks_count);
1099
1100 int pos1 = blocks_pointer1;
1101 int pos2 = blocks_pointer2;
1102 int pos3 = blocks_pointer3;
1103 int pos4 = blocks_pointer4;
1104
1105 // Read block data from 4 different locations
1106 for (int i = 0; i < 0x80 && i < blocks_count; i++) {
1107 blocks_data[i] = rom_data[pos1 + i];
1108
1109 if (i + 0x80 < blocks_count) {
1110 blocks_data[i + 0x80] = rom_data[pos2 + i];
1111 }
1112 if (i + 0x100 < blocks_count) {
1113 blocks_data[i + 0x100] = rom_data[pos3 + i];
1114 }
1115 if (i + 0x180 < blocks_count) {
1116 blocks_data[i + 0x180] = rom_data[pos4 + i];
1117 }
1118 }
1119
1120 // Parse blocks for this room (4 bytes per block entry)
1121 for (int i = 0; i < blocks_count; i += 4) {
1122 if (i + 3 >= blocks_count) break;
1123
1124 uint8_t b1 = blocks_data[i];
1125 uint8_t b2 = blocks_data[i + 1];
1126 uint8_t b3 = blocks_data[i + 2];
1127 uint8_t b4 = blocks_data[i + 3];
1128
1129 // Check if this block belongs to our room
1130 uint16_t block_room_id = (b2 << 8) | b1;
1131 if (block_room_id == room_id_) {
1132 // End marker for this room's blocks
1133 if (b3 == 0xFF && b4 == 0xFF) {
1134 break;
1135 }
1136
1137 // Decode block position
1138 int address = ((b4 & 0x1F) << 8 | b3) >> 1;
1139 uint8_t px = address % 64;
1140 uint8_t py = address >> 6;
1141 uint8_t layer = (b4 & 0x20) >> 5;
1142
1143 // Create block object (ID 0x0E00)
1144 RoomObject block_obj(0x0E00, px, py, 0, layer);
1145 block_obj.set_rom(rom_);
1147
1148 tile_objects_.push_back(block_obj);
1149
1150 LOG_DEBUG("Room", "Loaded block at (%d,%d) layer=%d", px, py, layer);
1151 }
1152 }
1153}
1154
1156 auto rom_data = rom()->vector();
1157
1158 // Read pit count
1159 int pit_entries = rom_data[pit_count] / 2;
1160
1161 // Read pit pointer (long pointer)
1162 int pit_ptr = (rom_data[pit_pointer + 2] << 16) |
1163 (rom_data[pit_pointer + 1] << 8) |
1164 rom_data[pit_pointer];
1165 int pit_data_addr = SnesToPc(pit_ptr);
1166
1167 LOG_DEBUG("Room", "LoadPits: room_id=%d, pit_entries=%d, pit_ptr=0x%06X",
1168 room_id_, pit_entries, pit_ptr);
1169
1170 // Pit data is stored as: room_id (2 bytes), target info (2 bytes)
1171 // This data is already loaded in LoadRoomFromRom() into pits_ destination struct
1172 // The pit destination (where you go when you fall) is set via SetPitsTarget()
1173
1174 // Pits are typically represented in the layout/collision data, not as objects
1175 // The pits_ member already contains the target room and layer
1176 LOG_DEBUG("Room", "Pit destination - target=%d, target_layer=%d",
1178}
1179
1180} // namespace zelda3
1181} // namespace yaze
The Rom class is used to load, save, and modify Rom data.
Definition rom.h:71
absl::Status WriteVector(int addr, std::vector< uint8_t > data)
Definition rom.cc:777
auto data() const
Definition rom.h:203
auto size() const
Definition rom.h:202
bool is_loaded() const
Definition rom.h:197
void QueueTextureCommand(TextureCommandType type, Bitmap *bitmap)
Definition arena.cc:32
static Arena & Get()
Definition arena.cc:15
void DrawBackground(std::span< uint8_t > gfx16_data)
void SetTileAt(int x, int y, uint16_t value)
void DrawFloor(const std::vector< uint8_t > &rom_data, int tile_address, int tile_address_floor, uint8_t floor_graphics)
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)
Draw all objects in a room.
const std::vector< RoomObject > & GetObjects() const
Definition room_layout.h:22
static RoomObject DecodeObjectFromBytes(uint8_t b1, uint8_t b2, uint8_t b3, uint8_t layer)
void set_rom(Rom *rom)
Definition room_object.h:67
void set_options(ObjectOption options)
void SetBlockset(uint8_t blockset)
Definition room.h:271
bool ValidateObject(const RoomObject &object) const
Definition room.cc:875
destination pits_
Definition room.h:457
std::vector< RoomObject > tile_objects_
Definition room.h:442
uint8_t cached_blockset_
Definition room.h:409
void SetSpriteset(uint8_t spriteset)
Definition room.h:277
EffectKey effect_
Definition room.h:452
void SetTag2Direct(TagKey tag2)
Definition room.h:324
uint8_t cached_layout_
Definition room.h:412
absl::Status UpdateObject(size_t index, const RoomObject &object)
Definition room.cc:851
TagKey cached_tag2_
Definition room.h:417
void SetStair4Target(uint8_t target)
Definition room.h:334
void SetPitsTarget(uint8_t target)
Definition room.h:330
void SetIsLight(bool is_light)
Definition room.h:264
void LoadChests()
Definition room.cc:978
gfx::BackgroundBuffer bg2_buffer_
Definition room.h:401
uint8_t cached_floor2_graphics_
Definition room.h:414
std::vector< zelda3::Sprite > sprites_
Definition room.h:444
void SetLoaded(bool loaded)
Definition room.h:338
std::array< uint8_t, 0x4000 > current_gfx16_
Definition room.h:397
void CopyRoomGraphicsToBuffer()
Definition room.cc:228
uint8_t cached_palette_
Definition room.h:411
void LoadRoomGraphics(uint8_t entrance_blockset=0xFF)
Definition room.cc:195
uint8_t cached_effect_
Definition room.h:415
bool graphics_dirty_
Definition room.h:420
void SetStaircaseRoom(int index, uint8_t room)
Definition room.h:305
absl::Status RemoveObject(size_t index)
Definition room.cc:841
void SetStair1TargetLayer(uint8_t layer)
Definition room.h:326
void LoadBlocks()
Definition room.cc:1089
bool layout_dirty_
Definition room.h:422
void SetLayer2Mode(uint8_t mode)
Definition room.h:316
RoomLayout layout_
Definition room.h:447
void LoadLayoutTilesToBuffer()
Definition room.cc:435
void SetTag2(TagKey tag2)
Definition room.h:295
void ParseObjectsFromLocation(int objects_location)
Definition room.cc:656
uint8_t cached_floor1_graphics_
Definition room.h:413
void SetTag1Direct(TagKey tag1)
Definition room.h:323
void LoadTorches()
Definition room.cc:1015
void SetPaletteDirect(uint8_t palette)
Definition room.h:319
void SetStair2Target(uint8_t target)
Definition room.h:332
int animated_frame_
Definition room.h:426
void SetCollision(CollisionKey collision)
Definition room.h:263
uint8_t blockset
Definition room.h:349
gfx::BackgroundBuffer bg1_buffer_
Definition room.h:400
absl::Status SaveObjects()
Definition room.cc:783
void SetStaircasePlane(int index, uint8_t plane)
Definition room.h:301
void SetIsDark(bool is_dark)
Definition room.h:318
bool objects_dirty_
Definition room.h:421
uint8_t layout
Definition room.h:352
void RenderRoomGraphics()
Definition room.cc:289
bool textures_dirty_
Definition room.h:423
uint8_t staircase_rooms_[4]
Definition room.h:429
std::vector< uint8_t > EncodeObjects() const
Definition room.cc:732
void SetEffect(EffectKey effect)
Definition room.h:283
std::array< uint8_t, 16 > blocks_
Definition room.h:439
void SetTag1(TagKey tag1)
Definition room.h:289
void SetBackgroundTileset(uint8_t tileset)
Definition room.h:320
void SetStair3TargetLayer(uint8_t layer)
Definition room.h:328
uint8_t floor2_graphics_
Definition room.h:436
bool IsLight() const
Definition room.h:312
uint8_t floor1_graphics_
Definition room.h:435
void SetMessageIdDirect(uint16_t message_id)
Definition room.h:315
void SetLayerMerging(LayerMergeType merging)
Definition room.h:317
void SetPitsTargetLayer(uint8_t layer)
Definition room.h:325
void LoadObjects()
Definition room.cc:606
uint8_t spriteset
Definition room.h:350
void SetSpriteTileset(uint8_t tileset)
Definition room.h:321
void SetStair1Target(uint8_t target)
Definition room.h:331
void SetBg2(background2 bg2)
Definition room.h:262
uint8_t palette
Definition room.h:351
void LoadAnimatedGraphics()
Definition room.cc:545
std::vector< chest_data > chests_in_room_
Definition room.h:446
uint8_t cached_spriteset_
Definition room.h:410
std::vector< staircase > z3_staircases_
Definition room.h:445
void SetPalette(uint8_t palette)
Definition room.h:265
void LoadSprites()
Definition room.cc:929
TagKey cached_tag1_
Definition room.h:416
void SetHolewarp(uint8_t holewarp)
Definition room.h:304
uint8_t background_tileset_
Definition room.h:431
void SetStair3Target(uint8_t target)
Definition room.h:333
void HandleSpecialObjects(short oid, uint8_t posX, uint8_t posY, int &nbr_of_staircase)
Definition room.cc:892
void SetStair4TargetLayer(uint8_t layer)
Definition room.h:329
absl::Status AddObject(const RoomObject &object)
Definition room.cc:829
absl::StatusOr< size_t > FindObjectAt(int x, int y, int layer) const
Definition room.cc:865
void SetStair2TargetLayer(uint8_t layer)
Definition room.h:327
void RenderObjectsToBackground()
Definition room.cc:477
void SetLayer2Behavior(uint8_t behavior)
Definition room.h:322
A class for managing sprites in the overworld and underworld.
Definition sprite.h:279
auto id() const
Definition sprite.h:338
auto layer() const
Definition sprite.h:349
auto set_key_drop(int key)
Definition sprite.h:357
auto subtype() const
Definition sprite.h:350
auto y() const
Definition sprite.h:341
auto x() const
Definition sprite.h:340
zelda3_bg2_effect
Background layer 2 effects.
Definition zelda.h:368
#define LOG_DEBUG(category, format,...)
Definition log.h:104
uint16_t TileInfoToWord(TileInfo tile_info)
Definition snes_tile.cc:291
absl::StatusOr< PaletteGroup > CreatePaletteGroupFromLargePalette(SnesPalette &palette, int num_colors)
Take a SNESPalette, divide it into palettes of 8 colors.
constexpr int kGfxBufferAnimatedFrameStride
Definition room.cc:222
constexpr int kGfxBufferAnimatedFrameOffset
Definition room.cc:221
constexpr int chests_length_pointer
Definition room.h:31
constexpr int chests_data_pointer1
Definition room.h:32
constexpr int pit_count
Definition room.h:46
constexpr int blocks_pointer3
Definition room.h:37
constexpr int blocks_pointer4
Definition room.h:38
constexpr int kGfxBufferRoomOffset
Definition room.cc:223
constexpr int rooms_sprite_pointer
Definition room.h:29
constexpr int kGfxBufferRoomSpriteOffset
Definition room.cc:224
RoomSize CalculateRoomSize(Rom *rom, int room_id)
Definition room.cc:21
constexpr int messages_id_dungeon
Definition room.h:33
constexpr int blocks_length
Definition room.h:34
constexpr int tile_address_floor
Definition room.h:59
constexpr int blocks_pointer2
Definition room.h:36
constexpr int kGfxBufferRoomSpriteStride
Definition room.cc:225
constexpr uint16_t stairsObjects[]
Definition room.h:61
Room LoadRoomFromRom(Rom *rom, int room_id)
Definition room.cc:75
constexpr int blocks_pointer1
Definition room.h:35
constexpr int torches_length_pointer
Definition room.h:40
constexpr int kRoomHeaderPointer
constexpr int torch_data
Definition room.h:39
constexpr int kRoomHeaderPointerBank
constexpr int kGfxBufferStride
Definition room.cc:220
constexpr int room_object_pointer
Definition room.h:25
constexpr int kGfxBufferRoomSpriteLastLineOffset
Definition room.cc:226
constexpr int tile_address
Definition room.h:58
constexpr int pit_pointer
Definition room.h:45
constexpr int dungeon_spr_ptrs
Definition room.h:57
constexpr int kGfxBufferOffset
Definition room.cc:219
Main namespace for the application.
uint32_t SnesToPc(uint32_t addr) noexcept
Definition snes.h:8
Legacy chest data structure.
Definition zelda.h:437
uint8_t target_layer
Definition zelda.h:450
uint8_t target
Definition zelda.h:449
Represents a group of palettes.
int64_t room_size_pointer
Definition room.h:468
Yet Another Zelda3 Editor (YAZE) - Public C API.