6#include "absl/status/status.h"
62 if (current_graphics.empty()) {
66 constexpr int kCurrentGfxBitmapWidth = 0x80;
67 if (current_graphics.size() % kCurrentGfxBitmapWidth != 0) {
69 "Current graphics size %zu is not divisible by bitmap width %d",
70 current_graphics.size(), kCurrentGfxBitmapWidth);
74 const int bitmap_height =
75 static_cast<int>(current_graphics.size() / kCurrentGfxBitmapWidth);
76 const bool recreate_bitmap =
82 if (recreate_bitmap) {
115 PrepareMapForRefresh(
ctx_, map);
116 map->LoadAreaGraphics();
123 map->BuildBitmap(MapTilesForMapIndex(
ctx_.
overworld, map_index));
124 (*
ctx_.
maps_bmp)[map_index].set_data(map->bitmap_data());
151 if (!is_current_map && !is_current_world) {
172 bool needs_graphics_rebuild = (*
ctx_.
maps_bmp)[map_index].modified();
174 if (needs_graphics_rebuild) {
176 PrepareMapForRefresh(
ctx_, map);
177 map->LoadAreaGraphics();
180 auto status = map->BuildTileset();
183 "Failed to build tileset for map %d: %s", map_index,
184 status.message().data());
193 "Failed to build tiles16 graphics for map %d: %s", map_index,
194 status.message().data());
199 status = map->BuildBitmap(MapTilesForMapIndex(
ctx_.
overworld, map_index));
202 "Failed to build bitmap for map %d: %s", map_index,
203 status.message().data());
208 (*
ctx_.
maps_bmp)[map_index].set_data(map->bitmap_data());
212 if (!(*
ctx_.
maps_bmp)[map_index].ValidateDataSurfaceSync()) {
214 "Warning: Surface synchronization issue detected for map %d",
231 bool use_v3_area_sizes =
234 if (use_v3_area_sizes) {
239 if (map->is_large_map()) {
264 if (area_size == AreaSizeEnum::SmallArea) {
269 int parent_id = map->
parent();
275 "RefreshMultiAreaMapsSafely: Could not get parent map %d for "
277 parent_id, map_index);
282 "RefreshMultiAreaMapsSafely: Processing %s area from parent %d "
284 (area_size == AreaSizeEnum::LargeArea) ?
"large"
285 : (area_size == AreaSizeEnum::WideArea) ?
"wide"
287 parent_id, map_index);
291 std::vector<int> sibling_maps;
294 case AreaSizeEnum::LargeArea:
296 sibling_maps = {parent_id, parent_id + 1, parent_id + 8, parent_id + 9};
299 case AreaSizeEnum::WideArea:
301 sibling_maps = {parent_id, parent_id + 1};
304 case AreaSizeEnum::TallArea:
306 sibling_maps = {parent_id, parent_id + 8};
311 "RefreshMultiAreaMapsSafely: Unknown area size %d for map %d",
312 static_cast<int>(area_size), map_index);
319 for (
int sibling : sibling_maps) {
322 if (sibling == map_index) {
338 if (is_current_map || is_current_world) {
340 "RefreshMultiAreaMapsSafely: Refreshing sibling map %d",
348 PrepareMapForRefresh(
ctx_, sibling_map);
349 sibling_map->LoadAreaGraphics();
351 auto status = sibling_map->BuildTileset();
354 "Failed to build tileset for sibling %d: %s", sibling,
355 status.message().data());
363 "Failed to build tiles16 for sibling %d: %s", sibling,
364 status.message().data());
368 status = sibling_map->LoadPalette();
371 "Failed to load palette for sibling %d: %s", sibling,
372 status.message().data());
376 status = sibling_map->BuildBitmap(
380 "Failed to build bitmap for sibling %d: %s", sibling,
381 status.message().data());
386 (*
ctx_.
maps_bmp)[sibling].set_data(sibling_map->bitmap_data());
391 (*
ctx_.
maps_bmp)[sibling].SetPalette(sibling_map->current_palette());
412 return absl::FailedPreconditionError(
"Current overworld map not loaded");
414 PrepareMapForRefresh(
ctx_, current_map);
416 const auto current_map_palette = current_map->current_palette();
426 bool use_v3_area_sizes =
429 if (use_v3_area_sizes) {
432 auto area_size = current_map->area_size();
434 if (area_size != AreaSizeEnum::SmallArea) {
436 std::vector<int> sibling_maps;
437 int parent_id = current_map->parent();
440 case AreaSizeEnum::LargeArea:
442 sibling_maps = {parent_id, parent_id + 1, parent_id + 8,
445 case AreaSizeEnum::WideArea:
447 sibling_maps = {parent_id, parent_id + 1};
449 case AreaSizeEnum::TallArea:
451 sibling_maps = {parent_id, parent_id + 8};
458 for (
int sibling_index : sibling_maps) {
467 PrepareMapForRefresh(
ctx_, sibling_map);
470 sibling_map->current_palette());
478 if (current_map->is_large_map()) {
480 for (
int i = 1; i < 4; i++) {
481 int sibling_index = current_map->parent() + i;
489 PrepareMapForRefresh(
ctx_, sibling_map);
497 sibling_map->current_palette());
509 return absl::OkStatus();
514 if (map_index >= 0 && map_index <
static_cast<int>(
ctx_.
maps_bmp->size())) {
525 "ForceRefreshGraphics: Map %d marked for refresh", map_index);
531 if (map_index < 0 || map_index >=
static_cast<int>(
ctx_.
maps_bmp->size())) {
540 int parent_id = map->parent();
541 std::vector<int> siblings;
543 switch (map->area_size()) {
545 siblings = {parent_id, parent_id + 1, parent_id + 8, parent_id + 9};
548 siblings = {parent_id, parent_id + 1};
551 siblings = {parent_id, parent_id + 8};
557 for (
int sibling : siblings) {
560 if (sibling == map_index && !include_self) {
572 PrepareMapForRefresh(
ctx_, sibling_map);
573 sibling_map->LoadAreaGraphics();
581 "RefreshSiblingMapGraphics: Refreshed sibling map %d", sibling);
587 const auto& current_ow_map =
592 bool use_v3_area_sizes =
595 if (use_v3_area_sizes) {
598 auto area_size = current_ow_map.area_size();
600 if (area_size != AreaSizeEnum::SmallArea) {
602 std::vector<int> sibling_maps;
603 int parent_id = current_ow_map.parent();
606 case AreaSizeEnum::LargeArea:
608 sibling_maps = {parent_id + 1, parent_id + 8, parent_id + 9};
610 case AreaSizeEnum::WideArea:
612 sibling_maps = {parent_id + 1};
614 case AreaSizeEnum::TallArea:
616 sibling_maps = {parent_id + 8};
623 const int game_state = CurrentGameState(
ctx_);
624 for (
int sibling_index : sibling_maps) {
629 map.set_area_graphics(current_ow_map.area_graphics());
630 map.set_area_palette(current_ow_map.area_palette());
631 map.set_sprite_graphics(game_state,
632 current_ow_map.sprite_graphics(game_state));
633 map.set_sprite_palette(game_state,
634 current_ow_map.sprite_palette(game_state));
635 map.set_message_id(current_ow_map.message_id());
638 PrepareMapForRefresh(
ctx_, &map);
639 map.LoadAreaGraphics();
644 if (current_ow_map.is_large_map()) {
646 const int game_state = CurrentGameState(
ctx_);
647 for (
int i = 1; i < 4; i++) {
648 int sibling_index = current_ow_map.parent() + i;
653 map.set_area_graphics(current_ow_map.area_graphics());
654 map.set_area_palette(current_ow_map.area_palette());
655 map.set_sprite_graphics(game_state,
656 current_ow_map.sprite_graphics(game_state));
657 map.set_sprite_palette(game_state,
658 current_ow_map.sprite_palette(game_state));
659 map.set_message_id(current_ow_map.message_id());
662 PrepareMapForRefresh(
ctx_, &map);
663 map.LoadAreaGraphics();
670 LOG_DEBUG(
"MapRefreshCoordinator",
"RefreshTile16Blockset called");
672 return absl::FailedPreconditionError(
"Current overworld map not loaded");
677 return absl::FailedPreconditionError(
"Current overworld map not loaded");
687 SyncCurrentGraphicsBitmapForTile16Editor(
ctx_, *current_map);
693 const auto& tile16_data = current_map->current_tile16_blockset();
710 return absl::OkStatus();
732 constexpr int kTilesPerRow = 8;
733 constexpr int kTileSize = 16;
737 bool atlas_modified =
false;
750 if (!pending_bmp || !pending_bmp->
is_active() ||
751 pending_bmp->
vector().empty()) {
756 int tile_x = (tile_id % kTilesPerRow) * kTileSize;
757 int tile_y = (tile_id / kTilesPerRow) * kTileSize;
760 if (tile_x + kTileSize > atlas_width || tile_y + kTileSize > atlas_height) {
766 const auto& pending_data = pending_bmp->
vector();
768 for (
int y = 0; y < kTileSize && y < pending_bmp->
height(); ++y) {
769 for (
int x = 0; x < kTileSize && x < pending_bmp->
width(); ++x) {
770 int atlas_idx = (tile_y + y) * atlas_width + (tile_x + x);
771 int pending_idx = y * pending_bmp->
width() + x;
773 if (atlas_idx >= 0 && atlas_idx <
static_cast<int>(atlas_data.size()) &&
775 pending_idx <
static_cast<int>(pending_data.size())) {
776 atlas_data[atlas_idx] = pending_data[pending_idx];
777 atlas_modified =
true;
void RefreshMapProperties()
Refresh map properties (copy parent properties to siblings)
void RefreshMultiAreaMapsSafely(int map_index, zelda3::OverworldMap *map)
Safely refresh multi-area maps without recursion.
void RefreshChildMapOnDemand(int map_index)
On-demand child map refresh with selective updates.
void RefreshChildMap(int map_index)
Refresh a child map's graphics pipeline (legacy full rebuild)
absl::Status RefreshMapPalette()
Refresh map palette after palette property changes.
void InvalidateGraphicsCache(int map_id=-1)
Invalidate cached graphics for a specific map or all maps.
void RefreshOverworldMap()
Refresh the current overworld map.
absl::Status RefreshTile16Blockset()
Refresh the tile16 blockset after graphics/palette changes.
void RefreshSiblingMapGraphics(int map_index, bool include_self=false)
Refresh sibling map graphics for multi-area maps.
void UpdateBlocksetWithPendingTileChanges()
Update blockset atlas with pending tile16 editor changes.
void RefreshOverworldMapOnDemand(int map_index)
On-demand map refresh that only updates what's actually needed.
void ForceRefreshGraphics(int map_index)
Force refresh graphics for a specific map.
const gfx::Bitmap * GetPendingTileBitmap(int tile_id) const
Get preview bitmap for a pending tile (nullptr if not modified)
bool has_pending_changes() const
Check if any tiles have uncommitted changes.
bool is_tile_modified(int tile_id) const
Check if a specific tile has pending changes.
void set_palette(const gfx::SnesPalette &palette)
void QueueTextureCommand(TextureCommandType type, Bitmap *bitmap)
Represents a bitmap image optimized for SNES ROM hacking.
void Create(int width, int height, int depth, std::span< uint8_t > data)
Create a bitmap with the given dimensions and data.
TextureHandle texture() const
const std::vector< uint8_t > & vector() const
void set_modified(bool modified)
void set_data(const std::vector< uint8_t > &data)
void SetPalette(const SnesPalette &palette)
Set the palette for the bitmap using SNES palette format.
std::vector< uint8_t > & mutable_data()
Represents a single Overworld map screen.
const std::vector< uint8_t > & current_graphics() const
void set_game_state(int state)
static OverworldVersion GetVersion(const Rom &rom)
Detect ROM version from ASM marker byte.
static bool SupportsAreaEnum(OverworldVersion version)
Check if ROM supports area enum system (v3+ only)
Represents the full Overworld data, light and dark world.
void ClearGraphicsConfigCache()
Clear entire graphics config cache Call when palette or graphics settings change globally.
void InvalidateSiblingMapCaches(int map_index)
Invalidate cached tilesets for a map and all its siblings.
const std::vector< gfx::Tile16 > & tiles16() const
auto overworld_map(int i) const
auto mutable_overworld_map(int i)
absl::Status EnsureMapBuilt(int map_index)
Build a map on-demand if it hasn't been built yet.
OverworldBlockset & GetMapTiles(int world_type)
#define LOG_DEBUG(category, format,...)
#define LOG_ERROR(category, format,...)
#define LOG_WARN(category, format,...)
#define PRINT_IF_ERROR(expression)
int CurrentGameState(const MapRefreshContext &ctx)
void SyncCurrentGraphicsBitmapForTile16Editor(const MapRefreshContext &ctx, const zelda3::OverworldMap &map)
zelda3::OverworldBlockset & MapTilesForMapIndex(zelda3::Overworld *overworld, int map_index)
void PrepareMapForRefresh(const MapRefreshContext &ctx, zelda3::OverworldMap *map)
bool IsMapInCurrentWorld(int map_index, int current_world)
int WorldForMapIndex(int map_index)
Editors are the view controllers for the application.
void UpdateTilemap(IRenderer *renderer, Tilemap &tilemap, const std::vector< uint8_t > &data)
constexpr int kNumTile16Individual
constexpr int kSpecialWorldMapIdStart
constexpr int kNumOverworldMaps
std::vector< std::vector< uint16_t > > OverworldBlockset
Represents tile32 data for the overworld.
AreaSizeEnum
Area size enumeration for v3+ ROMs.
constexpr int kDarkWorldMapIdStart
#define RETURN_IF_ERROR(expr)
Context struct holding all data dependencies for map refresh operations. All pointers/references must...
gfx::Tilemap * tile16_blockset
gfx::SnesPalette * palette
zelda3::Overworld * overworld
std::function< void(int map_index)> ensure_map_texture
Callback to ensure a map texture is created (stays in OverworldEditor)
gfx::IRenderer * renderer
Tile16Editor * tile16_editor
std::array< gfx::Bitmap, zelda3::kNumOverworldMaps > * maps_bmp
gfx::Bitmap * current_gfx_bmp
bool * map_blockset_loaded
gfx::BitmapTable * current_graphics_set
Bitmap atlas
Master bitmap containing all tiles.