4#include "absl/status/status.h"
5#include "absl/strings/str_cat.h"
6#include "absl/strings/str_format.h"
17#include "imgui/imgui.h"
23using ImGui::BeginChild;
24using ImGui::BeginCombo;
25using ImGui::BeginGroup;
26using ImGui::BeginTabItem;
27using ImGui::BeginTable;
31using ImGui::EndTabItem;
33using ImGui::GetContentRegionAvail;
35using ImGui::IsItemClicked;
39using ImGui::Selectable;
40using ImGui::Separator;
41using ImGui::SetNextItemWidth;
42using ImGui::SliderFloat;
43using ImGui::TableHeadersRow;
44using ImGui::TableNextColumn;
45using ImGui::TableNextRow;
46using ImGui::TableSetupColumn;
49using gfx::kPaletteGroupNames;
58constexpr int kTileSize = 16;
62 float scale = kDefaultScale) {
75 canvas.
DrawBackground(ImVec2(display_width + 1, display_height + 1));
82 canvas.
DrawGrid(
static_cast<int>(8 * scale));
101 if (BeginTabItem(tr(
"Blocksets"))) {
103 static_cast<uint8_t
>(0x24));
105 false,
"blockset",
"0x" + std::to_string(
Ws().selected_blockset),
106 "Blockset " + std::to_string(
Ws().selected_blockset));
111 if (BeginTabItem(tr(
"Roomsets"))) {
113 static_cast<uint8_t
>(81));
115 false,
"roomset",
"0x" + std::to_string(
Ws().selected_roomset),
116 "Roomset " + std::to_string(
Ws().selected_roomset));
121 if (BeginTabItem(tr(
"Spritesets"))) {
123 static_cast<uint8_t
>(143));
125 false,
"spriteset",
"0x" + std::to_string(
Ws().selected_spriteset),
126 "Spriteset " + std::to_string(
Ws().selected_spriteset));
134 return absl::OkStatus();
139 Text(tr(
"No game data loaded"));
143 PushID(
"BlocksetViewer");
146 if (BeginTable(
"##BlocksetTable", sheet_only ? 1 : 2,
147 ImGuiTableFlags_Borders | ImGuiTableFlags_Resizable,
150 TableSetupColumn(
"Inputs", ImGuiTableColumnFlags_WidthStretch,
151 GetContentRegionAvail().x);
154 TableSetupColumn(
"Sheets", ImGuiTableColumnFlags_WidthFixed,
155 kSheetDisplayWidth + 16);
162 for (
int idx = 0; idx < 8; idx++) {
165 (
"Slot " + std::to_string(idx)).c_str(),
166 &
game_data()->main_blockset_ids[ws.selected_blockset][idx]);
173 for (
int idx = 0; idx < 8; idx++) {
191 int unique_id = (ws.selected_blockset << 16) | (idx << 8) | sheet_id;
203 Text(tr(
"No game data loaded"));
207 PushID(
"RoomsetViewer");
209 Text(tr(
"Roomsets overwrite slots 4-7 of the main blockset"));
211 if (BeginTable(
"##RoomsTable", 3,
212 ImGuiTableFlags_Borders | ImGuiTableFlags_Resizable,
214 TableSetupColumn(
"List", ImGuiTableColumnFlags_WidthFixed, 120);
215 TableSetupColumn(
"Inputs", ImGuiTableColumnFlags_WidthStretch,
216 GetContentRegionAvail().x);
217 TableSetupColumn(
"Sheets", ImGuiTableColumnFlags_WidthFixed,
218 kSheetDisplayWidth + 16);
224 if (BeginChild(
"##RoomsetListChild", ImVec2(0, 300))) {
225 for (
int idx = 0; idx < 0x51; idx++) {
227 std::string roomset_label = absl::StrFormat(
"0x%02X", idx);
228 bool is_selected = (ws.selected_roomset ==
static_cast<uint8_t
>(idx));
229 if (Selectable(roomset_label.c_str(), is_selected)) {
230 ws.selected_roomset =
static_cast<uint8_t
>(idx);
240 Text(tr(
"Sheet IDs (overwrites slots 4-7):"));
241 for (
int idx = 0; idx < 4; idx++) {
244 (
"Slot " + std::to_string(idx + 4)).c_str(),
245 &
game_data()->room_blockset_ids[ws.selected_roomset][idx]);
252 for (
int idx = 0; idx < 4; idx++) {
269 (0x1000) | (ws.selected_roomset << 8) | (idx << 4) | sheet_id;
281 Text(tr(
"No game data loaded"));
285 PushID(
"SpritesetViewer");
288 if (BeginTable(
"##SpritesTable", sheet_only ? 1 : 2,
289 ImGuiTableFlags_Borders | ImGuiTableFlags_Resizable,
292 TableSetupColumn(
"Inputs", ImGuiTableColumnFlags_WidthStretch,
293 GetContentRegionAvail().x);
295 TableSetupColumn(
"Sheets", ImGuiTableColumnFlags_WidthFixed,
296 kSheetDisplayWidth + 16);
303 Text(tr(
"Sprite sheet IDs (base 115+):"));
304 for (
int idx = 0; idx < 4; idx++) {
307 (
"Slot " + std::to_string(idx)).c_str(),
308 &
game_data()->spriteset_ids[ws.selected_spriteset][idx]);
315 for (
int idx = 0; idx < 4; idx++) {
317 int sheet_id = 115 + sheet_offset;
333 (0x2000) | (ws.selected_spriteset << 8) | (idx << 4) | sheet_offset;
346 if (palette.
empty()) {
349 for (
size_t color_idx = 0; color_idx < palette.
size(); color_idx++) {
350 PushID(
static_cast<int>(color_idx));
351 if ((color_idx % 8) != 0) {
352 SameLine(0.0f, GetStyle().ItemSpacing.y);
357 ImGuiColorEditFlags_NoAlpha |
358 ImGuiColorEditFlags_NoPicker |
359 ImGuiColorEditFlags_NoTooltip);
377 SliderFloat(
"##ViewScale", &ws.view_scale, 1.0f, 4.0f,
"%.1fx");
386 static constexpr int kNumPaletteCategories = 14;
389 gfx::kPaletteCategoryNames[ws.selected_palette_category].data())) {
390 for (
int cat = 0; cat < kNumPaletteCategories; cat++) {
392 bool is_selected = (ws.selected_palette_category == category);
393 if (Selectable(gfx::kPaletteCategoryNames[category].data(),
395 ws.selected_palette_category = category;
396 ws.selected_palette_index = 0;
400 ImGui::SetItemDefaultFocus();
413 ImGui::Checkbox(tr(
"Override palette"), &ws.override_palette);
414 if (ImGui::IsItemHovered()) {
416 tr(
"When off, each sheet renders against its slot's role-default "
417 "palette (overworld main/aux/animated, sprites, hud, dungeon).\n"
418 "When on, the palette picked above is forced onto every sheet."));
434 const auto& ws =
Ws();
436 switch (ws.selected_palette_category) {
437 case PaletteCategory::kSword:
439 groups.swords.mutable_palette(ws.selected_palette_index);
441 case PaletteCategory::kShield:
443 groups.shields.mutable_palette(ws.selected_palette_index);
445 case PaletteCategory::kClothes:
447 groups.armors.mutable_palette(ws.selected_palette_index);
449 case PaletteCategory::kWorldColors:
451 groups.overworld_main.mutable_palette(ws.selected_palette_index);
453 case PaletteCategory::kAreaColors:
455 groups.overworld_aux.mutable_palette(ws.selected_palette_index);
457 case PaletteCategory::kGlobalSprites:
459 groups.global_sprites.mutable_palette(ws.selected_palette_index);
461 case PaletteCategory::kSpritesAux1:
463 groups.sprites_aux1.mutable_palette(ws.selected_palette_index);
465 case PaletteCategory::kSpritesAux2:
467 groups.sprites_aux2.mutable_palette(ws.selected_palette_index);
469 case PaletteCategory::kSpritesAux3:
471 groups.sprites_aux3.mutable_palette(ws.selected_palette_index);
473 case PaletteCategory::kDungeons:
475 groups.dungeon_main.mutable_palette(ws.selected_palette_index);
477 case PaletteCategory::kWorldMap:
478 case PaletteCategory::kDungeonMap:
480 groups.overworld_mini_map.mutable_palette(ws.selected_palette_index);
482 case PaletteCategory::kTriforce:
483 case PaletteCategory::kCrystal:
485 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)
CanvasConfig & GetConfig()
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
void EnsureSheetTextureQueued(gfx::Bitmap &sheet)
bool ApplyRoleDefaultPalette(gfx::Bitmap &sheet, gfx::SheetRole role, gfx::PaletteGroupMap &palette_groups)
PaletteCategory
Categories for organizing palette groups in the UI.
constexpr int kTilesheetHeight
constexpr int kTilesheetWidth
SheetRole RoleForRoomsetSlot(int slot_index)
SheetRole RoleForBlocksetSlot(int slot_index)
SheetRole RoleForSpritesetSlot(int slot_index)
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