6#include "absl/strings/str_format.h"
12#include "imgui/imgui.h"
26uint64_t
HashBytes(uint64_t hash,
const std::vector<uint8_t>& bytes) {
27 for (
const uint8_t b : bytes) {
83 uint64_t hash = kFnvOffset;
84 auto groups = [&](
const auto& table) {
85 for (
const auto& row : table)
86 for (
const auto id : row)
87 hash = (hash ^
id) * kFnvPrime;
108 std::map<int, uint64_t> revisions;
110 for (
auto it = contexts.begin(); it != contexts.end();) {
111 auto* room = it->second.room;
112 if (!room || room->rom() !=
rom_ || room->game_data() !=
game_data_ ||
113 !room->IsLoaded() || room->id() != it->first) {
114 it = contexts.erase(it);
117 std::copy_n(room->blocks().begin(), 16, blocks[it->first].begin());
118 revisions[it->first] = room->composite_source_revision();
119 if (it->second.current)
140 const int preferred =
144 const auto selected =
159 uint64_t hash = kFnvOffset;
167 for (
const auto sheet : found->second) {
168 hash = (hash ^ sheet) * kFnvPrime;
177 ids.insert(target.sheet_id);
180 ids.insert(slot.sheet_id);
184 for (
const auto& [
id, pixels] : sheets) {
185 hash = (hash ^ id) * kFnvPrime;
186 hash = HashBytes(hash, pixels);
200 const int tile = (selection.y / 8) * 16 + selection.
x / 8;
201 return std::clamp(tile, 0, 63);
213 const int depth = indexed ? 8 : 32;
214 std::vector<SDL_Color> colors(image.
colors.begin(), image.
colors.end());
227 QueueTexture(bitmap);
231 const std::vector<SDL_Rect>& rects,
232 float max_scale,
bool fit_height) {
234 ImGui::TextDisabled(
"%s", tr(
"Rendering..."));
237 const auto available = ImGui::GetContentRegionAvail();
239 std::min(max_scale, std::max(1.0f, available.x) / bitmap.
width());
241 scale = std::min(scale, std::max(1.0f, available.y) / bitmap.
height());
243 scale = std::max(0.5f, scale);
245 const ImVec2 size(bitmap.
width() * scale, bitmap.
height() * scale);
246 const ImVec2 origin = ImGui::GetCursorScreenPos();
247 ImGui::Image((ImTextureID)(intptr_t)bitmap.
texture(), size);
248 auto* draw_list = ImGui::GetWindowDrawList();
250 for (
const auto& r : rects) {
252 ImVec2(origin.x + r.x * scale, origin.y + r.y * scale),
253 ImVec2(origin.x + (r.x + r.w) * scale, origin.y + (r.y + r.h) * scale),
254 color, 0.0f, 0, 1.5f);
262 ImGui::TextDisabled(
"%s", tr(
"No source preview available."));
267 ImGui::TextUnformatted(tr(
"Source preview"));
269 ImGui::Checkbox(tr(
"Live"), &
live_);
277 HOVER_HINT(
"Render unsaved source pixels now");
279 if (!source.palette_error.empty()) {
283 ImGui::TextWrapped(
"%s", source.palette_error.c_str());
295 const uint64_t hash =
301 const double now = ImGui::GetTime();
303 !ImGui::IsMouseDown(ImGuiMouseButton_Left) ||
305 const auto rendered = source.render_preview();
307 : std::string(rendered.status().message());
309 if (rendered->empty())
327 uint64_t hash = kFnvOffset;
328 auto add = [&](uint64_t value) {
329 hash = (hash ^ value) * kFnvPrime;
337 for (
const auto& [
id, context] : rooms) {
339 add(
reinterpret_cast<uintptr_t
>(context.room));
340 add(context.entrance_slot);
341 if (context.room && context.room->rom() ==
rom_ &&
343 add(context.room->composite_source_revision());
344 for (
const auto sheet : context.room->blocks()) {
361 return absl::FailedPreconditionError(
362 "Reopen Edit Graphics from a live source to find shared uses.");
371 return absl::FailedPreconditionError(
372 "Graphics source changed; reopen Edit Graphics from the source.");
375 auto validated = source;
380 shared_targets_.push_back(validated.targets.at(source.selected_target));
386 return absl::OkStatus();
390 const bool expanded = ImGui::CollapsingHeader(
391 tr(
"Shared uses in loaded rooms"), ImGuiTreeNodeFlags_NoAutoOpenOnLog);
402 tr(
"Source or loaded rooms changed. Find shared uses again.");
404 if (ImGui::RadioButton(tr(
"Current tile##SharedScope"),
411 if (ImGui::RadioButton(tr(
"Source pattern##SharedScope"),
418 if (ImGui::Button(tr(
"Find shared uses"))) {
421 status.ok() ? std::string() : std::string(status.message());
425 "%s", tr(
"Loaded dungeon rooms only. Doors, layout, sprites, unloaded "
426 "rooms and untraced drawing are not covered. Matches are "
427 "possible uses, not a prediction of visible changes."));
432 size_t matches = 0, unknown = 0, failed = 0;
434 matches += room.objects.size();
435 unknown += room.unknown_objects;
436 failed += !room.error.empty();
438 ImGui::Text(tr(
"%zu objects / %zu loaded rooms"), matches,
440 ImGui::Text(tr(
"%zu unknown objects; %zu rooms unavailable"), unknown,
442 ImGui::TextWrapped(
"%s", tr(
"Pixels = same sheet and tile. Palette = shared "
443 "source color addresses; unmapped colors are "
445 if (ImGui::TreeNodeEx(tr(
"Source tiles and palettes"),
446 ImGuiTreeNodeFlags_NoAutoOpenOnLog)) {
448 ImGui::Text(
"Sheet $%02X / tile $%02X", target.sheet_id,
450 if (target.writable_palette) {
451 const auto& mapping = *target.writable_palette;
452 ImGui::Text(
"Row %u, %s half", mapping.row,
453 mapping.half_offset == 8 ?
"right" :
"left");
454 for (
size_t i = 1; i < mapping.colors.size(); ++i) {
455 if (
const auto& address = mapping.colors[i])
456 ImGui::Text(
"%zu: %s / %d / %d", i,
458 address->palette_index, address->color_index);
461 ImGui::TextWrapped(
"%s", target.palette_address_limitation.c_str());
467 ImGui::TextWrapped(
"%s", tr(
"No traced matches in these loaded rooms. "
468 "This does not establish exclusive use."));
470 ImGui::BeginChild(
"##SharedUseResults", ImVec2(0, 8 * ImGui::GetFontSize()),
473 if (!room.error.empty())
474 ImGui::TextWrapped(
"$%03X: %s", room.room_id, room.error.c_str());
475 for (
const auto&
object : room.objects) {
476 const auto label = absl::StrFormat(
477 "$%03X / object %03X #%zu: %s%s%s", room.room_id,
object.object_id,
478 object.object_index,
object.shares_pixels ?
"Pixels" :
"",
479 object.shares_pixels &&
object.shares_palette ?
" + " :
"",
480 object.shares_palette ?
"Palette" :
"");
481 if (ImGui::Selectable(
485 const auto live = rooms.find(room.room_id);
488 ? absl::FailedPreconditionError(
"Room is no longer loaded")
490 {},
false, live->second.room);
492 status.ok() ? std::string() : std::string(status.message());
501 if (ImGui::IsItemHovered()) {
502 ImGui::BeginTooltip();
503 for (
const auto& reference :
object.references)
504 ImGui::Text(
"Sheet $%02X / tile $%02X / row %d / slot %d",
505 reference.sheet, reference.tile_word & 0x3F,
506 (reference.tile_word >> 10) & 7, reference.slot);
524 ImGui::TextDisabled(
"%s", tr(
"Load a ROM to preview sheet usage."));
529 const auto palette_revision =
538 const bool source_changed =
540 if (source_changed) {
554 if (std::find(slots.begin() + 4, slots.end(), sheet) != slots.end()) {
560 std::stable_partition(
564 room_gfx_.begin(), room_gfx_.end(), [ss](const auto& room) {
565 return room.second.spriteset + zelda3::kDungeonSpritesetBase ==
576 ImGui::Text(tr(
"Sheet 0x%02X"), sheet);
578 ImGui::Checkbox(tr(
"Live"), &live_);
580 "Re-render while you draw (at most 10 times a second, and when the "
581 "stroke ends). Unsaved edits only; the ROM is not written.");
585 last_render_time_ = -1.0;
586 source_dirty_ =
true;
587 source_render_time_ = -1.0;
593 const uint64_t hash =
594 live_ ? HashWatchedSheets(mode_ == Mode::kRoom)
596 if (hash != last_hash_) {
600 const double now = ImGui::GetTime();
601 const bool stroke_active = ImGui::IsMouseDown(ImGuiMouseButton_Left);
602 const bool render_now =
603 dirty_ && (last_render_time_ < 0.0 || !stroke_active ||
604 now - last_render_time_ >= kMinRenderInterval);
606 last_render_time_ = now;
610 if (ImGui::BeginTabBar(
"##UsagePreviewTabs")) {
611 if (state_->source_context && state_->source_context->render_preview &&
612 state_->source_context->owner == game_data_) {
613 if (ImGui::BeginTabItem(
614 tr(
"Source"),
nullptr,
615 source_changed ? ImGuiTabItemFlags_SetSelected : 0)) {
616 DrawSourcePreview(
false);
620 if (ImGui::BeginTabItem(
tr(
"Room"))) {
621 if (mode_ != Mode::kRoom) {
625 DrawRoomContext(render_now);
628 if (ImGui::BeginTabItem(
tr(
"Sprite"))) {
629 if (mode_ != Mode::kSprite) {
630 mode_ = Mode::kSprite;
633 DrawSpriteContext(render_now, ImGui::GetIO().DeltaTime);
636 if (ImGui::BeginTabItem(
tr(
"Tile16"))) {
637 if (mode_ != Mode::kTile16) {
638 mode_ = Mode::kTile16;
641 DrawTile16Context(render_now);
646 if (!status_.empty()) {
651void UsagePreviewView::DrawRoomContext(
bool render_now) {
652 if (rooms_for_sheet_.empty()) {
653 ImGui::TextDisabled(
"%s", tr(
"No dungeon room loads this sheet."));
656 room_choice_ = std::clamp(room_choice_, 0,
657 static_cast<int>(rooms_for_sheet_.size()) - 1);
658 const int room_id = rooms_for_sheet_[room_choice_];
659 ImGui::SetNextItemWidth(160.0f);
660 if (ImGui::BeginCombo(tr(
"Room"),
661 absl::StrFormat(
"0x%03X", room_id).c_str())) {
662 for (
int i = 0; i < static_cast<int>(rooms_for_sheet_.size()); ++i) {
663 const bool selected = i == room_choice_;
664 if (ImGui::Selectable(
665 absl::StrFormat(
"0x%03X", rooms_for_sheet_[i]).c_str(),
674 ImGui::TextDisabled(tr(
"%zu rooms"), rooms_for_sheet_.size());
675 if (ImGui::Checkbox(tr(
"Objects only"), &objects_only_)) {
678 HOVER_HINT(
"Hide the floor and layout layers; show object layers only.");
680 ImGui::Checkbox(tr(
"Outline objects using this sheet"), &outline_objects_);
682 if (render_now || room_preview_.room_id() != room_id) {
683 const auto live = live_rooms_.find(room_id);
684 auto* room = live == live_rooms_.end() ? nullptr : live->second.room;
685 const auto status = room_preview_.Render(rom_, game_data_, room_id,
686 Snapshot(), objects_only_, room);
688 live_room_revisions_[room_id] = room->composite_source_revision();
689 status_ = status.ok() ? std::string() : std::string(status.message());
691 object_rects_.clear();
695 room_preview_.ObjectRectsUsingSheet(state_->current_sheet_id);
696 QueueTexture(room_preview_.mutable_bitmap());
698 const auto live = live_rooms_.find(room_id);
699 const auto* context = live == live_rooms_.end() ? nullptr : &live->second;
700 const auto blocks = room_preview_.blocks();
701 const bool loads_sheet = std::find(blocks.begin(), blocks.end(),
702 state_->current_sheet_id) != blocks.end();
703 ImGui::TextDisabled(
"%s%s", usage_preview::RoomContextLabel(context).c_str(),
704 loads_sheet ?
"" :
" · Sheet not loaded");
705 if (ImGui::IsItemHovered()) {
706 ImGui::BeginTooltip();
707 ImGui::TextUnformatted(
708 context ? context->provenance.c_str()
709 :
"ROM room header / default entrance (not live room edits)");
710 ImGui::TextUnformatted(
711 "Session sheet pixels and graphics-group assignments");
712 ImGui::Text(
"%zu objects with traced references; %zu unknown/unattributed",
713 object_rects_.size(), room_preview_.unknown_objects());
714 ImGui::Text(
"BG sheets: %02X %02X %02X %02X %02X %02X %02X %02X", blocks[0],
715 blocks[1], blocks[2], blocks[3], blocks[4], blocks[5],
716 blocks[6], blocks[7]);
717 ImGui::TextUnformatted(
718 "Object traces only; doors, layout and sprites are unscanned.");
719 ImGui::TextUnformatted(
720 "Loaded sheets and traced references do not prove a visible pixel "
722 ImGui::TextUnformatted(
723 "Coverage can be partial. Unknown includes covered, custom or untraced "
726 for (
const auto& use : room_preview_.tile_references()) {
727 if (use.sheet != state_->current_sheet_id)
730 ImGui::TextDisabled(
"Additional traced references omitted here.");
733 ImGui::Text(
"Object %03X: slot %d, tile %03X, palette row %d, H:%d V:%d",
734 use.object_id, use.slot, use.tile_word & 0x3FF,
735 (use.tile_word >> 10) & 7, (use.tile_word >> 14) & 1,
736 (use.tile_word >> 15) & 1);
740 DrawBitmapFit(room_preview_.mutable_bitmap(),
741 outline_objects_ ? object_rects_ : std::vector<SDL_Rect>{},
745void UsagePreviewView::DrawSpriteContext(
bool render_now,
float delta) {
746 if (spritesets_for_sheet_.empty()) {
747 ImGui::TextDisabled(
"%s",
748 tr(
"No spriteset loads this sheet in slots 4-7."));
751 spriteset_choice_ = std::clamp(
752 spriteset_choice_, 0,
static_cast<int>(spritesets_for_sheet_.size()) - 1);
753 const int spriteset = spritesets_for_sheet_[spriteset_choice_];
754 ImGui::SetNextItemWidth(160.0f);
755 if (ImGui::BeginCombo(tr(
"Spriteset"),
756 absl::StrFormat(
"0x%02X", spriteset).c_str())) {
757 for (
int i = 0; i < static_cast<int>(spritesets_for_sheet_.size()); ++i) {
758 if (ImGui::Selectable(
759 absl::StrFormat(
"0x%02X", spritesets_for_sheet_[i]).c_str(),
760 i == spriteset_choice_)) {
761 spriteset_choice_ = i;
762 configured_spriteset_ = -1;
768 const bool spriteset_changed = configured_spriteset_ != spriteset;
769 if (spriteset_changed || sprite_palette_revision_ != palette_revision_) {
772 int palette_room = -1;
773 for (
const auto& [room_id, info] : room_gfx_) {
775 palette_room = room_id;
779 const auto status = sprite_preview_.Configure(rom_, game_data_, spriteset,
780 palette_room, hack_name_);
781 status_ = status.ok() ? std::string() : std::string(status.message());
782 if (spriteset_changed) {
783 if (zsm_.has_value()) {
784 sprite_preview_.SetZsm(*zsm_, zsm_animation_);
786 sprite_preview_.SetVanillaSprite(
787 static_cast<uint8_t
>(vanilla_sprite_id_));
790 sprite_palette_revision_ = palette_revision_;
791 configured_spriteset_ = spriteset;
795 ImGui::SetNextItemWidth(120.0f);
796 uint8_t sprite_id =
static_cast<uint8_t
>(vanilla_sprite_id_);
797 constexpr uint8_t kStep = 1;
798 if (ImGui::InputScalar(tr(
"Vanilla sprite"), ImGuiDataType_U8, &sprite_id,
799 &kStep,
nullptr,
"%02X",
800 ImGuiInputTextFlags_CharsHexadecimal)) {
801 vanilla_sprite_id_ = sprite_id;
803 zsm_loaded_path_.clear();
804 sprite_preview_.SetVanillaSprite(
static_cast<uint8_t
>(vanilla_sprite_id_));
809 static_cast<uint16_t
>(vanilla_sprite_id_)));
811 ImGui::SetNextItemWidth(-80.0f);
812 ImGui::InputTextWithHint(
"##ZsmPath", tr(
".zsm path"), zsm_path_,
815 if (ImGui::Button(tr(
"Load"))) {
817 const auto status = zsm.
Load(zsm_path_);
819 zsm_ = std::move(zsm);
820 zsm_loaded_path_ = zsm_path_;
822 sprite_preview_.SetZsm(*zsm_, zsm_animation_);
825 status_ = std::string(status.message());
829 if (zsm_.has_value() && !zsm_->animations.empty()) {
830 zsm_animation_ = std::clamp(zsm_animation_, 0,
831 static_cast<int>(zsm_->animations.size()) - 1);
832 ImGui::SetNextItemWidth(200.0f);
833 if (ImGui::BeginCombo(
835 zsm_->animations[zsm_animation_].frame_name.c_str())) {
836 for (
int i = 0; i < static_cast<int>(zsm_->animations.size()); ++i) {
837 if (ImGui::Selectable(zsm_->animations[i].frame_name.c_str(),
838 i == zsm_animation_)) {
840 sprite_preview_.SetZsm(*zsm_, zsm_animation_);
847 playing_ = !playing_;
852 sprite_preview_.SetFrame(sprite_preview_.frame() - 1);
858 sprite_preview_.SetFrame(sprite_preview_.frame() + 1);
862 ImGui::Text(tr(
"Frame %d / %d"), sprite_preview_.frame(),
863 sprite_preview_.frame_count());
867 if (playing_ && sprite_preview_.Advance(delta)) {
871 ImGui::PushTextWrapPos(0.0f);
873 "%s", tr(
"Vanilla sprites have one catalog pose; load a .zsm to "
874 "play its animations."));
875 ImGui::PopTextWrapPos();
878 if (render_now || !sprite_bitmap_.is_active()) {
879 ShowImage(sprite_bitmap_, sprite_preview_.RenderFrame(Snapshot()));
881 const auto& slots = sprite_preview_.slots();
883 tr(
"OAM slots: %02X %02X %02X %02X | %02X %02X %02X %02X"), slots[0],
884 slots[1], slots[2], slots[3], slots[4], slots[5], slots[6], slots[7]);
885 DrawBitmapFit(sprite_bitmap_, {}, 4.0f);
888void UsagePreviewView::DrawTile16Context(
bool render_now) {
889 const uint16_t sheet = state_->current_sheet_id;
890 if (!areas_.has_value()) {
892 "%s", tr(
"Reads all 160 overworld areas' graphics (about a second)."));
893 if (ImGui::Button(tr(
"Load overworld areas"))) {
895 last_sheet_ = 0xFFFF;
899 if (areas_for_sheet_.empty()) {
900 for (
const auto& [area, info] : *areas_) {
901 if (std::find(info.static_graphics.begin(),
902 info.static_graphics.begin() + 8,
903 sheet) != info.static_graphics.begin() + 8) {
904 areas_for_sheet_.push_back(area);
908 if (areas_for_sheet_.empty()) {
909 ImGui::TextDisabled(
"%s", tr(
"No overworld area loads this sheet."));
912 area_choice_ = std::clamp(area_choice_, 0,
913 static_cast<int>(areas_for_sheet_.size()) - 1);
914 const int area = areas_for_sheet_[area_choice_];
915 ImGui::SetNextItemWidth(160.0f);
916 if (ImGui::BeginCombo(tr(
"Area"), absl::StrFormat(
"0x%02X", area).c_str())) {
917 for (
int i = 0; i < static_cast<int>(areas_for_sheet_.size()); ++i) {
918 if (ImGui::Selectable(
919 absl::StrFormat(
"0x%02X", areas_for_sheet_[i]).c_str(),
920 i == area_choice_)) {
927 ImGui::Checkbox(tr(
"Follow selection"), &follow_selection_);
928 HOVER_HINT(
"Use the 8x8 tile at the pixel editor's selection origin.");
929 if (!follow_selection_ || !state_->selection.is_active) {
931 ImGui::SetNextItemWidth(100.0f);
932 if (ImGui::InputInt(tr(
"8x8 tile"), &tile_index_)) {
933 tile_index_ = std::clamp(tile_index_, 0, 63);
937 const int tile_index = SelectedTileIndex();
939 if (tile16_preview_.area() != area) {
940 const auto status = tile16_preview_.Configure(rom_, game_data_, area);
941 status_ = status.ok() ? std::string() : std::string(status.message());
944 const auto tile16s = tile16_preview_.Tile16sUsingTile(sheet, tile_index);
945 if (tile16s != tile16s_) {
949 ImGui::Text(tr(
"8x8 tile 0x%02X: %zu tile16s"), tile_index, tile16s_.size());
950 if (tile16s_.empty()) {
953 if (render_now || !tile16_bitmap_.is_active()) {
954 tile16_highlights_.clear();
955 ShowImage(tile16_bitmap_,
956 tile16_preview_.Render(Snapshot(), tile16s_, sheet, tile_index,
957 &tile16_highlights_));
959 DrawBitmapFit(tile16_bitmap_, tile16_highlights_, 4.0f);
The Rom class is used to load, save, and modify Rom data. This is a generic SNES ROM container and do...
std::optional< GraphicsEditContext > source_context
std::set< uint16_t > modified_sheets
uint16_t current_sheet_id
uint64_t source_context_revision
const GraphicsEditTarget * SourceTarget() const
gfx::Bitmap source_bitmap_
usage_preview::SheetPixels Snapshot() const
static constexpr double kMinRenderInterval
uint64_t palette_revision_
void DrawBitmapFit(gfx::Bitmap &bitmap, const std::vector< SDL_Rect > &rects, float max_scale, bool fit_height=false)
bool shared_whole_pattern_
usage_preview::RoomUsagePreview shared_room_preview_
GraphicsEditorState * state_
usage_preview::LiveRoomProvider live_rooms_provider_
uint64_t source_context_revision_
std::optional< std::map< int, zelda3::OverworldAreaGfxInfo > > areas_
std::vector< GraphicsEditTarget > shared_targets_
void ShowImage(gfx::Bitmap &bitmap, const usage_preview::PreviewImage &image)
zelda3::GameData * game_data_
absl::Status ScanSharedUses(const usage_preview::LiveRoomContexts &rooms)
void DrawSourcePreview(bool show_controls=true)
std::string shared_scan_status_
double source_render_time_
uint64_t HashWatchedSheets(bool include_room_context) const
uint64_t shared_scan_hash_
std::vector< int > spritesets_for_sheet_
uint64_t rendered_source_revision_
std::string source_status_
std::vector< int > rooms_for_sheet_
uint64_t SharedUsesHash(const usage_preview::LiveRoomContexts &rooms) const
std::map< int, uint64_t > live_room_revisions_
void RefreshRoomContexts()
uint64_t source_palette_revision_
int configured_spriteset_
std::map< int, zelda3::RoomGfxInfo > room_gfx_
std::map< int, std::array< uint8_t, 16 > > room_blocks_
int SelectedTileIndex() const
void SetGameData(zelda3::GameData *game_data)
size_t shared_preview_object_
usage_preview::RoomUsagePreview room_preview_
std::map< int, std::array< uint8_t, 16 > > fallback_room_blocks_
std::vector< int > areas_for_sheet_
std::vector< usage_preview::LoadedRoomSourceUses > shared_uses_
usage_preview::Tile16UsagePreview tile16_preview_
std::vector< SDL_Rect > object_rects_
usage_preview::LiveRoomContexts live_rooms_
absl::Status Render(Rom *rom, zelda3::GameData *game_data, int room_id, const SheetPixels &sheets, bool objects_only, zelda3::Room *live_room=nullptr)
gfx::Bitmap & mutable_bitmap()
void Reset()
Clears the loaded room so the next Render() reloads it from the ROM.
std::vector< SDL_Rect > ObjectRectsForObject(size_t index) const
void QueueTextureCommand(TextureCommandType type, Bitmap *bitmap)
Represents a bitmap image optimized for SNES ROM hacking.
void Create(int width, int height, int depth, std::span< uint8_t > data)
Create a bitmap with the given dimensions and data.
TextureHandle texture() const
BitmapMetadata & metadata()
void set_data(const std::vector< uint8_t > &data)
void SetPalette(const SnesPalette &palette)
Set the palette for the bitmap using SNES palette format.
uint64_t ColorRevision(const zelda3::GameData *game_data) const
static PaletteManager & Get()
Get the singleton instance.
static CustomObjectManager & Get()
uint64_t Revision(uint16_t sheet) const
#define ICON_MD_PLAY_ARROW
#define ICON_MD_SKIP_NEXT
#define ICON_MD_SKIP_PREVIOUS
#define HOVER_HINT(string)
constexpr uint64_t kFnvOffset
void QueueTexture(gfx::Bitmap &bitmap)
constexpr uint64_t kFnvPrime
uint64_t HashBytes(uint64_t hash, const std::vector< uint8_t > &bytes)
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::map< uint16_t, std::vector< uint8_t > > SheetPixels
Sheet id -> 4096 8bpp pixels (the GameData::graphics_buffer layout).
std::map< int, LiveRoomContext > LiveRoomContexts
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)
Editors are the view controllers for the application.
absl::Status UpdateGraphicsContextPalettes(GraphicsEditContext &context, GraphicsEditContext refreshed)
void AutoRegisterLastItem(const std::string &widget_type, const std::string &explicit_label, const std::string &description)
Automatically register the last ImGui item.
const char * tr(const char *source)
std::map< int, OverworldAreaGfxInfo > CollectOverworldAreaGfx(Rom &rom, GameData *game_data)
constexpr uint16_t kDungeonSpritesetBase
std::map< int, RoomGfxInfo > CollectRoomGfx(const Rom &rom)
const char * PaletteAddressGroupName(PaletteAddressGroup group)
constexpr bool IsUnderworldSpriteset(int spriteset)
Dungeon rooms use spritesets 0x40-0x8F (room header value + 0x40).
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.
const char * ResolveSpriteName(uint16_t id)
#define RETURN_IF_ERROR(expr)
Which graphics sheets back sprite OAM tiles 0x000-0x1FF.
zelda3::ResolvedGraphicsContext resolved
const zelda3::GameData * owner
std::array< SDL_Color, 256 > colors
std::vector< uint8_t > pixels
absl::Status Load(const std::string &filename)
Load a ZSM file from disk.
std::array< std::array< uint8_t, 4 >, kNumSpritesets > spriteset_ids
std::array< std::array< uint8_t, 4 >, kNumRoomBlocksets > room_blockset_ids
std::array< std::array< uint8_t, 8 >, kNumMainBlocksets > main_blockset_ids
GraphicsSheetStore sheet_store