feat(editor): enhance ScreenEditor with layer visibility controls and custom map loading/saving
- Added checkboxes for toggling visibility of title screen background layers (BG1 and BG2). - Implemented a new method for rendering the composite view of the title screen. - Introduced functionality for loading and saving custom maps from external binary files, enhancing user flexibility in map management. Benefits: - Improves user experience by allowing dynamic control over layer visibility during title screen editing. - Expands the capabilities of the ScreenEditor with custom map handling, facilitating easier map modifications and sharing.
This commit is contained in:
@@ -748,23 +748,35 @@ void ScreenEditor::DrawTitleScreenEditor() {
|
||||
ImGui::EndPopup();
|
||||
}
|
||||
|
||||
// Layout: 3-column table for layers
|
||||
if (ImGui::BeginTable("TitleScreenTable", 3,
|
||||
// Layer visibility controls
|
||||
bool prev_bg1 = show_title_bg1_;
|
||||
bool prev_bg2 = show_title_bg2_;
|
||||
ImGui::Checkbox("Show BG1", &show_title_bg1_);
|
||||
ImGui::SameLine();
|
||||
ImGui::Checkbox("Show BG2", &show_title_bg2_);
|
||||
|
||||
// Re-render composite if visibility changed
|
||||
if (prev_bg1 != show_title_bg1_ || prev_bg2 != show_title_bg2_) {
|
||||
status_ = title_screen_.RenderCompositeLayer(show_title_bg1_, show_title_bg2_);
|
||||
if (status_.ok()) {
|
||||
gfx::Arena::Get().QueueTextureCommand(
|
||||
gfx::Arena::TextureCommandType::UPDATE,
|
||||
&title_screen_.composite_bitmap());
|
||||
}
|
||||
}
|
||||
|
||||
// Layout: 2-column table (composite view + tile selector)
|
||||
if (ImGui::BeginTable("TitleScreenTable", 2,
|
||||
ImGuiTableFlags_Resizable | ImGuiTableFlags_Borders)) {
|
||||
ImGui::TableSetupColumn("BG1 Layer");
|
||||
ImGui::TableSetupColumn("BG2 Layer");
|
||||
ImGui::TableSetupColumn("Title Screen (Composite)");
|
||||
ImGui::TableSetupColumn("Tile Selector");
|
||||
ImGui::TableHeadersRow();
|
||||
|
||||
// Column 1: BG1 Canvas
|
||||
// Column 1: Composite Canvas (BG1+BG2 stacked)
|
||||
ImGui::TableNextColumn();
|
||||
DrawTitleScreenBG1Canvas();
|
||||
DrawTitleScreenCompositeCanvas();
|
||||
|
||||
// Column 2: BG2 Canvas
|
||||
ImGui::TableNextColumn();
|
||||
DrawTitleScreenBG2Canvas();
|
||||
|
||||
// Column 3: Blockset Selector
|
||||
// Column 2: Blockset Selector
|
||||
ImGui::TableNextColumn();
|
||||
DrawTitleScreenBlocksetSelector();
|
||||
|
||||
@@ -772,6 +784,58 @@ void ScreenEditor::DrawTitleScreenEditor() {
|
||||
}
|
||||
}
|
||||
|
||||
void ScreenEditor::DrawTitleScreenCompositeCanvas() {
|
||||
title_bg1_canvas_.DrawBackground();
|
||||
title_bg1_canvas_.DrawContextMenu();
|
||||
|
||||
// Draw composite tilemap (BG1+BG2 stacked with transparency)
|
||||
auto& composite_bitmap = title_screen_.composite_bitmap();
|
||||
if (composite_bitmap.is_active()) {
|
||||
title_bg1_canvas_.DrawBitmap(composite_bitmap, 0, 0, 2.0f, 255);
|
||||
}
|
||||
|
||||
// Handle tile painting - always paint to BG1 layer
|
||||
if (current_mode_ == EditingMode::DRAW && selected_title_tile16_ >= 0) {
|
||||
if (title_bg1_canvas_.DrawTileSelector(8.0f)) {
|
||||
if (!title_bg1_canvas_.points().empty()) {
|
||||
auto click_pos = title_bg1_canvas_.points().front();
|
||||
int tile_x = static_cast<int>(click_pos.x) / 8;
|
||||
int tile_y = static_cast<int>(click_pos.y) / 8;
|
||||
|
||||
if (tile_x >= 0 && tile_x < 32 && tile_y >= 0 && tile_y < 32) {
|
||||
int tilemap_index = tile_y * 32 + tile_x;
|
||||
|
||||
// Create tile word: tile_id | (palette << 10) | h_flip | v_flip
|
||||
uint16_t tile_word = selected_title_tile16_ & 0x3FF;
|
||||
tile_word |= (title_palette_ & 0x07) << 10;
|
||||
if (title_h_flip_) tile_word |= 0x4000;
|
||||
if (title_v_flip_) tile_word |= 0x8000;
|
||||
|
||||
// Update BG1 buffer and re-render both layers and composite
|
||||
title_screen_.mutable_bg1_buffer()[tilemap_index] = tile_word;
|
||||
status_ = title_screen_.RenderBG1Layer();
|
||||
if (status_.ok()) {
|
||||
// Update BG1 texture
|
||||
gfx::Arena::Get().QueueTextureCommand(
|
||||
gfx::Arena::TextureCommandType::UPDATE,
|
||||
&title_screen_.bg1_bitmap());
|
||||
|
||||
// Re-render and update composite
|
||||
status_ = title_screen_.RenderCompositeLayer(show_title_bg1_, show_title_bg2_);
|
||||
if (status_.ok()) {
|
||||
gfx::Arena::Get().QueueTextureCommand(
|
||||
gfx::Arena::TextureCommandType::UPDATE, &composite_bitmap);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
title_bg1_canvas_.DrawGrid();
|
||||
title_bg1_canvas_.DrawOverlay();
|
||||
}
|
||||
|
||||
void ScreenEditor::DrawTitleScreenBG1Canvas() {
|
||||
title_bg1_canvas_.DrawBackground();
|
||||
title_bg1_canvas_.DrawContextMenu();
|
||||
@@ -941,8 +1005,41 @@ void ScreenEditor::DrawOverworldMapEditor() {
|
||||
gfx::Arena::TextureCommandType::UPDATE, &ow_map_screen_.map_bitmap());
|
||||
}
|
||||
}
|
||||
ImGui::SameLine();
|
||||
|
||||
// Custom map load/save buttons
|
||||
if (ImGui::Button("Load Custom Map...")) {
|
||||
std::string path = util::FileDialogWrapper::ShowOpenFileDialog();
|
||||
if (!path.empty()) {
|
||||
status_ = ow_map_screen_.LoadCustomMap(path);
|
||||
if (!status_.ok()) {
|
||||
ImGui::OpenPopup("CustomMapLoadError");
|
||||
}
|
||||
}
|
||||
}
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Button("Save Custom Map...")) {
|
||||
std::string path = util::FileDialogWrapper::ShowSaveFileDialog();
|
||||
if (!path.empty()) {
|
||||
status_ = ow_map_screen_.SaveCustomMap(path, ow_show_dark_world_);
|
||||
if (status_.ok()) {
|
||||
ImGui::OpenPopup("CustomMapSaveSuccess");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ImGui::SameLine();
|
||||
ImGui::Text("Selected Tile: %d", selected_ow_tile_);
|
||||
|
||||
// Custom map error/success popups
|
||||
if (ImGui::BeginPopup("CustomMapLoadError")) {
|
||||
ImGui::Text("Error loading custom map: %s", status_.message().data());
|
||||
ImGui::EndPopup();
|
||||
}
|
||||
if (ImGui::BeginPopup("CustomMapSaveSuccess")) {
|
||||
ImGui::Text("Custom map saved successfully!");
|
||||
ImGui::EndPopup();
|
||||
}
|
||||
|
||||
// Save success popup
|
||||
if (ImGui::BeginPopup("OWSaveSuccess")) {
|
||||
|
||||
@@ -67,6 +67,7 @@ class ScreenEditor : public Editor {
|
||||
void DrawInventoryToolset();
|
||||
|
||||
// Title screen layer editing
|
||||
void DrawTitleScreenCompositeCanvas();
|
||||
void DrawTitleScreenBG1Canvas();
|
||||
void DrawTitleScreenBG2Canvas();
|
||||
void DrawTitleScreenBlocksetSelector();
|
||||
@@ -134,6 +135,8 @@ class ScreenEditor : public Editor {
|
||||
bool title_h_flip_ = false;
|
||||
bool title_v_flip_ = false;
|
||||
int title_palette_ = 0;
|
||||
bool show_title_bg1_ = true;
|
||||
bool show_title_bg2_ = true;
|
||||
|
||||
// Overworld map screen state
|
||||
int selected_ow_tile_ = 0;
|
||||
|
||||
@@ -1,8 +1,11 @@
|
||||
#include "overworld_map_screen.h"
|
||||
#include "zelda3/screen/overworld_map_screen.h"
|
||||
|
||||
#include <fstream>
|
||||
|
||||
#include "app/gfx/resource/arena.h"
|
||||
#include "app/gfx/types/snes_color.h"
|
||||
#include "util/macro.h"
|
||||
#include "app/rom.h"
|
||||
#include "app/snes.h"
|
||||
|
||||
namespace yaze {
|
||||
namespace zelda3 {
|
||||
@@ -12,44 +15,47 @@ absl::Status OverworldMapScreen::Create(Rom* rom) {
|
||||
return absl::InvalidArgumentError("ROM is not loaded");
|
||||
}
|
||||
|
||||
// Initialize bitmaps
|
||||
tiles8_bitmap_.Create(128, 128, 8, std::vector<uint8_t>(128 * 128));
|
||||
map_bitmap_.Create(512, 512, 8, std::vector<uint8_t>(512 * 512));
|
||||
|
||||
// Set metadata for overworld map bitmaps
|
||||
// Mode 7 graphics use full 128-color palettes
|
||||
tiles8_bitmap_.metadata().source_bpp = 8;
|
||||
tiles8_bitmap_.metadata().palette_format = 0; // Full palette
|
||||
tiles8_bitmap_.metadata().source_type = "mode7";
|
||||
tiles8_bitmap_.metadata().palette_colors = 128;
|
||||
|
||||
map_bitmap_.metadata().source_bpp = 8;
|
||||
map_bitmap_.metadata().palette_format = 0; // Full palette
|
||||
map_bitmap_.metadata().source_type = "mode7";
|
||||
map_bitmap_.metadata().palette_colors = 128;
|
||||
|
||||
// Load mode 7 graphics from 0x0C4000
|
||||
// Load Mode 7 graphics (256 tiles, 8x8 pixels each, 8BPP)
|
||||
const int mode7_gfx_addr = 0x0C4000;
|
||||
auto& tiles8_data = tiles8_bitmap_.mutable_data();
|
||||
std::vector<uint8_t> mode7_gfx_raw(0x4000); // Raw tileset data from ROM
|
||||
|
||||
// Mode 7 graphics are stored as 8x8 tiles, 16 tiles per row
|
||||
for (int i = 0; i < 0x4000; i++) {
|
||||
ASSIGN_OR_RETURN(mode7_gfx_raw[i], rom->ReadByte(mode7_gfx_addr + i));
|
||||
}
|
||||
|
||||
// Mode 7 tiles are stored in tiled format (each tile's rows are consecutive)
|
||||
// but we need linear bitmap format (all tiles' first rows, then all second rows)
|
||||
// Convert from tiled to linear bitmap layout
|
||||
std::vector<uint8_t> mode7_gfx(0x4000);
|
||||
int pos = 0;
|
||||
for (int sy = 0; sy < 16; sy++) {
|
||||
for (int sx = 0; sx < 16; sx++) {
|
||||
for (int y = 0; y < 8; y++) {
|
||||
for (int x = 0; x < 8; x++) {
|
||||
int dest_index = x + (sx * 8) + (y * 128) + (sy * 1024);
|
||||
if (dest_index < tiles8_data.size() && mode7_gfx_addr + pos < rom->size()) {
|
||||
ASSIGN_OR_RETURN(uint8_t pixel, rom->ReadByte(mode7_gfx_addr + pos));
|
||||
tiles8_data[dest_index] = pixel;
|
||||
}
|
||||
for (int sy = 0; sy < 16 * 1024; sy += 1024) { // 16 rows of tiles
|
||||
for (int sx = 0; sx < 16 * 8; sx += 8) { // 16 columns of tiles
|
||||
for (int y = 0; y < 8 * 128; y += 128) { // 8 pixel rows within tile
|
||||
for (int x = 0; x < 8; x++) { // 8 pixels per row
|
||||
mode7_gfx[x + sx + y + sy] = mode7_gfx_raw[pos];
|
||||
pos++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Load palettes (128 colors each for mode 7 graphics)
|
||||
// Create tiles8 bitmap: 128×128 pixels (16×16 tiles = 256 tiles)
|
||||
tiles8_bitmap_.Create(128, 128, 8, mode7_gfx);
|
||||
tiles8_bitmap_.metadata().source_bpp = 8;
|
||||
tiles8_bitmap_.metadata().palette_format = 0;
|
||||
tiles8_bitmap_.metadata().source_type = "mode7_tileset";
|
||||
tiles8_bitmap_.metadata().palette_colors = 128;
|
||||
|
||||
// Create map bitmap (512x512 for 64x64 tiles at 8x8 each)
|
||||
map_bitmap_.Create(512, 512, 8, std::vector<uint8_t>(512 * 512));
|
||||
map_bitmap_.metadata().source_bpp = 8;
|
||||
map_bitmap_.metadata().palette_format = 0;
|
||||
map_bitmap_.metadata().source_type = "mode7_map";
|
||||
map_bitmap_.metadata().palette_colors = 128;
|
||||
|
||||
// Light World palette at 0x055B27
|
||||
const int lw_pal_addr = 0x055B27;
|
||||
for (int i = 0; i < 128; i++) {
|
||||
@@ -90,31 +96,31 @@ absl::Status OverworldMapScreen::Create(Rom* rom) {
|
||||
}
|
||||
|
||||
absl::Status OverworldMapScreen::LoadMapData(Rom* rom) {
|
||||
// Map data is stored in 4 sections with interleaved left/right format
|
||||
// Based on ZScream's implementation in ScreenEditor.cs lines 221-322
|
||||
// Map data is stored in interleaved format across 4 sections + 1 DW section
|
||||
// Based on ZScream's Constants.IDKZarby = 0x054727
|
||||
// The data alternates between left (32 columns) and right (32 columns)
|
||||
// for the first 2048 tiles, then continues for bottom half
|
||||
|
||||
const int p1_addr = 0x0564F8; // First section (left)
|
||||
const int p2_addr = 0x05634C; // First section (right)
|
||||
const int p3_addr = 0x056BF8; // Second section (left)
|
||||
const int p4_addr = 0x056A4C; // Second section (right)
|
||||
const int p5_addr = 0x057404; // Dark World additional section
|
||||
|
||||
int count = 0;
|
||||
int cSide = 0;
|
||||
bool rSide = false;
|
||||
|
||||
// Load Light World and Dark World base data
|
||||
while (count < 0x1000) {
|
||||
int p1 = p1_addr + (count - (rSide ? 1 : 0));
|
||||
int p2 = p2_addr + (count - (rSide ? 1 : 0));
|
||||
int p3 = p3_addr + (count - (rSide ? 1 : 0) - 0x800);
|
||||
int p4 = p4_addr + (count - (rSide ? 1 : 0) - 0x800);
|
||||
|
||||
if (count < 0x800) {
|
||||
const int base_addr = 0x054727; // IDKZarby constant from ZScream
|
||||
int p1 = base_addr + 0x0000; // Top-left quadrant data
|
||||
int p2 = base_addr + 0x0400; // Top-right quadrant data
|
||||
int p3 = base_addr + 0x0800; // Bottom-left quadrant data
|
||||
int p4 = base_addr + 0x0C00; // Bottom-right quadrant data
|
||||
int p5 = base_addr + 0x1000; // Dark World additional section
|
||||
|
||||
bool rSide = false; // false = left side, true = right side
|
||||
int cSide = 0; // Column counter within side (0-31)
|
||||
int count = 0; // Output tile index
|
||||
|
||||
// Load 64x64 map with interleaved left/right format
|
||||
while (count < 64 * 64) {
|
||||
if (count < 0x800) { // Top half (first 2048 tiles)
|
||||
if (!rSide) {
|
||||
// Read from left side (p1)
|
||||
ASSIGN_OR_RETURN(uint8_t tile, rom->ReadByte(p1));
|
||||
lw_map_tiles_[count] = tile;
|
||||
dw_map_tiles_[count] = tile;
|
||||
p1++;
|
||||
|
||||
if (cSide >= 31) {
|
||||
cSide = 0;
|
||||
@@ -123,9 +129,11 @@ absl::Status OverworldMapScreen::LoadMapData(Rom* rom) {
|
||||
continue;
|
||||
}
|
||||
} else {
|
||||
// Read from right side (p2)
|
||||
ASSIGN_OR_RETURN(uint8_t tile, rom->ReadByte(p2));
|
||||
lw_map_tiles_[count] = tile;
|
||||
dw_map_tiles_[count] = tile;
|
||||
p2++;
|
||||
|
||||
if (cSide >= 31) {
|
||||
cSide = 0;
|
||||
@@ -134,11 +142,13 @@ absl::Status OverworldMapScreen::LoadMapData(Rom* rom) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
} else { // Bottom half (remaining 2048 tiles)
|
||||
if (!rSide) {
|
||||
// Read from left side (p3)
|
||||
ASSIGN_OR_RETURN(uint8_t tile, rom->ReadByte(p3));
|
||||
lw_map_tiles_[count] = tile;
|
||||
dw_map_tiles_[count] = tile;
|
||||
p3++;
|
||||
|
||||
if (cSide >= 31) {
|
||||
cSide = 0;
|
||||
@@ -147,9 +157,11 @@ absl::Status OverworldMapScreen::LoadMapData(Rom* rom) {
|
||||
continue;
|
||||
}
|
||||
} else {
|
||||
// Read from right side (p4)
|
||||
ASSIGN_OR_RETURN(uint8_t tile, rom->ReadByte(p4));
|
||||
lw_map_tiles_[count] = tile;
|
||||
dw_map_tiles_[count] = tile;
|
||||
p4++;
|
||||
|
||||
if (cSide >= 31) {
|
||||
cSide = 0;
|
||||
@@ -159,24 +171,18 @@ absl::Status OverworldMapScreen::LoadMapData(Rom* rom) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
cSide++;
|
||||
count++;
|
||||
}
|
||||
|
||||
// Load Dark World specific section (bottom-right 32x32 area)
|
||||
|
||||
// Load Dark World specific data (bottom-right 32x32 section)
|
||||
count = 0;
|
||||
int line = 0;
|
||||
while (true) {
|
||||
int addr = p5_addr + count + (line * 32);
|
||||
if (addr < rom->size()) {
|
||||
ASSIGN_OR_RETURN(uint8_t tile, rom->ReadByte(addr));
|
||||
int dest_index = 1040 + count + (line * 64);
|
||||
if (dest_index < dw_map_tiles_.size()) {
|
||||
dw_map_tiles_[dest_index] = tile;
|
||||
}
|
||||
}
|
||||
|
||||
ASSIGN_OR_RETURN(uint8_t tile, rom->ReadByte(p5));
|
||||
dw_map_tiles_[1040 + count + (line * 64)] = tile;
|
||||
p5++;
|
||||
count++;
|
||||
if (count >= 32) {
|
||||
count = 0;
|
||||
@@ -217,42 +223,32 @@ absl::Status OverworldMapScreen::RenderMapLayer(bool use_dark_world) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Update surface with rendered pixel data
|
||||
map_bitmap_.UpdateSurfacePixels();
|
||||
|
||||
// Apply appropriate palette
|
||||
map_bitmap_.SetPalette(use_dark_world ? dw_palette_ : lw_palette_);
|
||||
// Copy pixel data to SDL surface
|
||||
map_bitmap_.UpdateSurfacePixels();
|
||||
|
||||
return absl::OkStatus();
|
||||
}
|
||||
|
||||
absl::Status OverworldMapScreen::Save(Rom* rom) {
|
||||
if (!rom || !rom->is_loaded()) {
|
||||
return absl::InvalidArgumentError("ROM is not loaded");
|
||||
}
|
||||
|
||||
// Save data back in the same interleaved format
|
||||
const int p1_addr = 0x0564F8;
|
||||
const int p2_addr = 0x05634C;
|
||||
const int p3_addr = 0x056BF8;
|
||||
const int p4_addr = 0x056A4C;
|
||||
const int p5_addr = 0x057404;
|
||||
|
||||
int count = 0;
|
||||
int cSide = 0;
|
||||
// Write data back in the same interleaved format
|
||||
const int base_addr = 0x054727;
|
||||
int p1 = base_addr + 0x0000;
|
||||
int p2 = base_addr + 0x0400;
|
||||
int p3 = base_addr + 0x0800;
|
||||
int p4 = base_addr + 0x0C00;
|
||||
int p5 = base_addr + 0x1000;
|
||||
|
||||
bool rSide = false;
|
||||
|
||||
// Save Light World data (same pattern as loading)
|
||||
while (count < 0x1000) {
|
||||
int p1 = p1_addr + (count - (rSide ? 1 : 0));
|
||||
int p2 = p2_addr + (count - (rSide ? 1 : 0));
|
||||
int p3 = p3_addr + (count - (rSide ? 1 : 0) - 0x800);
|
||||
int p4 = p4_addr + (count - (rSide ? 1 : 0) - 0x800);
|
||||
|
||||
int cSide = 0;
|
||||
int count = 0;
|
||||
|
||||
// Write 64x64 map with interleaved left/right format
|
||||
while (count < 64 * 64) {
|
||||
if (count < 0x800) {
|
||||
if (!rSide) {
|
||||
RETURN_IF_ERROR(rom->WriteByte(p1, lw_map_tiles_[count]));
|
||||
p1++;
|
||||
|
||||
if (cSide >= 31) {
|
||||
cSide = 0;
|
||||
@@ -262,6 +258,7 @@ absl::Status OverworldMapScreen::Save(Rom* rom) {
|
||||
}
|
||||
} else {
|
||||
RETURN_IF_ERROR(rom->WriteByte(p2, lw_map_tiles_[count]));
|
||||
p2++;
|
||||
|
||||
if (cSide >= 31) {
|
||||
cSide = 0;
|
||||
@@ -273,6 +270,7 @@ absl::Status OverworldMapScreen::Save(Rom* rom) {
|
||||
} else {
|
||||
if (!rSide) {
|
||||
RETURN_IF_ERROR(rom->WriteByte(p3, lw_map_tiles_[count]));
|
||||
p3++;
|
||||
|
||||
if (cSide >= 31) {
|
||||
cSide = 0;
|
||||
@@ -282,6 +280,7 @@ absl::Status OverworldMapScreen::Save(Rom* rom) {
|
||||
}
|
||||
} else {
|
||||
RETURN_IF_ERROR(rom->WriteByte(p4, lw_map_tiles_[count]));
|
||||
p4++;
|
||||
|
||||
if (cSide >= 31) {
|
||||
cSide = 0;
|
||||
@@ -291,22 +290,17 @@ absl::Status OverworldMapScreen::Save(Rom* rom) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
cSide++;
|
||||
count++;
|
||||
}
|
||||
|
||||
// Save Dark World specific section
|
||||
|
||||
// Write Dark World specific data
|
||||
count = 0;
|
||||
int line = 0;
|
||||
while (true) {
|
||||
int addr = p5_addr + count + (line * 32);
|
||||
int src_index = 1040 + count + (line * 64);
|
||||
|
||||
if (src_index < dw_map_tiles_.size()) {
|
||||
RETURN_IF_ERROR(rom->WriteByte(addr, dw_map_tiles_[src_index]));
|
||||
}
|
||||
|
||||
RETURN_IF_ERROR(rom->WriteByte(p5, dw_map_tiles_[1040 + count + (line * 64)]));
|
||||
p5++;
|
||||
count++;
|
||||
if (count >= 32) {
|
||||
count = 0;
|
||||
@@ -320,6 +314,52 @@ absl::Status OverworldMapScreen::Save(Rom* rom) {
|
||||
return absl::OkStatus();
|
||||
}
|
||||
|
||||
absl::Status OverworldMapScreen::LoadCustomMap(const std::string& file_path) {
|
||||
// Load custom map from external binary file
|
||||
std::ifstream file(file_path, std::ios::binary | std::ios::ate);
|
||||
if (!file.is_open()) {
|
||||
return absl::NotFoundError("Could not open custom map file: " + file_path);
|
||||
}
|
||||
|
||||
std::streamsize size = file.tellg();
|
||||
if (size != 4096) {
|
||||
return absl::InvalidArgumentError(
|
||||
"Custom map file must be exactly 4096 bytes (64×64 tiles)");
|
||||
}
|
||||
|
||||
file.seekg(0, std::ios::beg);
|
||||
|
||||
// Read into Light World map buffer (could add option for Dark World later)
|
||||
file.read(reinterpret_cast<char*>(lw_map_tiles_.data()), 4096);
|
||||
|
||||
if (!file) {
|
||||
return absl::InternalError("Failed to read custom map data");
|
||||
}
|
||||
|
||||
// Re-render with new data
|
||||
RETURN_IF_ERROR(RenderMapLayer(false));
|
||||
gfx::Arena::Get().QueueTextureCommand(
|
||||
gfx::Arena::TextureCommandType::UPDATE, &map_bitmap_);
|
||||
|
||||
return absl::OkStatus();
|
||||
}
|
||||
|
||||
absl::Status OverworldMapScreen::SaveCustomMap(const std::string& file_path,
|
||||
bool use_dark_world) {
|
||||
std::ofstream file(file_path, std::ios::binary);
|
||||
if (!file.is_open()) {
|
||||
return absl::InternalError("Could not create custom map file: " + file_path);
|
||||
}
|
||||
|
||||
const auto& tiles = use_dark_world ? dw_map_tiles_ : lw_map_tiles_;
|
||||
file.write(reinterpret_cast<const char*>(tiles.data()), tiles.size());
|
||||
|
||||
if (!file) {
|
||||
return absl::InternalError("Failed to write custom map data");
|
||||
}
|
||||
|
||||
return absl::OkStatus();
|
||||
}
|
||||
|
||||
} // namespace zelda3
|
||||
} // namespace yaze
|
||||
|
||||
|
||||
@@ -57,6 +57,19 @@ class OverworldMapScreen {
|
||||
*/
|
||||
absl::Status RenderMapLayer(bool use_dark_world);
|
||||
|
||||
/**
|
||||
* @brief Load custom map from external binary file
|
||||
* @param file_path Path to .bin file containing 64×64 tile indices
|
||||
*/
|
||||
absl::Status LoadCustomMap(const std::string& file_path);
|
||||
|
||||
/**
|
||||
* @brief Save map data to external binary file
|
||||
* @param file_path Path to output .bin file
|
||||
* @param use_dark_world If true, save DW tiles, otherwise LW tiles
|
||||
*/
|
||||
absl::Status SaveCustomMap(const std::string& file_path, bool use_dark_world);
|
||||
|
||||
private:
|
||||
/**
|
||||
* @brief Load map tile data from ROM
|
||||
|
||||
Reference in New Issue
Block a user