6#include "absl/status/status.h"
68 if (current_graphics.empty()) {
72 constexpr int kCurrentGfxBitmapWidth = 0x80;
73 if (current_graphics.size() % kCurrentGfxBitmapWidth != 0) {
75 "Current graphics size %zu is not divisible by bitmap width %d",
76 current_graphics.size(), kCurrentGfxBitmapWidth);
80 const int bitmap_height =
81 static_cast<int>(current_graphics.size() / kCurrentGfxBitmapWidth);
82 const bool recreate_bitmap =
88 if (recreate_bitmap) {
131 PrepareMapForRefresh(
ctx_, map);
132 map->LoadAreaGraphics();
139 map->BuildBitmap(MapTilesForMapIndex(
ctx_.
overworld, map_index));
140 (*
ctx_.
maps_bmp)[map_index].set_data(map->bitmap_data());
167 if (!is_current_map && !is_current_world) {
188 bool needs_graphics_rebuild = (*
ctx_.
maps_bmp)[map_index].modified();
190 if (needs_graphics_rebuild) {
192 PrepareMapForRefresh(
ctx_, map);
193 map->LoadAreaGraphics();
196 auto status = map->BuildTileset();
199 "Failed to build tileset for map %d: %s", map_index,
200 status.message().data());
209 "Failed to build tiles16 graphics for map %d: %s", map_index,
210 status.message().data());
215 status = map->BuildBitmap(MapTilesForMapIndex(
ctx_.
overworld, map_index));
218 "Failed to build bitmap for map %d: %s", map_index,
219 status.message().data());
224 (*
ctx_.
maps_bmp)[map_index].set_data(map->bitmap_data());
228 if (!(*
ctx_.
maps_bmp)[map_index].ValidateDataSurfaceSync()) {
230 "Warning: Surface synchronization issue detected for map %d",
247 bool use_v3_area_sizes =
250 if (use_v3_area_sizes) {
255 if (map->is_large_map()) {
280 if (area_size == AreaSizeEnum::SmallArea) {
285 int parent_id = map->
parent();
291 "RefreshMultiAreaMapsSafely: Could not get parent map %d for "
293 parent_id, map_index);
298 "RefreshMultiAreaMapsSafely: Processing %s area from parent %d "
300 (area_size == AreaSizeEnum::LargeArea) ?
"large"
301 : (area_size == AreaSizeEnum::WideArea) ?
"wide"
303 parent_id, map_index);
307 std::vector<int> sibling_maps;
310 case AreaSizeEnum::LargeArea:
312 sibling_maps = {parent_id, parent_id + 1, parent_id + 8, parent_id + 9};
315 case AreaSizeEnum::WideArea:
317 sibling_maps = {parent_id, parent_id + 1};
320 case AreaSizeEnum::TallArea:
322 sibling_maps = {parent_id, parent_id + 8};
327 "RefreshMultiAreaMapsSafely: Unknown area size %d for map %d",
328 static_cast<int>(area_size), map_index);
335 for (
int sibling : sibling_maps) {
338 if (sibling == map_index) {
354 if (is_current_map || is_current_world) {
356 "RefreshMultiAreaMapsSafely: Refreshing sibling map %d",
364 PrepareMapForRefresh(
ctx_, sibling_map);
365 sibling_map->LoadAreaGraphics();
367 auto status = sibling_map->BuildTileset();
370 "Failed to build tileset for sibling %d: %s", sibling,
371 status.message().data());
379 "Failed to build tiles16 for sibling %d: %s", sibling,
380 status.message().data());
384 status = sibling_map->LoadPalette();
387 "Failed to load palette for sibling %d: %s", sibling,
388 status.message().data());
392 status = sibling_map->BuildBitmap(
396 "Failed to build bitmap for sibling %d: %s", sibling,
397 status.message().data());
402 (*
ctx_.
maps_bmp)[sibling].set_data(sibling_map->bitmap_data());
407 (*
ctx_.
maps_bmp)[sibling].SetPalette(sibling_map->current_palette());
426 const std::string& group,
int palette_index) {
427 if (group !=
"ow_main" && group !=
"ow_aux" && group !=
"ow_animated" &&
428 group !=
"grass" && group !=
"hud" && group !=
"global_sprites" &&
429 group !=
"sprites_aux1" && group !=
"sprites_aux2" &&
430 group !=
"sprites_aux3") {
431 return absl::OkStatus();
433 bool refreshed =
false;
436 if (!map || map->current_palette().empty()) {
439 if (group ==
"ow_main" && palette_index >= 0 &&
440 map->render_main_palette() != palette_index) {
445 if (bitmap.is_active() && bitmap.surface()) {
446 bitmap.SetPalette(map->current_palette());
463 return absl::FailedPreconditionError(
"Current overworld map not loaded");
465 PrepareMapForRefresh(
ctx_, current_map);
467 const auto current_map_palette = current_map->current_palette();
470 bool use_v3_area_sizes =
473 if (use_v3_area_sizes) {
476 auto area_size = current_map->area_size();
478 if (area_size != AreaSizeEnum::SmallArea) {
480 std::vector<int> sibling_maps;
481 int parent_id = current_map->parent();
484 case AreaSizeEnum::LargeArea:
486 sibling_maps = {parent_id, parent_id + 1, parent_id + 8,
489 case AreaSizeEnum::WideArea:
491 sibling_maps = {parent_id, parent_id + 1};
493 case AreaSizeEnum::TallArea:
495 sibling_maps = {parent_id, parent_id + 8};
502 for (
int sibling_index : sibling_maps) {
511 PrepareMapForRefresh(
ctx_, sibling_map);
514 sibling_map->current_palette());
518 (*
ctx_.
maps_bmp)[target_map].SetPalette(current_map_palette);
522 if (current_map->is_large_map()) {
524 for (
int i = 1; i < 4; i++) {
525 int sibling_index = current_map->parent() + i;
533 PrepareMapForRefresh(
ctx_, sibling_map);
541 sibling_map->current_palette());
549 (*
ctx_.
maps_bmp)[target_map].SetPalette(current_map_palette);
571 "ForceRefreshGraphics: Map %d marked for refresh", map_index);
586 int parent_id = map->parent();
587 std::vector<int> siblings;
589 switch (map->area_size()) {
591 siblings = {parent_id, parent_id + 1, parent_id + 8, parent_id + 9};
594 siblings = {parent_id, parent_id + 1};
597 siblings = {parent_id, parent_id + 8};
603 for (
int sibling : siblings) {
606 if (sibling == map_index && !include_self) {
618 PrepareMapForRefresh(
ctx_, sibling_map);
619 sibling_map->LoadAreaGraphics();
627 "RefreshSiblingMapGraphics: Refreshed sibling map %d", sibling);
641 const auto& current_ow_map = parent ? *parent : *selected;
647 bool use_v3_area_sizes =
650 if (use_v3_area_sizes) {
653 auto area_size = current_ow_map.area_size();
655 if (area_size != AreaSizeEnum::SmallArea) {
657 std::vector<int> sibling_maps;
658 int parent_id = current_ow_map.parent();
661 case AreaSizeEnum::LargeArea:
663 sibling_maps = {parent_id + 1, parent_id + 8, parent_id + 9};
665 case AreaSizeEnum::WideArea:
667 sibling_maps = {parent_id + 1};
669 case AreaSizeEnum::TallArea:
671 sibling_maps = {parent_id + 8};
678 const int game_state = CurrentGameState(
ctx_);
679 for (
int sibling_index : sibling_maps) {
684 map.set_area_graphics(current_ow_map.area_graphics());
685 map.set_area_palette(current_ow_map.area_palette());
686 map.set_sprite_graphics(game_state,
687 current_ow_map.sprite_graphics(game_state));
688 map.set_sprite_palette(game_state,
689 current_ow_map.sprite_palette(game_state));
692 PrepareMapForRefresh(
ctx_, &map);
693 map.LoadAreaGraphics();
698 if (current_ow_map.is_large_map()) {
700 const int game_state = CurrentGameState(
ctx_);
701 for (
int i = 1; i < 4; i++) {
702 int sibling_index = current_ow_map.parent() + i;
707 map.set_area_graphics(current_ow_map.area_graphics());
708 map.set_area_palette(current_ow_map.area_palette());
709 map.set_sprite_graphics(game_state,
710 current_ow_map.sprite_graphics(game_state));
711 map.set_sprite_palette(game_state,
712 current_ow_map.sprite_palette(game_state));
715 PrepareMapForRefresh(
ctx_, &map);
716 map.LoadAreaGraphics();
723 LOG_DEBUG(
"MapRefreshCoordinator",
"RefreshTile16Blockset called");
725 return absl::FailedPreconditionError(
"Current overworld map not loaded");
730 return absl::FailedPreconditionError(
"Current overworld map not loaded");
740 SyncCurrentGraphicsBitmapForTile16Editor(
ctx_, *current_map);
746 const auto& tile16_data = current_map->current_tile16_blockset();
763 return absl::OkStatus();
785 constexpr int kTilesPerRow = 8;
786 constexpr int kTileSize = 16;
790 bool atlas_modified =
false;
803 if (!pending_bmp || !pending_bmp->
is_active() ||
804 pending_bmp->
vector().empty()) {
809 int tile_x = (tile_id % kTilesPerRow) * kTileSize;
810 int tile_y = (tile_id / kTilesPerRow) * kTileSize;
813 if (tile_x + kTileSize > atlas_width || tile_y + kTileSize > atlas_height) {
819 const auto& pending_data = pending_bmp->
vector();
821 for (
int y = 0; y < kTileSize && y < pending_bmp->
height(); ++y) {
822 for (
int x = 0; x < kTileSize && x < pending_bmp->
width(); ++x) {
823 int atlas_idx = (tile_y + y) * atlas_width + (tile_x + x);
824 int pending_idx = y * pending_bmp->
width() + x;
826 if (atlas_idx >= 0 && atlas_idx <
static_cast<int>(atlas_data.size()) &&
828 pending_idx <
static_cast<int>(pending_data.size())) {
829 atlas_data[atlas_idx] = pending_data[pending_idx];
830 atlas_modified =
true;
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 RefreshPaletteUsers(const std::string &group, int palette_index)
void InvalidateTile16Definitions()
Shared Tile16 definitions affect maps in every world, including hidden maps.
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.
absl::Status RefreshMapPalette(int map_id=-1)
Refresh map palette after palette property changes.
void RefreshMapProperties(int map_id=-1)
Refresh map properties (copy parent properties to siblings)
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
bool has_pending_changes() const
bool is_tile_modified(int tile_id) const
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)
void SyncAreaProperties(int map_index)
Copy the area parent's render settings (main palette, animated GFX, tile GFX groups,...
void ClearGraphicsConfigCache()
Drop every cached tileset/blockset. Call after graphics buffer or tile16 edits that are not tied to o...
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.
void set_game_state(int state)
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::array< gfx::Bitmap, zelda3::kExpandedMapCount > * maps_bmp
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
gfx::Bitmap * current_gfx_bmp
bool * map_blockset_loaded
gfx::BitmapTable * current_graphics_set
Bitmap atlas
Master bitmap containing all tiles.