yaze 0.3.2
Link to the Past ROM Editor
 
Loading...
Searching...
No Matches
usage_preview.cc
Go to the documentation of this file.
2
3#include <algorithm>
4#include <cstring>
5
6#include "absl/strings/str_format.h"
19
21
22namespace {
23
24constexpr int kTilesPerSheet = 64;
25
26// Copies 4096 sheet bytes into an 8bpp buffer laid out like Room's and
27// OverworldMap's (16 tiles per 128-pixel row, one sheet per 0x1000 bytes).
28void CopySheet(const std::vector<uint8_t>& sheet, std::vector<uint8_t>& dst,
29 size_t block) {
30 if (sheet.size() == kSheetBytes && (block + 1) * kSheetBytes <= dst.size()) {
31 std::copy(sheet.begin(), sheet.end(), dst.begin() + block * kSheetBytes);
32 }
33}
34
35} // namespace
36
37SheetPixels SnapshotSessionSheets(const std::set<uint16_t>& ids,
38 const zelda3::GameData* game_data) {
39 SheetPixels out;
40 if (game_data == nullptr) {
41 return out;
42 }
43 for (uint16_t id : ids) {
44 const auto pixels = game_data->sheet_store.Sheet(id);
45 if (!pixels.empty()) {
46 out[id] = std::vector<uint8_t>(pixels.begin(), pixels.end());
47 }
48 }
49 return out;
50}
51
52SheetPixels SnapshotArenaSheets(const std::set<uint16_t>& ids,
53 const zelda3::GameData* game_data) {
54 SheetPixels out;
55 auto& arena = gfx::Arena::Get();
56 if (game_data != nullptr && arena.gfx_sheets_owner() != nullptr &&
57 arena.gfx_sheets_owner() != game_data) {
58 return out;
59 }
60 const auto& sheets = arena.gfx_sheets();
61 for (const uint16_t id : ids) {
62 if (id >= sheets.size()) {
63 continue;
64 }
65 const auto& sheet = sheets[id];
66 if (sheet.width() == 128 && sheet.height() == 32 &&
67 sheet.vector().size() == kSheetBytes) {
68 out[id] = sheet.vector();
69 }
70 }
71 return out;
72}
73
74std::vector<uint8_t> PreviewImage::ToRgba() const {
75 if (empty())
76 return {};
77 std::vector<uint8_t> rgba(static_cast<size_t>(width) * height * 4, 0);
78 if (format == Format::kRgba) {
79 std::copy_n(pixels.begin(), std::min(pixels.size(), rgba.size()),
80 rgba.begin());
81 return rgba;
82 }
83 for (size_t i = 0; i < pixels.size() && i * 4 + 3 < rgba.size(); ++i) {
84 const uint8_t index = pixels[i];
85 if (index0_transparent && index == 0) {
86 continue;
87 }
88 const SDL_Color& c = colors[index];
89 rgba[i * 4 + 0] = c.r;
90 rgba[i * 4 + 1] = c.g;
91 rgba[i * 4 + 2] = c.b;
92 rgba[i * 4 + 3] = c.a;
93 }
94 return rgba;
95}
96
98 if (width != other.width || height != other.height)
99 return -1;
100 const auto a = ToRgba();
101 const auto b = other.ToRgba();
102 int count = 0;
103 for (size_t i = 0; i < a.size(); i += 4) {
104 count += !std::equal(a.begin() + i, a.begin() + i + 4, b.begin() + i);
105 }
106 return count;
107}
108
110 PreviewImage image;
111 if (!bitmap.is_active() || bitmap.width() <= 0 || bitmap.height() <= 0) {
112 return image;
113 }
114 image.width = bitmap.width();
115 image.height = bitmap.height();
116 image.pixels = bitmap.vector();
119 image.pixels.resize(static_cast<size_t>(image.width) * image.height *
120 (bitmap.IsIndexed() ? 1 : 4),
121 0);
122 if (SDL_Palette* palette = platform::GetSurfacePalette(bitmap.surface())) {
123 for (int i = 0; i < palette->ncolors && i < 256; ++i) {
124 image.colors[i] = palette->colors[i];
125 }
126 }
127 Uint32 color_key;
128 if (bitmap.IsIndexed() &&
129 SDL_GetColorKey(bitmap.surface(), &color_key) == 0 &&
130 color_key < image.colors.size()) {
131 image.colors[color_key].a = 0;
132 }
133 return image;
134}
135
136// ---------------------------------------------------------------------------
137// Room context
138// ---------------------------------------------------------------------------
139
140std::map<int, std::array<uint8_t, 16>> CollectRoomSheetBlocks(
141 Rom* rom, zelda3::GameData* game_data,
142 const std::map<int, zelda3::RoomGfxInfo>& rooms) {
143 std::map<int, std::array<uint8_t, 16>> out;
144 if (rom == nullptr || game_data == nullptr) {
145 return out;
146 }
147 for (const auto& [room_id, info] : rooms) {
148 zelda3::Room room(room_id, rom, game_data);
149 room.SetBlockset(info.blockset);
150 room.SetSpriteset(info.spriteset);
151 room.LoadRoomGraphics();
152 std::array<uint8_t, 16> blocks{};
153 const auto& room_blocks = room.blocks();
154 std::copy_n(room_blocks.begin(), 16, blocks.begin());
155 out[room_id] = blocks;
156 }
157 return out;
158}
159
160std::vector<int> RoomsUsingSheet(
161 const std::map<int, std::array<uint8_t, 16>>& room_blocks, uint16_t sheet,
162 bool include_sprites) {
163 std::vector<int> rooms;
164 for (const auto& [room_id, blocks] : room_blocks) {
165 const int end = include_sprites ? 16 : 8;
166 if (std::find(blocks.begin(), blocks.begin() + end, sheet) !=
167 blocks.begin() + end) {
168 rooms.push_back(room_id);
169 }
170 }
171 return rooms;
172}
173
174std::string RoomContextLabel(const LiveRoomContext* live) {
175 if (!live)
176 return "ROM · Default entrance";
177 if (live->entrance_slot < 0)
178 return "Live · Default entrance";
179 return absl::StrFormat("Live · Entrance $%02X", live->entrance_slot);
180}
181
182std::vector<SourceObjectUse> FindSourceObjectUses(
183 const std::vector<RoomTileReference>& references,
184 const std::vector<GraphicsEditTarget>& targets,
185 const zelda3::GameData* owner) {
186 std::map<size_t, SourceObjectUse> matches;
187 for (const auto& reference : references) {
188 bool pixels = false;
189 bool palette = false;
190 for (const auto& target : targets) {
191 pixels |= target.sheet_id == reference.sheet &&
192 target.tile_index == (reference.tile_word & 0x3F);
193 if (!owner || !target.writable_palette ||
194 target.writable_palette->owner != owner)
195 continue;
196 for (size_t raw = 1; raw < reference.palette_colors.size(); ++raw) {
197 if (!reference.palette_colors[raw])
198 continue;
199 for (size_t source = 1; source < 8; ++source) {
200 palette |= target.writable_palette->colors[source] &&
201 reference.palette_colors[raw] ==
202 target.writable_palette->colors[source];
203 }
204 }
205 }
206 if (!pixels && !palette)
207 continue;
208 auto& match = matches[reference.object_index];
209 match.object_index = reference.object_index;
210 match.object_id = reference.object_id;
211 match.shares_pixels |= pixels;
212 match.shares_palette |= palette;
213 match.references.push_back(reference);
214 }
215 std::vector<SourceObjectUse> out;
216 for (auto& [index, match] : matches)
217 out.push_back(std::move(match));
218 return out;
219}
220
222 zelda3::GameData* game_data,
223 zelda3::Room* room) {
224 if (!rom || !rom->is_loaded() || !game_data || !room || room->rom() != rom ||
225 room->game_data() != game_data || !room->IsLoaded()) {
226 return absl::FailedPreconditionError("Room usage session mismatch");
227 }
228 room->PrepareForRender();
229 CaptureUsage(*room);
230 return absl::OkStatus();
231}
232
233std::vector<LoadedRoomSourceUses> FindLoadedRoomSourceUses(
234 Rom* rom, zelda3::GameData* game_data, const LiveRoomContexts& rooms,
235 const std::vector<GraphicsEditTarget>& targets) {
236 std::vector<LoadedRoomSourceUses> out;
237 RoomUsagePreview trace;
238 for (const auto& [id, context] : rooms) {
240 result.room_id = id;
241 const auto status =
242 context.room && context.room->id() == id
243 ? trace.TraceLoadedRoom(rom, game_data, context.room)
244 : absl::FailedPreconditionError("Room ID mismatch");
245 if (!status.ok()) {
246 result.error = std::string(status.message());
247 } else {
248 result.objects =
249 FindSourceObjectUses(trace.tile_references(), targets, game_data);
250 result.unknown_objects = trace.unknown_objects();
251 }
252 out.push_back(std::move(result));
253 }
254 return out;
255}
256
257absl::Status RoomUsagePreview::Render(Rom* rom, zelda3::GameData* game_data,
258 int room_id, const SheetPixels& sheets,
259 bool objects_only,
260 zelda3::Room* live_room) {
261 room_id_ = -1;
262 if (!rom || !rom->is_loaded() || !game_data) {
263 return absl::FailedPreconditionError("ROM not loaded");
264 }
265 if (room_id < 0 || room_id >= zelda3::kNumberOfRooms) {
266 return absl::InvalidArgumentError("Invalid room ID");
267 }
268 if (live_room &&
269 (live_room->rom() != rom || live_room->game_data() != game_data ||
270 live_room->id() != room_id || !live_room->IsLoaded())) {
271 return absl::FailedPreconditionError("Room usage session mismatch");
272 }
273 zelda3::Room* rendered = live_room;
274 if (!rendered) {
275 if (!room_ || room_->id() != room_id || room_->rom() != rom ||
276 room_->game_data() != game_data) {
277 room_ =
278 std::make_unique<zelda3::Room>(zelda3::LoadRoomFromRom(rom, room_id));
279 room_->SetGameData(game_data);
280 }
281 room_->SetGraphicsSheetOverrides(sheets);
282 rendered = room_.get();
283 } else {
284 // The session store includes a live stroke before its revision commits.
285 // Do not install overrides or copy the live room into a second model.
286 rendered->MarkGraphicsDirty();
287 }
288 rendered->PrepareForRender();
291 layers.ApplyLayerMerging(rendered->layer_merging());
292 layers.ApplyRoomEffect(rendered->effect());
294 if (objects_only) {
297 }
298 rendered->RenderComposite(layers, composite_);
299 if (!composite_.is_active() || composite_.width() <= 0) {
300 return absl::InternalError("Room composite is empty after render");
301 }
302 CaptureUsage(*rendered);
303 return absl::OkStatus();
304}
305
306std::array<uint8_t, 16> RoomUsagePreview::blocks() const {
307 return blocks_;
308}
309
311 std::copy_n(room.blocks().begin(), 16, blocks_.begin());
312 tile_references_.clear();
313 object_rects_.clear();
314 const auto maps = zelda3::ComposeYazeRoomTilemaps(room);
315 const auto& objects = room.GetTileObjects();
316 const auto owners =
317 zelda3::ComputeObjectTileOwners(room.rom(), room.id(), objects, maps);
318 std::set<size_t> attributed;
319 std::set<std::pair<size_t, uint16_t>> seen;
320 for (int layer = 0; layer < 2; ++layer) {
321 const auto& words = layer == 0 ? maps.bg1 : maps.bg2;
322 const auto& indices = layer == 0 ? owners.bg1 : owners.bg2;
323 for (size_t i = 0; i < indices.size(); ++i) {
324 const int index = indices[i];
325 if (index < 0 || index >= static_cast<int>(objects.size()))
326 continue;
327 const uint16_t word = words[i];
328 const int tile = word & 0x3FF;
329 // Animated/composite slots have no single raw sheet attribution here.
330 if (tile >= 512 || (tile >= 0x1B0 && tile < 0x1D0))
331 continue;
332 attributed.insert(index);
333 if (!seen.emplace(index, word).second)
334 continue;
335 const auto& object = objects[index];
336 tile_references_.push_back(
337 {static_cast<size_t>(index), static_cast<uint16_t>(object.id_), word,
338 blocks_[tile / kTilesPerSheet], tile / kTilesPerSheet});
339 auto& reference = tile_references_.back();
340 const int half = room.BackgroundPaletteOffset(reference.slot);
341 const int row = (word >> 10) & 7;
342 for (int raw = 1; raw < 8; ++raw) {
343 reference.palette_colors[raw] =
344 room.rendered_palette_addresses()[row * 16 + half + raw];
345 }
346 const auto [x, y, w, h] =
348 object_rects_[index] = {x, y, w, h};
349 }
350 }
351 // No attributable final tile may mean occlusion or untraced/custom drawing.
352 // It never establishes that an object does not use a sheet.
353 unknown_objects_ = objects.size() - attributed.size();
354}
355
357 uint16_t sheet) const {
358 std::vector<SDL_Rect> rects;
359 std::set<size_t> seen;
360 for (const auto& use : tile_references_) {
361 if (use.sheet == sheet && seen.insert(use.object_index).second)
362 rects.push_back(object_rects_.at(use.object_index));
363 }
364 return rects;
365}
366
368 size_t index) const {
369 const auto found = object_rects_.find(index);
370 return found == object_rects_.end() ? std::vector<SDL_Rect>{}
371 : std::vector<SDL_Rect>{found->second};
372}
373
375 room_.reset();
376 room_id_ = -1;
377 blocks_ = {};
378 tile_references_.clear();
379 object_rects_.clear();
381}
382
383// ---------------------------------------------------------------------------
384// Sprite context
385// ---------------------------------------------------------------------------
386
388 zelda3::GameData* game_data,
389 int spriteset, int palette_room,
390 std::string hack_name) {
391 if (rom == nullptr || !rom->is_loaded() || game_data == nullptr) {
392 return absl::FailedPreconditionError("ROM not loaded");
393 }
394 if (spriteset < 0 ||
395 spriteset >= static_cast<int>(game_data->spriteset_ids.size())) {
396 return absl::InvalidArgumentError(
397 absl::StrFormat("Invalid spriteset 0x%02X", spriteset));
398 }
399 rom_ = rom;
400 game_data_ = game_data;
401 hack_name_ = std::move(hack_name);
402 slots_ = zelda3::SpriteSheetSlots(game_data->spriteset_ids[spriteset],
404
405 colors_ = {};
406 if (palette_room >= 0) {
407 const auto room = zelda3::LoadRoomHeaderFromRom(rom, palette_room);
409 } else {
410 const auto& groups = game_data->palette_groups;
411 const auto palettes = internal::DefaultSpritePreviewPalettes(
412 groups.global_sprites, groups.sprites_aux1, groups.sprites_aux2,
413 groups.sprites_aux3);
414 for (size_t row = 0; row < palettes.size() && row < 8; ++row) {
415 const auto& palette = palettes.palette_ref(row);
416 for (size_t i = 0; i < palette.size() && i < 16; ++i) {
417 const auto rgb = palette[i].rgb();
418 colors_[(8 + row) * 16 + i] =
419 SDL_Color{static_cast<uint8_t>(rgb.x), static_cast<uint8_t>(rgb.y),
420 static_cast<uint8_t>(rgb.z), 255};
421 }
422 }
423 }
424 return absl::OkStatus();
425}
426
428 zsm_.reset();
429 sprite_id_ = sprite_id;
430 frame_ = 0;
431 remainder_ = 0.0f;
432}
433
434void SpriteUsagePreview::SetZsm(const zsprite::ZSprite& zsm, int animation) {
435 zsm_ = zsm;
436 animation_ = std::clamp<int>(
437 animation, 0,
438 std::max<int>(0, static_cast<int>(zsm.animations.size()) - 1));
439 frame_ = zsm.animations.empty() ? 0 : zsm.animations[animation_].frame_start;
440 remainder_ = 0.0f;
441}
442
444 if (!zsm_.has_value()) {
445 return 1;
446 }
447 return static_cast<int>(zsm_->editor.Frames.size());
448}
449
451 frame_ = std::clamp(frame, 0, std::max(0, frame_count() - 1));
452 remainder_ = 0.0f;
453}
454
455bool SpriteUsagePreview::Advance(float seconds) {
456 if (!zsm_.has_value() || zsm_->animations.empty()) {
457 return false;
458 }
459 return sprite_authoring::Advance(zsm_->animations[animation_],
460 zsm_->editor.Frames.size(), seconds, frame_,
461 remainder_);
462}
463
464bool SpriteUsagePreview::UsesSheet(uint16_t sheet) const {
465 return std::find(slots_.begin(), slots_.end(), sheet) != slots_.end();
466}
467
468std::vector<uint8_t> SpriteUsagePreview::SheetOrRom(const SheetPixels& sheets,
469 uint16_t sheet) const {
470 if (const auto it = sheets.find(sheet); it != sheets.end()) {
471 return it->second;
472 }
473 if (game_data_ != nullptr) {
474 const auto& buffer = game_data_->graphics_buffer;
475 const size_t offset = static_cast<size_t>(sheet) * kSheetBytes;
476 if (offset + kSheetBytes <= buffer.size()) {
477 return {buffer.begin() + offset, buffer.begin() + offset + kSheetBytes};
478 }
479 }
480 return {};
481}
482
484 PreviewImage image;
485 image.colors = colors_;
486 image.index0_transparent = true;
487 if (game_data_ == nullptr) {
488 return image;
489 }
490
491 if (zsm_.has_value()) {
492 // SpriteDrawer reads OAM tiles 0x000-0x1FF from eight sheets in a row,
493 // the same buffer SpriteEditor::LoadSpriteGraphicsBuffer builds.
494 std::vector<uint8_t> buffer(0x10000, 0);
495 for (size_t slot = 0; slot < slots_.size(); ++slot) {
496 CopySheet(SheetOrRom(sheets, slots_[slot]), buffer, slot);
497 }
498 constexpr int kCanvas = 128;
499 gfx::Bitmap canvas(kCanvas, kCanvas, 8,
500 std::vector<uint8_t>(kCanvas * kCanvas, 0));
501 SpriteDrawer drawer(buffer.data());
502 if (frame_ >= 0 && frame_ < frame_count()) {
503 drawer.DrawFrame(canvas, zsm_->editor.Frames[frame_], kCanvas / 2,
504 kCanvas / 2);
505 }
506 image.width = kCanvas;
507 image.height = kCanvas;
508 image.pixels = canvas.vector();
509 // SpriteDrawer writes OAM palette rows 0-7; CGRAM holds them at 8-15.
510 for (auto& pixel : image.pixels) {
511 if (pixel != 0) {
512 pixel = static_cast<uint8_t>(pixel | 0x80);
513 }
514 }
515 return image;
516 }
517
518 // Sprite::RenderPreviewGraphics reads a room-style buffer: sprite sheets
519 // in blocks 8-15 (8x8 tile 512 onward), the order Room::LoadRoomGraphics
520 // and SpriteSheetSlots share.
521 std::vector<uint8_t> buffer(0x10000, 0);
522 for (size_t slot = 0; slot < slots_.size(); ++slot) {
523 CopySheet(SheetOrRom(sheets, slots_[slot]), buffer, 8 + slot);
524 }
525 zelda3::Sprite sprite(sprite_id_, 0, 0, 0, 0);
526 const auto* layout =
528 sprite.RenderPreviewGraphics(buffer, layout);
529 const SDL_Rect bounds = sprite.preview_bounds();
530 const auto* pixels = sprite.preview_graphics();
531 if (pixels == nullptr || bounds.w <= 0 || bounds.h <= 0 ||
532 pixels->size() < static_cast<size_t>(bounds.w * bounds.h)) {
533 return image;
534 }
535 image.width = bounds.w;
536 image.height = bounds.h;
537 image.pixels.assign(pixels->begin(), pixels->begin() + bounds.w * bounds.h);
538 return image;
539}
540
541// ---------------------------------------------------------------------------
542// Tile16 context
543// ---------------------------------------------------------------------------
544
546 zelda3::GameData* game_data,
547 int area) {
548 if (rom == nullptr || !rom->is_loaded() || game_data == nullptr) {
549 return absl::FailedPreconditionError("ROM not loaded");
550 }
551 if (area < 0 || area >= zelda3::kNumOverworldAreas) {
552 return absl::InvalidArgumentError(
553 absl::StrFormat("Invalid overworld area 0x%02X", area));
554 }
555 if (tiles16_.empty() || rom_ != rom) {
556 auto tiles16 = zelda3::ReadMap16Tiles(*rom);
557 if (!tiles16.ok()) {
558 return tiles16.status();
559 }
560 tiles16_ = std::move(*tiles16);
561 }
562 rom_ = rom;
563 game_data_ = game_data;
564 if (!map_ || area_ != area) {
565 map_ = std::make_unique<zelda3::OverworldMap>(area, rom, game_data);
566 map_->set_game_state(0);
567 map_->LoadAreaGraphics();
568 if (auto status = map_->LoadPalette(); !status.ok()) {
569 map_.reset();
570 return status;
571 }
572 area_ = area;
573 }
574 return absl::OkStatus();
575}
576
577std::vector<int> Tile16UsagePreview::TileIdsFor(uint16_t sheet,
578 int tile_index) const {
579 std::vector<int> ids;
580 if (!map_ || tile_index < 0 || tile_index >= kTilesPerSheet) {
581 return ids;
582 }
583 // BuildTileset fills slots 0-7 (0x1000 bytes, 64 tiles each) from
584 // static_graphics; tile16s address that buffer with 10-bit tile ids.
585 for (int slot = 0; slot < 8; ++slot) {
586 if (map_->static_graphics(slot) == sheet) {
587 ids.push_back(slot * kTilesPerSheet + tile_index);
588 }
589 }
590 return ids;
591}
592
593std::vector<int> Tile16UsagePreview::Tile16sUsingTile(uint16_t sheet,
594 int tile_index) const {
595 std::vector<int> result;
596 const auto ids = TileIdsFor(sheet, tile_index);
597 if (ids.empty()) {
598 return result;
599 }
600 for (size_t i = 0; i < tiles16_.size(); ++i) {
601 const auto& info = tiles16_[i].tiles_info;
602 if (std::any_of(info.begin(), info.end(), [&](const gfx::TileInfo& t) {
603 return std::find(ids.begin(), ids.end(), t.id_) != ids.end();
604 })) {
605 result.push_back(static_cast<int>(i));
606 }
607 }
608 return result;
609}
610
612 const std::vector<int>& tile16s,
613 uint16_t sheet, int tile_index,
614 std::vector<SDL_Rect>* highlights) {
615 PreviewImage image;
616 image.index0_transparent = false;
617 if (!map_ || tile16s.empty()) {
618 return image;
619 }
620 map_->SetGraphicsSheetOverrides(sheets);
621 if (!map_->BuildTileset().ok() ||
622 !map_->BuildTiles16Gfx(tiles16_, static_cast<int>(tiles16_.size()))
623 .ok()) {
624 return image;
625 }
626 const auto& blockset = map_->current_tile16_blockset();
627 const auto& palette = map_->current_palette();
628 for (size_t i = 0; i < palette.size() && i < 256; ++i) {
629 const auto rgb = palette[i].rgb();
630 image.colors[i] =
631 SDL_Color{static_cast<uint8_t>(rgb.x), static_cast<uint8_t>(rgb.y),
632 static_cast<uint8_t>(rgb.z), 255};
633 }
634
635 constexpr int kPerRow = 8;
636 const int rows = (static_cast<int>(tile16s.size()) + kPerRow - 1) / kPerRow;
637 image.width = kPerRow * 16;
638 image.height = rows * 16;
639 image.pixels.assign(static_cast<size_t>(image.width) * image.height, 0);
640 const auto ids = TileIdsFor(sheet, tile_index);
641 constexpr int kQuadrantX[] = {0, 8, 0, 8};
642 constexpr int kQuadrantY[] = {0, 0, 8, 8};
643 for (size_t n = 0; n < tile16s.size(); ++n) {
644 const int tile16 = tile16s[n];
645 // BuildTiles16Gfx lays tile16s out 8 per 128-pixel row.
646 const int src_x = (tile16 % kPerRow) * 16;
647 const int src_y = (tile16 / kPerRow) * 16;
648 const int dst_x = static_cast<int>(n % kPerRow) * 16;
649 const int dst_y = static_cast<int>(n / kPerRow) * 16;
650 for (int y = 0; y < 16; ++y) {
651 const size_t src = static_cast<size_t>(src_y + y) * 128 + src_x;
652 if (src + 16 > blockset.size()) {
653 break;
654 }
655 std::copy_n(blockset.begin() + src, 16,
656 image.pixels.begin() + (dst_y + y) * image.width + dst_x);
657 }
658 if (highlights != nullptr && tile16 >= 0 &&
659 tile16 < static_cast<int>(tiles16_.size())) {
660 for (int q = 0; q < 4; ++q) {
661 const int id = tiles16_[tile16].tiles_info[q].id_;
662 if (std::find(ids.begin(), ids.end(), id) != ids.end()) {
663 highlights->push_back(
664 {dst_x + kQuadrantX[q], dst_y + kQuadrantY[q], 8, 8});
665 }
666 }
667 }
668 }
669 return image;
670}
671
672} // namespace yaze::editor::usage_preview
The Rom class is used to load, save, and modify Rom data. This is a generic SNES ROM container and do...
Definition rom.h:49
bool is_loaded() const
Definition rom.h:187
Draws sprite OAM tiles to bitmaps for preview rendering.
void DrawFrame(gfx::Bitmap &bitmap, const zsprite::Frame &frame, int origin_x, int origin_y)
Draw all tiles in a ZSM frame.
std::map< size_t, SDL_Rect > object_rects_
std::vector< RoomTileReference > tile_references_
absl::Status TraceLoadedRoom(Rom *rom, zelda3::GameData *game_data, zelda3::Room *room)
std::unique_ptr< zelda3::Room > room_
absl::Status Render(Rom *rom, zelda3::GameData *game_data, int room_id, const SheetPixels &sheets, bool objects_only, zelda3::Room *live_room=nullptr)
void CaptureUsage(const zelda3::Room &room)
std::array< uint8_t, 16 > blocks() const
Room graphics blocks (0-7 background, 8-15 sprites) of the loaded room.
std::vector< SDL_Rect > ObjectRectsUsingSheet(uint16_t sheet) const
const std::vector< RoomTileReference > & tile_references() const
void Reset()
Clears the loaded room so the next Render() reloads it from the ROM.
std::vector< SDL_Rect > ObjectRectsForObject(size_t index) const
absl::Status Configure(Rom *rom, zelda3::GameData *game_data, int spriteset, int palette_room, std::string hack_name={})
std::optional< zsprite::ZSprite > zsm_
void SetVanillaSprite(uint8_t sprite_id)
Plays a vanilla sprite (single pose from the sprite catalog).
PreviewImage RenderFrame(const SheetPixels &sheets) const
Draws the current frame with sheets (edited pixels) over the ROM's.
std::vector< uint8_t > SheetOrRom(const SheetPixels &sheets, uint16_t sheet) const
void SetZsm(const zsprite::ZSprite &zsm, int animation)
Plays animation animation of a .zsm (the preview keeps a copy).
int frame_count() const
Frames in the current animation (1 for vanilla sprites).
PreviewImage Render(const SheetPixels &sheets, const std::vector< int > &tile16s, uint16_t sheet, int tile_index, std::vector< SDL_Rect > *highlights=nullptr)
std::vector< int > TileIdsFor(uint16_t sheet, int tile_index) const
absl::Status Configure(Rom *rom, zelda3::GameData *game_data, int area)
Loads overworld area area (graphics and palette) and the tile16 table.
std::unique_ptr< zelda3::OverworldMap > map_
std::vector< int > Tile16sUsingTile(uint16_t sheet, int tile_index) const
static Arena & Get()
Definition arena.cc:24
Represents a bitmap image optimized for SNES ROM hacking.
Definition bitmap.h:70
const std::vector< uint8_t > & vector() const
Definition bitmap.h:411
bool IsIndexed() const
Definition bitmap.h:401
bool is_active() const
Definition bitmap.h:414
int height() const
Definition bitmap.h:399
int width() const
Definition bitmap.h:398
SDL_Surface * surface() const
Definition bitmap.h:409
SNES 16-bit tile metadata container.
Definition snes_tile.h:52
static DimensionService & Get()
std::tuple< int, int, int, int > GetSelectionBoundsPixels(const RoomObject &obj) const
absl::Span< const uint8_t > Sheet(uint16_t sheet) const
The sheet's 4096 pixels, or an empty span when it is not held.
RoomLayerManager - Manages layer visibility and compositing.
void ApplyGameLayerRegisters(const RoomLayerRegisters &registers)
void ApplyLayerMerging(const LayerMergeType &merge_type)
void SetLayerVisible(LayerType layer, bool visible)
void ApplyRoomEffect(EffectKey effect)
void RenderComposite(const RoomLayerManager &layer_mgr, gfx::Bitmap &output)
Definition room.cc:1248
RoomLayerRegisters GameLayerRegisters(uint16_t room_flags=0) const
Definition room.cc:943
auto game_data()
Definition room.h:1086
void MarkGraphicsDirty()
Definition room.h:512
bool IsLoaded() const
Definition room.h:970
auto rom() const
Definition room.h:1082
void LoadRoomGraphics(std::optional< uint8_t > entrance_blockset=std::nullopt)
Definition room.cc:953
void PrepareForRender(std::optional< uint8_t > entrance_blockset=std::nullopt)
Definition room.cc:1058
const std::vector< RoomObject > & GetTileObjects() const
Definition room.h:445
const LayerMergeType & layer_merging() const
Definition room.h:990
auto blocks() const
Definition room.h:1080
int BackgroundPaletteOffset(int slot) const
Definition room.cc:1085
const PaletteAddressMap & rendered_palette_addresses() const
Definition room.h:1135
EffectKey effect() const
Definition room.h:986
void SetSpriteset(uint8_t ss)
Definition room.h:781
void SetBlockset(uint8_t bs)
Definition room.h:773
int id() const
Definition room.h:1012
static const SpriteOamLayout * GetPreviewOverride(uint8_t sprite_id, std::string_view hack_name)
A class for managing sprites in the overworld and underworld.
Definition sprite.h:39
void RenderPreviewGraphics(std::span< const uint8_t > graphics, const SpriteOamLayout *layout_override=nullptr, std::span< const uint8_t > graphics_resource={})
Definition sprite.cc:1194
auto preview_graphics() const
Definition sprite.h:103
SDL_Rect preview_bounds() const
Definition sprite.h:106
gfx::PaletteGroup DefaultSpritePreviewPalettes(const gfx::PaletteGroup &global, const gfx::PaletteGroup &aux1, const gfx::PaletteGroup &aux2, const gfx::PaletteGroup &aux3)
bool Advance(const zsprite::AnimationGroup &animation, size_t frame_count, float delta, int &frame, float &remainder)
void CopySheet(const std::vector< uint8_t > &sheet, std::vector< uint8_t > &dst, size_t block)
SheetPixels SnapshotSessionSheets(const std::set< uint16_t > &ids, const zelda3::GameData *game_data)
std::vector< LoadedRoomSourceUses > FindLoadedRoomSourceUses(Rom *rom, zelda3::GameData *game_data, const LiveRoomContexts &rooms, const std::vector< GraphicsEditTarget > &targets)
std::string RoomContextLabel(const LiveRoomContext *live)
SheetPixels SnapshotArenaSheets(const std::set< uint16_t > &ids, const zelda3::GameData *game_data)
std::map< uint16_t, std::vector< uint8_t > > SheetPixels
Sheet id -> 4096 8bpp pixels (the GameData::graphics_buffer layout).
constexpr size_t kSheetBytes
std::map< int, LiveRoomContext > LiveRoomContexts
PreviewImage SnapshotBitmap(const gfx::Bitmap &bitmap)
Copies a palettized Bitmap (for example a room composite).
std::vector< int > RoomsUsingSheet(const std::map< int, std::array< uint8_t, 16 > > &room_blocks, uint16_t sheet, bool include_sprites)
std::map< int, std::array< uint8_t, 16 > > CollectRoomSheetBlocks(Rom *rom, zelda3::GameData *game_data, const std::map< int, zelda3::RoomGfxInfo > &rooms)
std::vector< SourceObjectUse > FindSourceObjectUses(const std::vector< RoomTileReference > &references, const std::vector< GraphicsEditTarget > &targets, const zelda3::GameData *owner)
SDL_Palette * GetSurfacePalette(SDL_Surface *surface)
Get the palette attached to a surface.
Definition sdl_compat.h:398
RoomTilemaps ComposeYazeRoomTilemaps(const Room &room)
Room LoadRoomHeaderFromRom(Rom *rom, int room_id)
Definition room.cc:680
absl::StatusOr< std::vector< gfx::Tile16 > > ReadMap16Tiles(const Rom &rom, bool *expanded)
Represents the full Overworld data, light and dark world.
Definition overworld.cc:734
Room LoadRoomFromRom(Rom *rom, int room_id)
Definition room.cc:655
constexpr int kNumOverworldAreas
constexpr bool IsUnderworldSpriteset(int spriteset)
Dungeon rooms use spritesets 0x40-0x8F (room header value + 0x40).
TileOwners ComputeObjectTileOwners(Rom *rom, int room_id, const std::vector< RoomObject > &objects, const RoomTilemaps &yaze, const std::vector< bool > &hidden)
constexpr int kNumberOfRooms
std::array< uint8_t, 8 > SpriteSheetSlots(const std::array< uint8_t, 4 > &spriteset_values, bool underworld)
The eight sheets for a spriteset's four values.
std::array< SDL_Color, 256 > BuildDungeonSpriteRenderPalette(const Room &room, const GameData *game_data)
Definition room.cc:200
SDL2/SDL3 compatibility layer.
Which graphics sheets back sprite OAM tiles 0x000-0x1FF.
std::vector< uint8_t > ToRgba() const
RGBA8 rows; transparent pixels get alpha 0.
int CountDifferentPixels(const PreviewImage &other) const
std::vector< AnimationGroup > animations
Definition zsprite.h:445
std::array< std::array< uint8_t, 4 >, kNumSpritesets > spriteset_ids
Definition game_data.h:99
gfx::PaletteGroupMap palette_groups
Definition game_data.h:95
std::vector< uint8_t > & graphics_buffer
Legacy name for sheet_store.mutable_pixels().
Renders a graphics sheet the way the game uses it, from unsaved sheet pixels, without writing the ROM...