3#include "absl/container/flat_hash_map.h"
13#include "imgui/imgui.h"
14#include "imgui_memory_editor.h"
21using ImGui::BeginChild;
22using ImGui::BeginTabBar;
23using ImGui::BeginTabItem;
24using ImGui::BeginTable;
27using ImGui::EndTabBar;
28using ImGui::EndTabItem;
29using ImGui::RadioButton;
31using ImGui::TableHeadersRow;
32using ImGui::TableNextColumn;
33using ImGui::TableNextRow;
34using ImGui::TableSetupColumn;
38 ImGuiTableFlags_Resizable | ImGuiTableFlags_Reorderable |
39 ImGuiTableFlags_Hideable | ImGuiTableFlags_BordersOuter |
40 ImGuiTableFlags_BordersV;
47 for (
int i = 0; i < 0x100 + 40; i++) {
53 if (room_size.room_size_pointer != 0x0A8000) {
60 auto palette_id =
rom()->
ReadWord(0xDEC4B + dungeon_palette_ptr);
61 if (palette_id.status() != absl::OkStatus()) {
64 int p_id = palette_id.value() / 180;
65 auto color = dungeon_man_pal_group[p_id][3];
71 for (
int i = 0; i < 0x07; ++i) {
75 for (
int i = 0; i < 0x85; ++i) {
86 return absl::OkStatus();
95 if (ImGui::BeginTabBar(
"##DungeonEditorTabBar")) {
96 if (ImGui::BeginTabItem(
"Room Editor")) {
100 if (ImGui::BeginTabItem(
"Usage Statistics")) {
109 return absl::OkStatus();
123 [
this, &sprites_aux1_pal_group](
int block) {
128 return absl::OkStatus();
132 std::map<int, std::vector<int>> rooms_by_bank;
134 int bank = room.second >> 16;
135 rooms_by_bank[bank].push_back(room.second);
139 for (
auto &bank_rooms : rooms_by_bank) {
141 std::ranges::sort(bank_rooms.second);
143 for (
size_t i = 0; i < bank_rooms.second.size(); ++i) {
144 int room_ptr = bank_rooms.second[i];
150 return entry.second == room_ptr;
153 if (room_ptr != 0x0A8000) {
154 if (i < bank_rooms.second.size() - 1) {
157 room_sizes_[room_id] = bank_rooms.second[i + 1] - room_ptr;
160 int bank_end_address = (bank_rooms.first << 16) | 0xFFFF;
161 room_sizes_[room_id] = bank_end_address - room_ptr + 1;
185 TableSetupColumn(
"Room Selector");
186 TableSetupColumn(
"Canvas", ImGuiTableColumnFlags_WidthStretch,
187 ImGui::GetContentRegionAvail().x);
188 TableSetupColumn(
"Object Selector");
193 if (ImGui::BeginTabBar(
"##DungeonRoomTabBar")) {
210 return absl::OkStatus();
214 if (BeginTable(
"DWToolset", 14, ImGuiTableFlags_SizingFixedFit,
216 static std::array<const char *, 14> tool_names = {
217 "Undo",
"Redo",
"Separator",
"Any",
"BG1",
"BG2",
"BG3",
218 "Separator",
"Sprite",
"Item",
"Door",
"Block",
"Palette"};
219 std::ranges::for_each(tool_names,
220 [](
const char *name) { TableSetupColumn(name); });
262 if (ImGui::IsItemHovered()) {
263 ImGui::SetTooltip(
"Sprites");
270 if (ImGui::IsItemHovered()) {
271 ImGui::SetTooltip(
"Items");
278 if (ImGui::IsItemHovered()) {
279 ImGui::SetTooltip(
"Doors");
286 if (ImGui::IsItemHovered()) {
287 ImGui::SetTooltip(
"Blocks");
300 if (
rom()->is_loaded()) {
304 if (ImGuiID child_id = ImGui::GetID((
void *)(intptr_t)9);
305 BeginChild(child_id, ImGui::GetContentRegionAvail(),
true,
306 ImGuiWindowFlags_AlwaysVerticalScrollbar)) {
311 each_room_name.data());
312 if (ImGui::IsItemClicked()) {
326using ImGui::Separator;
329 if (
rom()->is_loaded()) {
359 Text(
"Camera Boundaries");
361 Text(
"\t\t\t\t\tNorth East South West");
380 if (BeginChild(
"EntranceSelector", ImVec2(0, 0),
true,
381 ImGuiWindowFlags_AlwaysVerticalScrollbar)) {
382 for (
int i = 0; i < 0x85 + 7; i++) {
387 if (ImGui::IsItemClicked()) {
400 static int next_tab_id = 0;
422 ImGuiTabItemFlags_None)) {
461 if (Button(
"Load Room")) {
462 rooms_[room_id].LoadRoomGraphics(
rooms_[room_id].blockset);
463 rooms_[room_id].RenderRoomGraphics();
466 static bool show_objects =
false;
467 ImGui::Checkbox(
"Show Objects", &show_objects);
478 for (
const auto &
object :
rooms_[room_id].tile_objects_) {
479 canvas_.DrawOutline(
object.x_,
object.y_,
object.width_ * 16,
480 object.height_ * 16);
489 const auto height = 0x40;
495 int current_block = 0;
496 for (
int block : blocks) {
497 int offset = height * (current_block + 1);
499 if (current_block >= 1) {
517 if (BeginTabBar(
"##TabBar", ImGuiTabBarFlags_FittingPolicyScroll)) {
518 if (BeginTabItem(
"Room Graphics")) {
519 if (ImGuiID child_id = ImGui::GetID((
void *)(intptr_t)3);
520 BeginChild(child_id, ImGui::GetContentRegionAvail(),
true,
521 ImGuiWindowFlags_AlwaysVerticalScrollbar)) {
528 if (BeginTabItem(
"Object Renderer")) {
539 TableSetupColumn(
"Dungeon Objects", ImGuiTableColumnFlags_WidthStretch,
540 ImGui::GetContentRegionAvail().x);
541 TableSetupColumn(
"Canvas");
544 BeginChild(
"DungeonObjectButtons", ImVec2(250, 0),
true);
546 int selected_object = 0;
548 for (
const auto object_name : zelda3::Type1RoomObjectNames) {
549 if (ImGui::Selectable(object_name.data(), selected_object == i)) {
562 BeginChild(
"DungeonObjectCanvas", ImVec2(276, 0x10 * 0x40 + 1),
true);
576 static MemoryEditor mem_edit;
584 for (
const auto &room :
rooms_) {
606 const absl::flat_hash_map<uint16_t, int> &usage_map, uint16_t &selected_set,
607 int spriteset_offset) {
609 std::vector<std::pair<uint16_t, int>> sorted_usage(usage_map.begin(),
611 std::sort(sorted_usage.begin(), sorted_usage.end(),
612 [](
const auto &a,
const auto &b) { return a.first < b.first; });
614 for (
const auto &[set, count] : sorted_usage) {
615 std::string display_str;
616 if (spriteset_offset != 0x00) {
617 display_str = absl::StrFormat(
"%#02x, %#02x: %d", set,
618 (set + spriteset_offset), count);
621 absl::StrFormat(
"%#02x: %d", (set + spriteset_offset), count);
623 if (ImGui::Selectable(display_str.c_str(), selected_set == set)) {
636 int spriteset_offset = 0x00) {
637 std::vector<int> unused_sets;
638 for (
int i = 0; i < max_set; i++) {
639 if (usage_map.find(i) == usage_map.end()) {
640 unused_sets.push_back(i);
643 for (
const auto &set : unused_sets) {
644 if (spriteset_offset != 0x00) {
645 Text(
"%#02x, %#02x", set, (set + spriteset_offset));
654 if (Button(
"Refresh")) {
664 ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(0, 0));
665 ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(0, 0));
666 if (BeginTable(
"DungeonUsageStatsTable", 8,
668 ImGui::GetContentRegionAvail())) {
669 TableSetupColumn(
"Blockset Usage");
670 TableSetupColumn(
"Unused Blockset");
671 TableSetupColumn(
"Palette Usage");
672 TableSetupColumn(
"Unused Palette");
673 TableSetupColumn(
"Spriteset Usage");
674 TableSetupColumn(
"Unused Spriteset");
675 TableSetupColumn(
"Usage Grid");
676 TableSetupColumn(
"Group Preview");
678 ImGui::PopStyleVar(2);
681 BeginChild(
"BlocksetUsageScroll", ImVec2(0, 0),
true,
682 ImGuiWindowFlags_HorizontalScrollbar);
687 BeginChild(
"UnusedBlocksetScroll", ImVec2(0, 0),
true,
688 ImGuiWindowFlags_HorizontalScrollbar);
693 BeginChild(
"PaletteUsageScroll", ImVec2(0, 0),
true,
694 ImGuiWindowFlags_HorizontalScrollbar);
699 BeginChild(
"UnusedPaletteScroll", ImVec2(0, 0),
true,
700 ImGuiWindowFlags_HorizontalScrollbar);
706 BeginChild(
"SpritesetUsageScroll", ImVec2(0, 0),
true,
707 ImGuiWindowFlags_HorizontalScrollbar);
712 BeginChild(
"UnusedSpritesetScroll", ImVec2(0, 0),
true,
713 ImGuiWindowFlags_HorizontalScrollbar);
718 BeginChild(
"UsageGrid", ImVec2(0, 0),
true,
719 ImGuiWindowFlags_HorizontalScrollbar);
720 Text(
"%s", absl::StrFormat(
"Total size of all rooms: %d hex format: %#06x",
739 int totalSquares = 296;
740 int squaresWide = 16;
741 int squaresTall = (totalSquares + squaresWide - 1) /
744 for (
int row = 0; row < squaresTall; ++row) {
747 for (
int col = 0; col < squaresWide; ++col) {
749 if (row * squaresWide + col >= totalSquares) {
753 const auto &room =
rooms_[row * squaresWide + col];
758 color.x = color.x / 255;
759 color.y = color.y / 255;
760 color.z = color.z / 255;
762 if (
room_sizes_[row * squaresWide + col] > 0xFFFF) {
763 color = ImVec4(1.0f, 0.0f, 0.0f, 1.0f);
766 color = ImVec4(0.0f, 0.0f, 0.0f, 1.0f);
768 ImGui::PushStyleColor(ImGuiCol_Button, color);
770 ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(0.0f, 0.0f, 0.0f, 1.0f));
778 ImGui::PushStyleColor(
780 ImVec4(1.0f, 0.5f, 0.0f, 1.0f));
782 if (Button(absl::StrFormat(
"%#x",
room_sizes_[row * squaresWide + col])
789 if (ImGui::IsMouseClicked(ImGuiMouseButton_Right)) {
791 absl::StrFormat(
"RoomContextMenu%d", row * squaresWide + col)
794 ImGui::PopStyleColor(2);
799 ImGui::PopStyleColor();
803 if (ImGui::IsItemHovered()) {
805 ImGui::BeginTooltip();
806 Text(
"Room ID: %d", row * squaresWide + col);
807 Text(
"Blockset: %#02x", room.blockset);
808 Text(
"Spriteset: %#02x", room.spriteset);
809 Text(
"Palette: %#02x", room.palette);
810 Text(
"Floor1: %#02x", room.floor1);
811 Text(
"Floor2: %#02x", room.floor2);
812 Text(
"Message ID: %#04x", room.message_id_);
813 Text(
"Size: %#016llx",
room_sizes_[row * squaresWide + col]);
814 Text(
"Size Pointer: %#016llx",
auto palette_group() const
ResourceLabelManager * resource_label()
std::array< std::array< uint8_t, 4 >, kNumPalettesets > paletteset_ids
absl::StatusOr< uint16_t > ReadWord(int offset)
void UpdateBitmap(gfx::Bitmap *bitmap)
absl::flat_hash_map< uint16_t, int > spriteset_usage_
void LoadDungeonRoomSize()
uint16_t selected_blockset_
absl::Status RefreshGraphics()
absl::Status Update() override
uint64_t current_palette_group_id_
void DrawEntranceSelector()
std::unordered_map< int, ImVec4 > room_palette_
uint16_t current_room_id_
absl::flat_hash_map< uint16_t, int > blockset_usage_
uint64_t current_palette_id_
absl::flat_hash_map< uint16_t, int > palette_usage_
void RenderSetUsage(const absl::flat_hash_map< uint16_t, int > &usage_map, uint16_t &selected_set, int spriteset_offset=0x00)
uint16_t selected_spriteset_
GfxGroupEditor gfx_group_editor_
uint16_t selected_palette_
uint64_t total_room_size_
gfx::SnesPalette current_palette_
gfx::PaletteGroup current_palette_group_
void DrawDungeonCanvas(int room_id)
uint16_t current_entrance_id_
std::unordered_map< int, int > room_size_addresses_
absl::Status Redo() override
void Initialize() override
gui::Canvas room_gfx_canvas_
gfx::SnesPalette full_palette_
gui::Canvas object_canvas_
void DrawDungeonTabView()
std::vector< int64_t > room_sizes_
std::array< zelda3::RoomEntrance, 0x8C > entrances_
void CalculateUsageStats()
std::vector< int64_t > room_size_pointers_
absl::Status Load() override
void DrawObjectRenderer()
ImVector< int > active_rooms_
absl::Status UpdateDungeonRoomView()
absl::Status Undo() override
std::array< zelda3::Room, 0x128 > rooms_
std::array< gfx::Bitmap, 223 > & gfx_sheets()
Dungeon Room Entrance or Spawn Point.
#define ICON_MD_MORE_VERT
#define ICON_MD_FILTER_NONE
#define ICON_MD_SENSOR_DOOR
#define ICON_MD_PEST_CONTROL
#define PRINT_IF_ERROR(expression)
#define RETURN_IF_ERROR(expression)
#define ASSIGN_OR_RETURN(type_variable_name, expression)
void RenderUnusedSets(const absl::flat_hash_map< T, int > &usage_map, int max_set, int spriteset_offset=0x00)
Editors are the view controllers for the application.
constexpr ImGuiTableFlags kDungeonTableFlags
constexpr ImGuiTabItemFlags kDungeonTabFlags
constexpr ImGuiTabBarFlags kDungeonTabBarFlags
constexpr ImGuiTableFlags kDungeonObjectTableFlags
absl::StatusOr< PaletteGroup > CreatePaletteGroupFromLargePalette(SnesPalette &palette, int num_colors)
Take a SNESPalette, divide it into palettes of 8 colors.
bool InputHexWord(const char *label, uint16_t *data, float input_width, bool no_step)
bool InputHex(const char *label, uint64_t *data)
void SelectablePalettePipeline(uint64_t &palette_id, bool &refresh_graphics, gfx::SnesPalette &palette)
bool InputHexByte(const char *label, uint8_t *data, float input_width, bool no_step)
std::string HexByte(uint8_t byte, HexStringParams params)
constexpr std::string_view kEntranceNames[]
RoomSize CalculateRoomSize(Rom *rom, int room_id)
constexpr std::string_view kRoomNames[]
Room LoadRoomFromRom(Rom *rom, int room_id)
void SelectableLabelWithNameEdit(bool selected, const std::string &type, const std::string &key, const std::string &defaultValue)