3#include "absl/container/flat_hash_map.h"
14#include "imgui/imgui.h"
15#include "imgui_memory_editor.h"
23using ImGui::BeginChild;
24using ImGui::BeginTabBar;
25using ImGui::BeginTabItem;
26using ImGui::BeginTable;
29using ImGui::EndTabBar;
30using ImGui::EndTabItem;
31using ImGui::RadioButton;
33using ImGui::TableHeadersRow;
34using ImGui::TableNextColumn;
35using ImGui::TableNextRow;
36using ImGui::TableSetupColumn;
40 ImGuiTableFlags_Resizable | ImGuiTableFlags_Reorderable |
41 ImGuiTableFlags_Hideable | ImGuiTableFlags_BordersOuter |
42 ImGuiTableFlags_BordersV;
45 return absl::OkStatus();
59 if (ImGui::BeginTabBar(
"##DungeonEditorTabBar")) {
65 static bool calc_stats =
false;
76 return absl::OkStatus();
80 auto dungeon_man_pal_group =
rom()->palette_group().dungeon_main;
82 for (
int i = 0; i < 0x100 + 40; i++) {
85 rooms_[i].LoadRoomFromROM();
87 rooms_[i].LoadRoomGraphics();
91 if (
rooms_[i].room_size_ptr() != 0x0A8000) {
95 auto dungeon_palette_ptr =
rom()->paletteset_ids[
rooms_[i].palette][0];
96 auto palette_id =
rom()->ReadWord(0xDEC4B + dungeon_palette_ptr);
97 if (palette_id.status() != absl::OkStatus()) {
100 int p_id = palette_id.value() / 180;
101 auto color = dungeon_man_pal_group[p_id][3];
107 for (
int i = 0; i < 0x07; ++i) {
111 for (
int i = 0; i < 0x85; ++i) {
117 auto current_palette_group =
119 if (current_palette_group.ok()) {
135 [
this](
int block) -> absl::Status {
139 return absl::OkStatus();
142 auto sprites_aux1_pal_group =
rom()->palette_group().sprites_aux1;
145 [
this, &sprites_aux1_pal_group](
int block) -> absl::Status {
149 return absl::OkStatus();
151 return absl::OkStatus();
155 std::map<int, std::vector<int>> rooms_by_bank;
157 int bank = room.second >> 16;
158 rooms_by_bank[bank].push_back(room.second);
162 for (
auto &bank_rooms : rooms_by_bank) {
164 std::sort(bank_rooms.second.begin(), bank_rooms.second.end());
166 for (
size_t i = 0; i < bank_rooms.second.size(); ++i) {
167 int room_ptr = bank_rooms.second[i];
172 [room_ptr](
const auto &entry) {
173 return entry.second == room_ptr;
177 if (room_ptr != 0x0A8000) {
178 if (i < bank_rooms.second.size() - 1) {
181 rooms_[room_id].set_room_size(bank_rooms.second[i + 1] - room_ptr);
184 int bank_end_address = (bank_rooms.first << 16) | 0xFFFF;
185 rooms_[room_id].set_room_size(bank_end_address - room_ptr + 1);
190 rooms_[room_id].set_room_size(0x00);
201 auto dungeon_main_pal_group =
rom()->palette_group().dungeon_main;
209 TableSetupColumn(
"Room Selector");
210 TableSetupColumn(
"Canvas", ImGuiTableColumnFlags_WidthStretch,
211 ImGui::GetContentRegionAvail().x);
212 TableSetupColumn(
"Object Selector");
217 if (ImGui::BeginTabBar(
"##DungeonRoomTabBar")) {
234 return absl::OkStatus();
238 if (BeginTable(
"DWToolset", 13, ImGuiTableFlags_SizingFixedFit,
240 TableSetupColumn(
"#undoTool");
241 TableSetupColumn(
"#redoTool");
242 TableSetupColumn(
"#separator");
243 TableSetupColumn(
"#anyTool");
245 TableSetupColumn(
"#bg1Tool");
246 TableSetupColumn(
"#bg2Tool");
247 TableSetupColumn(
"#bg3Tool");
248 TableSetupColumn(
"#separator");
249 TableSetupColumn(
"#spriteTool");
250 TableSetupColumn(
"#itemTool");
251 TableSetupColumn(
"#doorTool");
252 TableSetupColumn(
"#blockTool");
294 if (ImGui::IsItemHovered()) {
295 ImGui::SetTooltip(
"Sprites");
302 if (ImGui::IsItemHovered()) {
303 ImGui::SetTooltip(
"Items");
310 if (ImGui::IsItemHovered()) {
311 ImGui::SetTooltip(
"Doors");
318 if (ImGui::IsItemHovered()) {
319 ImGui::SetTooltip(
"Blocks");
332 if (
rom()->is_loaded()) {
336 if (ImGuiID child_id = ImGui::GetID((
void *)(intptr_t)9);
337 BeginChild(child_id, ImGui::GetContentRegionAvail(),
true,
338 ImGuiWindowFlags_AlwaysVerticalScrollbar)) {
341 rom()->resource_label()->SelectableLabelWithNameEdit(
343 each_room_name.data());
344 if (ImGui::IsItemClicked()) {
358using ImGui::Separator;
361 if (
rom()->is_loaded()) {
391 Text(
"Camera Boundaries");
393 Text(
"\t\t\t\t\tNorth East South West");
412 if (BeginChild(
"EntranceSelector", ImVec2(0, 0),
true,
413 ImGuiWindowFlags_AlwaysVerticalScrollbar)) {
414 for (
int i = 0; i < 0x85 + 7; i++) {
415 rom()->resource_label()->SelectableLabelWithNameEdit(
419 if (ImGui::IsItemClicked()) {
432 static int next_tab_id = 0;
454 ImGuiTabItemFlags_None)) {
495 canvas_.DrawBackground(ImVec2(0x200, 0x200));
505 const auto height = 0x40;
506 const int num_sheets = 0x10;
507 room_gfx_canvas_.DrawBackground(ImVec2(0x100 + 1, num_sheets * height + 1));
512 int current_block = 0;
513 for (
int block : blocks) {
514 int offset = height * (current_block + 1);
516 if (current_block >= 1) {
532 if (BeginTabBar(
"##TabBar", ImGuiTabBarFlags_FittingPolicyScroll)) {
533 if (BeginTabItem(
"Room Graphics")) {
534 if (ImGuiID child_id = ImGui::GetID((
void *)(intptr_t)3);
535 BeginChild(child_id, ImGui::GetContentRegionAvail(),
true,
536 ImGuiWindowFlags_AlwaysVerticalScrollbar)) {
543 if (BeginTabItem(
"Object Renderer")) {
554 TableSetupColumn(
"Dungeon Objects", ImGuiTableColumnFlags_WidthStretch,
555 ImGui::GetContentRegionAvail().x);
556 TableSetupColumn(
"Canvas");
559 BeginChild(
"DungeonObjectButtons", ImVec2(250, 0),
true);
561 int selected_object = 0;
563 for (
const auto object_name : zelda3::Type1RoomObjectNames) {
564 if (ImGui::Selectable(object_name.data(), selected_object == i)) {
579 BeginChild(
"DungeonObjectCanvas", ImVec2(276, 0x10 * 0x40 + 1),
true);
596 static MemoryEditor mem_edit;
604 for (
const auto &room :
rooms_) {
626 const absl::flat_hash_map<uint16_t, int> &usage_map, uint16_t &selected_set,
627 int spriteset_offset) {
629 std::vector<std::pair<uint16_t, int>> sorted_usage(usage_map.begin(),
631 std::sort(sorted_usage.begin(), sorted_usage.end(),
632 [](
const auto &a,
const auto &b) { return a.first < b.first; });
634 for (
const auto &[set, count] : sorted_usage) {
635 std::string display_str;
636 if (spriteset_offset != 0x00) {
637 display_str = absl::StrFormat(
"%#02x, %#02x: %d", set,
638 (set + spriteset_offset), count);
641 absl::StrFormat(
"%#02x: %d", (set + spriteset_offset), count);
643 if (ImGui::Selectable(display_str.c_str(), selected_set == set)) {
656 int spriteset_offset = 0x00) {
657 std::vector<int> unused_sets;
658 for (
int i = 0; i < max_set; i++) {
659 if (usage_map.find(i) == usage_map.end()) {
660 unused_sets.push_back(i);
663 for (
const auto &set : unused_sets) {
664 if (spriteset_offset != 0x00) {
665 Text(
"%#02x, %#02x", set, (set + spriteset_offset));
674 if (Button(
"Refresh")) {
684 ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(0, 0));
685 ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(0, 0));
686 if (BeginTable(
"DungeonUsageStatsTable", 8,
688 ImGui::GetContentRegionAvail())) {
689 TableSetupColumn(
"Blockset Usage");
690 TableSetupColumn(
"Unused Blockset");
691 TableSetupColumn(
"Palette Usage");
692 TableSetupColumn(
"Unused Palette");
693 TableSetupColumn(
"Spriteset Usage");
694 TableSetupColumn(
"Unused Spriteset");
695 TableSetupColumn(
"Usage Grid");
696 TableSetupColumn(
"Group Preview");
698 ImGui::PopStyleVar(2);
701 BeginChild(
"BlocksetUsageScroll", ImVec2(0, 0),
true,
702 ImGuiWindowFlags_HorizontalScrollbar);
707 BeginChild(
"UnusedBlocksetScroll", ImVec2(0, 0),
true,
708 ImGuiWindowFlags_HorizontalScrollbar);
713 BeginChild(
"PaletteUsageScroll", ImVec2(0, 0),
true,
714 ImGuiWindowFlags_HorizontalScrollbar);
719 BeginChild(
"UnusedPaletteScroll", ImVec2(0, 0),
true,
720 ImGuiWindowFlags_HorizontalScrollbar);
726 BeginChild(
"SpritesetUsageScroll", ImVec2(0, 0),
true,
727 ImGuiWindowFlags_HorizontalScrollbar);
732 BeginChild(
"UnusedSpritesetScroll", ImVec2(0, 0),
true,
733 ImGuiWindowFlags_HorizontalScrollbar);
738 BeginChild(
"UsageGrid", ImVec2(0, 0),
true,
739 ImGuiWindowFlags_HorizontalScrollbar);
740 Text(
"%s", absl::StrFormat(
"Total size of all rooms: %d hex format: %#06x",
759 int totalSquares = 296;
760 int squaresWide = 16;
761 int squaresTall = (totalSquares + squaresWide - 1) /
764 for (
int row = 0; row < squaresTall; ++row) {
767 for (
int col = 0; col < squaresWide; ++col) {
769 if (row * squaresWide + col >= totalSquares) {
773 const auto &room =
rooms_[row * squaresWide + col];
778 color.x = color.x / 255;
779 color.y = color.y / 255;
780 color.z = color.z / 255;
782 if (
rooms_[row * squaresWide + col].room_size() > 0xFFFF) {
783 color = ImVec4(1.0f, 0.0f, 0.0f, 1.0f);
785 if (
rooms_[row * squaresWide + col].room_size() == 0) {
786 color = ImVec4(0.0f, 0.0f, 0.0f, 1.0f);
788 ImGui::PushStyleColor(ImGuiCol_Button, color);
790 ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(0.0f, 0.0f, 0.0f, 1.0f));
798 ImGui::PushStyleColor(
800 ImVec4(1.0f, 0.5f, 0.0f, 1.0f));
802 if (Button(absl::StrFormat(
"%#x",
803 rooms_[row * squaresWide + col].room_size())
810 if (ImGui::IsMouseClicked(ImGuiMouseButton_Right)) {
812 absl::StrFormat(
"RoomContextMenu%d", row * squaresWide + col)
815 ImGui::PopStyleColor(2);
820 ImGui::PopStyleColor();
824 if (ImGui::IsItemHovered()) {
826 ImGui::BeginTooltip();
827 Text(
"Room ID: %d", row * squaresWide + col);
828 Text(
"Blockset: %#02x", room.blockset);
829 Text(
"Spriteset: %#02x", room.spriteset);
830 Text(
"Palette: %#02x", room.palette);
831 Text(
"Floor1: %#02x", room.floor1);
832 Text(
"Floor2: %#02x", room.floor2);
833 Text(
"Message ID: %#04x", room.message_id_);
834 Text(
"Size: %#016llx", room.room_size());
835 Text(
"Size Pointer: %#016llx", room.room_size_ptr());
static GraphicsSheetManager & GetInstance()
std::array< gfx::Bitmap, kNumGfxSheets > & gfx_sheets()
void UpdateBitmap(gfx::Bitmap *bitmap)
Used to update a bitmap on the screen.
void RenderBitmap(gfx::Bitmap *bitmap)
Used to render a bitmap to the screen.
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::vector< zelda3::RoomEntrance > entrances_
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_
std::vector< zelda3::Room > rooms_
void RenderSetUsage(const absl::flat_hash_map< uint16_t, int > &usage_map, uint16_t &selected_set, int spriteset_offset=0x00)
uint16_t selected_spriteset_
std::vector< gfx::Bitmap * > room_gfx_sheets_
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
zelda3::DungeonObjectRenderer object_renderer_
gui::Canvas room_gfx_canvas_
gfx::SnesPalette full_palette_
gui::Canvas object_canvas_
void DrawDungeonTabView()
void CalculateUsageStats()
std::vector< int64_t > room_size_pointers_
absl::Status Load() override
std::array< gfx::Bitmap, kNumGfxSheets > graphics_bin_
void DrawObjectRenderer()
ImVector< int > active_rooms_
absl::Status UpdateDungeonRoomView()
absl::Status Undo() override
static Renderer & GetInstance()
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)
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[]
constexpr std::string_view kRoomNames[]
Main namespace for the application.