3#include "absl/status/status.h"
4#include "absl/strings/str_cat.h"
5#include "absl/strings/str_format.h"
15#include "imgui/imgui.h"
21using ImGui::BeginChild;
22using ImGui::BeginCombo;
23using ImGui::BeginGroup;
24using ImGui::BeginTabItem;
25using ImGui::BeginTable;
29using ImGui::EndTabItem;
31using ImGui::GetContentRegionAvail;
33using ImGui::IsItemClicked;
37using ImGui::Selectable;
38using ImGui::Separator;
39using ImGui::SetNextItemWidth;
40using ImGui::SliderFloat;
41using ImGui::TableHeadersRow;
42using ImGui::TableNextColumn;
43using ImGui::TableNextRow;
44using ImGui::TableSetupColumn;
47using gfx::kPaletteGroupNames;
56constexpr int kTileSize = 16;
60 float scale = kDefaultScale) {
68 canvas.
DrawBackground(ImVec2(display_width + 1, display_height + 1));
75 canvas.
DrawGrid(
static_cast<int>(8 * scale));
94 if (BeginTabItem(
"Blocksets")) {
96 static_cast<uint8_t
>(0x24));
98 false,
"blockset",
"0x" + std::to_string(
Ws().selected_blockset),
99 "Blockset " + std::to_string(
Ws().selected_blockset));
104 if (BeginTabItem(
"Roomsets")) {
106 static_cast<uint8_t
>(81));
108 false,
"roomset",
"0x" + std::to_string(
Ws().selected_roomset),
109 "Roomset " + std::to_string(
Ws().selected_roomset));
114 if (BeginTabItem(
"Spritesets")) {
116 static_cast<uint8_t
>(143));
118 false,
"spriteset",
"0x" + std::to_string(
Ws().selected_spriteset),
119 "Spriteset " + std::to_string(
Ws().selected_spriteset));
127 return absl::OkStatus();
132 Text(
"No game data loaded");
136 PushID(
"BlocksetViewer");
139 if (BeginTable(
"##BlocksetTable", sheet_only ? 1 : 2,
140 ImGuiTableFlags_Borders | ImGuiTableFlags_Resizable,
143 TableSetupColumn(
"Inputs", ImGuiTableColumnFlags_WidthStretch,
144 GetContentRegionAvail().x);
147 TableSetupColumn(
"Sheets", ImGuiTableColumnFlags_WidthFixed,
148 kSheetDisplayWidth + 16);
155 for (
int idx = 0; idx < 8; idx++) {
158 (
"Slot " + std::to_string(idx)).c_str(),
159 &
game_data()->main_blockset_ids[ws.selected_blockset][idx]);
166 for (
int idx = 0; idx < 8; idx++) {
177 int unique_id = (ws.selected_blockset << 16) | (idx << 8) | sheet_id;
189 Text(
"No game data loaded");
193 PushID(
"RoomsetViewer");
195 Text(
"Roomsets overwrite slots 4-7 of the main blockset");
197 if (BeginTable(
"##RoomsTable", 3,
198 ImGuiTableFlags_Borders | ImGuiTableFlags_Resizable,
200 TableSetupColumn(
"List", ImGuiTableColumnFlags_WidthFixed, 120);
201 TableSetupColumn(
"Inputs", ImGuiTableColumnFlags_WidthStretch,
202 GetContentRegionAvail().x);
203 TableSetupColumn(
"Sheets", ImGuiTableColumnFlags_WidthFixed,
204 kSheetDisplayWidth + 16);
210 if (BeginChild(
"##RoomsetListChild", ImVec2(0, 300))) {
211 for (
int idx = 0; idx < 0x51; idx++) {
213 std::string roomset_label = absl::StrFormat(
"0x%02X", idx);
214 bool is_selected = (ws.selected_roomset ==
static_cast<uint8_t
>(idx));
215 if (Selectable(roomset_label.c_str(), is_selected)) {
216 ws.selected_roomset =
static_cast<uint8_t
>(idx);
226 Text(
"Sheet IDs (overwrites slots 4-7):");
227 for (
int idx = 0; idx < 4; idx++) {
230 (
"Slot " + std::to_string(idx + 4)).c_str(),
231 &
game_data()->room_blockset_ids[ws.selected_roomset][idx]);
238 for (
int idx = 0; idx < 4; idx++) {
250 (0x1000) | (ws.selected_roomset << 8) | (idx << 4) | sheet_id;
262 Text(
"No game data loaded");
266 PushID(
"SpritesetViewer");
269 if (BeginTable(
"##SpritesTable", sheet_only ? 1 : 2,
270 ImGuiTableFlags_Borders | ImGuiTableFlags_Resizable,
273 TableSetupColumn(
"Inputs", ImGuiTableColumnFlags_WidthStretch,
274 GetContentRegionAvail().x);
276 TableSetupColumn(
"Sheets", ImGuiTableColumnFlags_WidthFixed,
277 kSheetDisplayWidth + 16);
284 Text(
"Sprite sheet IDs (base 115+):");
285 for (
int idx = 0; idx < 4; idx++) {
288 (
"Slot " + std::to_string(idx)).c_str(),
289 &
game_data()->spriteset_ids[ws.selected_spriteset][idx]);
296 for (
int idx = 0; idx < 4; idx++) {
298 int sheet_id = 115 + sheet_offset;
309 (0x2000) | (ws.selected_spriteset << 8) | (idx << 4) | sheet_offset;
322 if (palette.
empty()) {
325 for (
size_t color_idx = 0; color_idx < palette.
size(); color_idx++) {
326 PushID(
static_cast<int>(color_idx));
327 if ((color_idx % 8) != 0) {
328 SameLine(0.0f, GetStyle().ItemSpacing.y);
333 ImGuiColorEditFlags_NoAlpha |
334 ImGuiColorEditFlags_NoPicker |
335 ImGuiColorEditFlags_NoTooltip);
353 SliderFloat(
"##ViewScale", &ws.view_scale, 1.0f, 4.0f,
"%.1fx");
362 static constexpr int kNumPaletteCategories = 14;
365 gfx::kPaletteCategoryNames[ws.selected_palette_category].data())) {
366 for (
int cat = 0; cat < kNumPaletteCategories; cat++) {
368 bool is_selected = (ws.selected_palette_category == category);
369 if (Selectable(gfx::kPaletteCategoryNames[category].data(),
371 ws.selected_palette_category = category;
372 ws.selected_palette_index = 0;
376 ImGui::SetItemDefaultFocus();
389 ImGui::Checkbox(
"Apply", &ws.use_custom_palette);
390 if (ImGui::IsItemHovered()) {
391 ImGui::SetTooltip(
"Apply selected palette to sheet previews");
407 const auto& ws =
Ws();
409 switch (ws.selected_palette_category) {
410 case PaletteCategory::kSword:
412 groups.swords.mutable_palette(ws.selected_palette_index);
414 case PaletteCategory::kShield:
416 groups.shields.mutable_palette(ws.selected_palette_index);
418 case PaletteCategory::kClothes:
420 groups.armors.mutable_palette(ws.selected_palette_index);
422 case PaletteCategory::kWorldColors:
424 groups.overworld_main.mutable_palette(ws.selected_palette_index);
426 case PaletteCategory::kAreaColors:
428 groups.overworld_aux.mutable_palette(ws.selected_palette_index);
430 case PaletteCategory::kGlobalSprites:
432 groups.global_sprites.mutable_palette(ws.selected_palette_index);
434 case PaletteCategory::kSpritesAux1:
436 groups.sprites_aux1.mutable_palette(ws.selected_palette_index);
438 case PaletteCategory::kSpritesAux2:
440 groups.sprites_aux2.mutable_palette(ws.selected_palette_index);
442 case PaletteCategory::kSpritesAux3:
444 groups.sprites_aux3.mutable_palette(ws.selected_palette_index);
446 case PaletteCategory::kDungeons:
448 groups.dungeon_main.mutable_palette(ws.selected_palette_index);
450 case PaletteCategory::kWorldMap:
451 case PaletteCategory::kDungeonMap:
453 groups.overworld_mini_map.mutable_palette(ws.selected_palette_index);
455 case PaletteCategory::kTriforce:
456 case PaletteCategory::kCrystal:
458 groups.object_3d.mutable_palette(ws.selected_palette_index);
project::ResourceLabelManager * resource_label()
std::string host_surface_hint_
std::array< gui::Canvas, 8 > blockset_canvases_
zelda3::GameData * game_data() const
std::array< gui::Canvas, 4 > roomset_canvases_
void UpdateCurrentPalette()
GfxGroupWorkspaceState & Ws()
gfx::SnesPalette * current_palette_
void DrawSpritesetViewer(bool sheet_only=false)
std::array< gui::Canvas, 4 > spriteset_canvases_
void DrawPaletteControls()
void DrawBlocksetViewer(bool sheet_only=false)
auto mutable_gfx_sheets()
Get mutable reference to all graphics sheets.
void NotifySheetModified(int sheet_index)
Notify Arena that a graphics sheet has been modified.
Represents a bitmap image optimized for SNES ROM hacking.
Represents a palette of colors for the Super Nintendo Entertainment System (SNES).
Modern, robust canvas for drawing and manipulating graphics.
void DrawBitmap(Bitmap &bitmap, int border_offset, float scale)
void DrawBackground(ImVec2 canvas_size=ImVec2(0, 0))
void DrawGrid(float grid_step=64.0f, int tile_id_offset=8)
static float GetHexInputWidth()
static float GetSliderWidth()
static float GetComboWidth()
constexpr int kSheetDisplayWidth
void DrawScaledSheet(gui::Canvas &canvas, gfx::Bitmap &sheet, int unique_id, float scale=kDefaultScale)
constexpr int kSheetDisplayHeight
void DrawPaletteFromPaletteGroup(gfx::SnesPalette &palette)
constexpr float kDefaultScale
PaletteCategory
Categories for organizing palette groups in the UI.
constexpr int kTilesheetHeight
constexpr int kTilesheetWidth
IMGUI_API bool SnesColorButton(absl::string_view id, gfx::SnesColor &color, ImGuiColorEditFlags flags, const ImVec2 &size_arg)
bool BeginThemedTabBar(const char *id, ImGuiTabBarFlags flags)
A stylized tab bar with "Mission Control" branding.
bool InputHexByte(const char *label, uint8_t *data, float input_width, bool no_step)
void SelectableLabelWithNameEdit(bool selected, const std::string &type, const std::string &key, const std::string &defaultValue)
std::array< std::array< uint8_t, 4 >, kNumSpritesets > spriteset_ids
std::array< std::array< uint8_t, 4 >, kNumRoomBlocksets > room_blockset_ids
gfx::PaletteGroupMap palette_groups
std::array< std::array< uint8_t, 8 >, kNumMainBlocksets > main_blockset_ids