From 77ceb0256b197897df6d8fdf3f5879069f8dca27 Mon Sep 17 00:00:00 2001 From: scawful Date: Thu, 25 Sep 2025 12:00:03 -0400 Subject: [PATCH] Refactor CMake and enhance API documentation for YAZE - Updated CMakeLists.txt to set a minimum required version of 3.5 and removed older policy settings for improved compatibility. - Enhanced yaze.h and zelda.h with detailed documentation, including versioning information and API descriptions, to improve clarity for developers. - Added new functions for version compatibility checks and improved error handling in the YAZE API. - Refactored existing structures and enums for better readability and maintainability, ensuring a more consistent coding style. --- CMakeLists.txt | 10 +- incl/yaze.h | 545 ++++++++++++++++++--- incl/zelda.h | 493 +++++++++++++++---- src/app/editor/overworld/map_properties.cc | 8 +- src/app/gui/color.cc | 6 +- src/yaze.cc | 315 +++++++++--- 6 files changed, 1139 insertions(+), 238 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 597c9ca5..85b96c5c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,14 +1,8 @@ # Yet Another Zelda3 Editor # by scawful -cmake_minimum_required(VERSION 3.16) +cmake_minimum_required(VERSION 3.5) + -# Set policy for older submodules -if(POLICY CMP0077) - cmake_policy(SET CMP0077 NEW) -endif() -if(POLICY CMP0135) - cmake_policy(SET CMP0135 NEW) -endif() project(yaze VERSION 0.3.0 DESCRIPTION "Yet Another Zelda3 Editor" LANGUAGES CXX C) diff --git a/incl/yaze.h b/incl/yaze.h index 618bf62d..ef7a093e 100644 --- a/incl/yaze.h +++ b/incl/yaze.h @@ -1,6 +1,18 @@ #ifndef YAZE_H #define YAZE_H +/** + * @file yaze.h + * @brief Yet Another Zelda3 Editor (YAZE) - Public C API + * + * This header provides the main C API for YAZE, a modern ROM editor for + * The Legend of Zelda: A Link to the Past. This API allows external + * applications to interact with YAZE's functionality. + * + * @version 0.3.0 + * @author YAZE Team + */ + #ifdef __cplusplus extern "C" { #endif @@ -11,56 +23,290 @@ extern "C" { #include "zelda.h" +/** + * @defgroup version Version Information + * @{ + */ + +/** Major version number */ +#define YAZE_VERSION_MAJOR 0 +/** Minor version number */ +#define YAZE_VERSION_MINOR 3 +/** Patch version number */ +#define YAZE_VERSION_PATCH 0 + +/** Combined version as a string */ +#define YAZE_VERSION_STRING "0.3.0" + +/** Combined version as a number (major * 10000 + minor * 100 + patch) */ +#define YAZE_VERSION_NUMBER 300 + +/** @} */ + typedef struct yaze_editor_context { zelda3_rom* rom; const char* error_message; } yaze_editor_context; +/** + * @defgroup core Core API + * @{ + */ + +/** + * @brief Status codes returned by YAZE functions + * + * All YAZE functions that can fail return a status code to indicate + * success or the type of error that occurred. + */ typedef enum yaze_status { - YAZE_UNKNOWN = -1, - YAZE_OK = 0, - YAZE_ERROR = 1, + YAZE_OK = 0, /**< Operation completed successfully */ + YAZE_ERROR_UNKNOWN = -1, /**< Unknown error occurred */ + YAZE_ERROR_INVALID_ARG = 1, /**< Invalid argument provided */ + YAZE_ERROR_FILE_NOT_FOUND = 2, /**< File not found */ + YAZE_ERROR_MEMORY = 3, /**< Memory allocation failed */ + YAZE_ERROR_IO = 4, /**< I/O operation failed */ + YAZE_ERROR_CORRUPTION = 5, /**< Data corruption detected */ + YAZE_ERROR_NOT_INITIALIZED = 6, /**< Component not initialized */ } yaze_status; +/** + * @brief Convert a status code to a human-readable string + * + * @param status The status code to convert + * @return A null-terminated string describing the status + */ +const char* yaze_status_to_string(yaze_status status); + +/** + * @brief Initialize the YAZE library + * + * This function must be called before using any other YAZE functions. + * It initializes internal subsystems and prepares the library for use. + * + * @return YAZE_OK on success, error code on failure + */ +yaze_status yaze_library_init(void); + +/** + * @brief Shutdown the YAZE library + * + * This function cleans up resources allocated by yaze_library_init(). + * After calling this function, no other YAZE functions should be called + * until yaze_library_init() is called again. + */ +void yaze_library_shutdown(void); + +/** + * @brief Main entry point for the YAZE application + * + * @param argc Number of command line arguments + * @param argv Array of command line argument strings + * @return Exit code (0 for success, non-zero for error) + */ int yaze_app_main(int argc, char** argv); -void yaze_check_version(const char* version); -yaze_status yaze_init(yaze_editor_context*, char* rom_filename); -yaze_status yaze_shutdown(yaze_editor_context*); +/** + * @brief Check if the current YAZE version is compatible with the expected version + * + * @param expected_version Expected version string (e.g., "0.3.0") + * @return true if compatible, false otherwise + */ +bool yaze_check_version_compatibility(const char* expected_version); +/** + * @brief Get the current YAZE version string + * + * @return A null-terminated string containing the version + */ +const char* yaze_get_version_string(void); + +/** + * @brief Get the current YAZE version number + * + * @return Version number (major * 10000 + minor * 100 + patch) + */ +int yaze_get_version_number(void); + +/** + * @brief Initialize a YAZE editor context + * + * Creates and initializes an editor context for working with ROM files. + * The context manages the ROM data and provides access to editing functions. + * + * @param context Pointer to context structure to initialize + * @param rom_filename Path to the ROM file to load (can be NULL to create empty context) + * @return YAZE_OK on success, error code on failure + * + * @note The caller is responsible for calling yaze_shutdown() to clean up the context + */ +yaze_status yaze_init(yaze_editor_context* context, const char* rom_filename); + +/** + * @brief Shutdown and clean up a YAZE editor context + * + * Releases all resources associated with the context, including ROM data. + * After calling this function, the context should not be used. + * + * @param context Pointer to context to shutdown + * @return YAZE_OK on success, error code on failure + */ +yaze_status yaze_shutdown(yaze_editor_context* context); + +/** @} */ + +/** + * @defgroup graphics Graphics and Bitmap Functions + * @{ + */ + +/** + * @brief Bitmap data structure + * + * Represents a bitmap image with pixel data and metadata. + */ typedef struct yaze_bitmap { - int width; - int height; - uint8_t bpp; - uint8_t* data; + int width; /**< Width in pixels */ + int height; /**< Height in pixels */ + uint8_t bpp; /**< Bits per pixel (1, 2, 4, 8) */ + uint8_t* data; /**< Pixel data (caller owns memory) */ } yaze_bitmap; +/** + * @brief Load a bitmap from file + * + * Loads a bitmap image from the specified file. Supports common + * image formats and SNES-specific formats. + * + * @param filename Path to the image file + * @return Bitmap structure with loaded data, or empty bitmap on error + * + * @note The caller is responsible for freeing the data pointer + */ yaze_bitmap yaze_load_bitmap(const char* filename); /** - * @brief Primitive of 16-bit RGB SNES color. + * @brief Free bitmap data + * + * Releases memory allocated for bitmap pixel data. + * + * @param bitmap Pointer to bitmap structure to free + */ +void yaze_free_bitmap(yaze_bitmap* bitmap); + +/** + * @brief Create an empty bitmap + * + * Allocates a new bitmap with the specified dimensions. + * + * @param width Width in pixels + * @param height Height in pixels + * @param bpp Bits per pixel + * @return Initialized bitmap structure, or empty bitmap on error + */ +yaze_bitmap yaze_create_bitmap(int width, int height, uint8_t bpp); + +/** + * @brief SNES color in 15-bit RGB format (BGR555) + * + * Represents a color in the SNES native format. Colors are stored + * as 8-bit values but only the lower 5 bits are used by the SNES. */ typedef struct snes_color { - uint16_t red; /**< Red component of the color. */ - uint16_t blue; /**< Blue component of the color. */ - uint16_t green; /**< Green component of the color. */ + uint16_t red; /**< Red component (0-255, but SNES uses 0-31) */ + uint16_t green; /**< Green component (0-255, but SNES uses 0-31) */ + uint16_t blue; /**< Blue component (0-255, but SNES uses 0-31) */ } snes_color; /** - * @brief Primitive of a SNES color palette. + * @brief Convert RGB888 color to SNES color + * + * @param r Red component (0-255) + * @param g Green component (0-255) + * @param b Blue component (0-255) + * @return SNES color structure + */ +snes_color yaze_rgb_to_snes_color(uint8_t r, uint8_t g, uint8_t b); + +/** + * @brief Convert SNES color to RGB888 + * + * @param color SNES color to convert + * @param r Pointer to store red component (0-255) + * @param g Pointer to store green component (0-255) + * @param b Pointer to store blue component (0-255) + */ +void yaze_snes_color_to_rgb(snes_color color, uint8_t* r, uint8_t* g, uint8_t* b); + +/** + * @brief SNES color palette + * + * Represents a color palette used by the SNES. Each palette contains + * up to 256 colors, though most modes use fewer colors per palette. */ typedef struct snes_palette { - unsigned int id; /**< ID of the palette. */ - unsigned int size; /**< Size of the palette. */ - snes_color* colors; /**< Pointer to the colors in the palette. */ + uint16_t id; /**< Palette ID (0-255) */ + uint16_t size; /**< Number of colors in palette (1-256) */ + snes_color* colors; /**< Array of colors (caller owns memory) */ } snes_palette; +/** + * @brief Create an empty palette + * + * @param id Palette ID + * @param size Number of colors to allocate + * @return Initialized palette structure, or NULL on error + */ +snes_palette* yaze_create_palette(uint16_t id, uint16_t size); + +/** + * @brief Free palette memory + * + * @param palette Pointer to palette to free + */ +void yaze_free_palette(snes_palette* palette); + +/** + * @brief Load palette from ROM + * + * @param rom ROM to load palette from + * @param palette_id ID of palette to load + * @return Loaded palette, or NULL on error + */ +snes_palette* yaze_load_palette_from_rom(const zelda3_rom* rom, uint16_t palette_id); + +/** + * @brief 8x8 SNES tile data + * + * Represents an 8x8 pixel tile with indexed color data. + * Each pixel value is an index into a palette. + */ typedef struct snes_tile8 { - uint32_t id; - uint32_t palette_id; - uint8_t data[64]; + uint32_t id; /**< Tile ID for reference */ + uint32_t palette_id; /**< Associated palette ID */ + uint8_t data[64]; /**< 64 pixels in row-major order (y*8+x) */ } snes_tile8; +/** + * @brief Load tile data from ROM + * + * @param rom ROM to load from + * @param tile_id ID of tile to load + * @param bpp Bits per pixel (1, 2, 4, 8) + * @return Loaded tile data, or empty tile on error + */ +snes_tile8 yaze_load_tile_from_rom(const zelda3_rom* rom, uint32_t tile_id, uint8_t bpp); + +/** + * @brief Convert tile data between different bit depths + * + * @param tile Source tile data + * @param from_bpp Source bits per pixel + * @param to_bpp Target bits per pixel + * @return Converted tile data + */ +snes_tile8 yaze_convert_tile_bpp(const snes_tile8* tile, uint8_t from_bpp, uint8_t to_bpp); + typedef struct snes_tile_info { uint16_t id; uint8_t palette; @@ -80,55 +326,180 @@ typedef struct snes_tile32 { uint16_t t3; } snes_tile32; +/** @} */ + /** - * @brief Get a color from a palette set. + * @defgroup rom ROM Manipulation + * @{ + */ + +/** + * @brief Load a ROM file + * + * Loads a Zelda 3 ROM file and validates its format. + * + * @param filename Path to ROM file (.sfc, .smc, etc.) + * @return Pointer to ROM structure, or NULL on error + * + * @note Caller must call yaze_unload_rom() to free memory + */ +zelda3_rom* yaze_load_rom_file(const char* filename); + +/** + * @brief Validate ROM integrity + * + * Checks if the ROM data is valid and uncorrupted. + * + * @param rom ROM to validate + * @return YAZE_OK if valid, error code if corrupted + */ +yaze_status yaze_validate_rom(const zelda3_rom* rom); + +/** + * @brief Get ROM information + * + * @param rom ROM to query + * @param version Pointer to store detected ROM version + * @param size Pointer to store ROM size in bytes + * @return YAZE_OK on success, error code on failure + */ +yaze_status yaze_get_rom_info(const zelda3_rom* rom, zelda3_version* version, uint64_t* size); + +/** + * @brief Get a color from a palette set * - * @details This function gets a color from a palette set and returns it as a - * snes_color object. + * Retrieves a specific color from a palette set in the ROM. * - * @param rom The ROM to get the color from. - * @param palette_set The palette set to get the color from. - * @param palette The palette to get the color from. - * @param color The color to get from the palette. - * @return The color from the palette set. + * @param rom The ROM to get the color from + * @param palette_set The palette set index (0-255) + * @param palette The palette index within the set (0-15) + * @param color The color index within the palette (0-15) + * @return The color from the palette set */ snes_color yaze_get_color_from_paletteset(const zelda3_rom* rom, int palette_set, int palette, int color); +/** @} */ + /** - * @brief Load the overworld from the ROM. + * @defgroup overworld Overworld Functions + * @{ + */ + +/** + * @brief Load the overworld from ROM * - * @param rom The ROM to load the overworld from. - * @return The status of the operation. If the operation is successful, the - * overworld object will be populated with the overworld from the ROM. + * Loads and parses the overworld data from the ROM, including all maps, + * sprites, and related data structures. + * + * @param rom The ROM to load the overworld from + * @return Pointer to overworld structure, or NULL on error + * + * @note Caller must free the returned pointer when done */ zelda3_overworld* yaze_load_overworld(const zelda3_rom* rom); /** - * @brief Load all rooms from the ROM. - * - * @details This function loads all rooms from the ROM and returns them as an - * array of rooms. - * - * @param rom The ROM to load rooms from. - * @return The status of the operation. If the operation is successful, the - * rooms array will be populated with the rooms from the ROM. + * @brief Free overworld data + * + * @param overworld Pointer to overworld to free */ -zelda3_dungeon_room* yaze_load_all_rooms(const zelda3_rom* rom); +void yaze_free_overworld(zelda3_overworld* overworld); /** - * @brief Load all messages from the ROM. - * - * @details This function loads all messages from the ROM and returns them as an - * array of messages. - * - * @param rom The ROM to load messages from. - * @param messages Pointer to an array of messages. - * @return The status of the operation. If the operation is successful, the - * messages array will be populated with the messages from the ROM. + * @brief Get overworld map by index + * + * @param overworld Overworld data + * @param map_index Map index (0-159 for most ROMs) + * @return Pointer to map data, or NULL if invalid index */ -yaze_status yaze_load_messages(zelda3_rom* rom, zelda3_message** messages); +const zelda3_overworld_map* yaze_get_overworld_map(const zelda3_overworld* overworld, int map_index); + +/** + * @brief Get total number of overworld maps + * + * @param overworld Overworld data + * @return Number of maps available + */ +int yaze_get_overworld_map_count(const zelda3_overworld* overworld); + +/** @} */ + +/** + * @defgroup dungeon Dungeon Functions + * @{ + */ + +/** + * @brief Load all dungeon rooms from ROM + * + * Loads and parses all dungeon room data from the ROM. + * + * @param rom The ROM to load rooms from + * @param room_count Pointer to store the number of rooms loaded + * @return Array of room structures, or NULL on error + * + * @note Caller must free the returned array when done + */ +zelda3_dungeon_room* yaze_load_all_rooms(const zelda3_rom* rom, int* room_count); + +/** + * @brief Load a specific dungeon room + * + * @param rom ROM to load from + * @param room_id Room ID to load (0-295 for most ROMs) + * @return Pointer to room data, or NULL on error + */ +const zelda3_dungeon_room* yaze_load_room(const zelda3_rom* rom, int room_id); + +/** + * @brief Free dungeon room data + * + * @param rooms Array of rooms to free + * @param room_count Number of rooms in array + */ +void yaze_free_rooms(zelda3_dungeon_room* rooms, int room_count); + +/** @} */ + +/** + * @defgroup messages Message System + * @{ + */ + +/** + * @brief Load all text messages from ROM + * + * Loads and parses all in-game text messages from the ROM. + * + * @param rom The ROM to load messages from + * @param messages Pointer to store array of messages + * @param message_count Pointer to store number of messages loaded + * @return YAZE_OK on success, error code on failure + * + * @note Caller must free the messages array when done + */ +yaze_status yaze_load_messages(const zelda3_rom* rom, zelda3_message** messages, int* message_count); + +/** + * @brief Get a specific message by ID + * + * @param rom ROM to load from + * @param message_id Message ID to retrieve + * @return Pointer to message data, or NULL if not found + */ +const zelda3_message* yaze_get_message(const zelda3_rom* rom, int message_id); + +/** + * @brief Free message data + * + * @param messages Array of messages to free + * @param message_count Number of messages in array + */ +void yaze_free_messages(zelda3_message* messages, int message_count); + +/** @} */ /** * @brief Function pointer to initialize the extension. @@ -139,31 +510,77 @@ typedef void (*yaze_initialize_func)(yaze_editor_context* context); typedef void (*yaze_cleanup_func)(void); /** - * @brief Extension interface for Yaze. + * @defgroup extensions Extension System + * @{ + */ + +/** + * @brief Extension interface for YAZE * - * @details Yaze extensions can be written in C or Python. + * Defines the interface for YAZE extensions. Extensions can add new + * functionality to YAZE and can be written in C or other languages. */ typedef struct yaze_extension { - const char* name; - const char* version; + const char* name; /**< Extension name (must not be NULL) */ + const char* version; /**< Extension version string */ + const char* description; /**< Brief description of functionality */ + const char* author; /**< Extension author */ + int api_version; /**< Required YAZE API version */ /** - * @brief Function to initialize the extension. + * @brief Initialize the extension * - * @details This function is called when the extension is loaded. It can be - * used to set up any resources or state needed by the extension. + * Called when the extension is loaded. Use this to set up + * any resources or state needed by the extension. + * + * @param context Editor context provided by YAZE + * @return YAZE_OK on success, error code on failure */ - yaze_initialize_func initialize; + yaze_status (*initialize)(yaze_editor_context* context); /** - * @brief Function to clean up the extension. + * @brief Clean up the extension * - * @details This function is called when the extension is unloaded. It can be - * used to clean up any resources or state used by the extension. + * Called when the extension is unloaded. Use this to clean up + * any resources or state used by the extension. */ - yaze_cleanup_func cleanup; + void (*cleanup)(void); + + /** + * @brief Get extension capabilities + * + * Returns a bitmask indicating what features this extension provides. + * + * @return Capability flags (see YAZE_EXT_CAP_* constants) + */ + uint32_t (*get_capabilities)(void); } yaze_extension; +/** Extension capability flags */ +#define YAZE_EXT_CAP_ROM_EDITING (1 << 0) /**< Can edit ROM data */ +#define YAZE_EXT_CAP_GRAPHICS (1 << 1) /**< Provides graphics functions */ +#define YAZE_EXT_CAP_AUDIO (1 << 2) /**< Provides audio functions */ +#define YAZE_EXT_CAP_SCRIPTING (1 << 3) /**< Provides scripting support */ +#define YAZE_EXT_CAP_IMPORT_EXPORT (1 << 4) /**< Can import/export data */ + +/** + * @brief Register an extension with YAZE + * + * @param extension Extension to register + * @return YAZE_OK on success, error code on failure + */ +yaze_status yaze_register_extension(const yaze_extension* extension); + +/** + * @brief Unregister an extension + * + * @param name Name of extension to unregister + * @return YAZE_OK on success, error code on failure + */ +yaze_status yaze_unregister_extension(const char* name); + +/** @} */ + #ifdef __cplusplus } #endif diff --git a/incl/zelda.h b/incl/zelda.h index 73786b98..cce9ba19 100644 --- a/incl/zelda.h +++ b/incl/zelda.h @@ -1,6 +1,17 @@ #ifndef ZELDA_H #define ZELDA_H +/** + * @file zelda.h + * @brief The Legend of Zelda: A Link to the Past - Data Structures and Constants + * + * This header defines data structures and constants specific to + * The Legend of Zelda: A Link to the Past ROM format and game data. + * + * @version 0.3.0 + * @author YAZE Team + */ + #ifdef __cplusplus extern "C" { #endif @@ -9,40 +20,126 @@ extern "C" { #include /** - * @brief Different versions of the game supported by yaze. + * @defgroup rom_types ROM Types and Versions + * @{ */ -enum zelda3_version { - US = 1, // US version - JP = 2, // JP version - SD = 3, // Super Donkey Proto (Experimental) - RANDO = 4, // Randomizer (Unimplemented) -}; /** - * @brief Pointers for each version of the game. + * @brief Different versions of the game supported by YAZE + * + * YAZE supports multiple regional versions and ROM hacks of + * The Legend of Zelda: A Link to the Past. */ -struct zelda3_version_pointers { - uint32_t kGfxAnimatedPointer; - uint32_t kOverworldGfxGroups1; - uint32_t kOverworldGfxGroups2; - uint32_t kCompressedAllMap32PointersHigh; - uint32_t kCompressedAllMap32PointersLow; - uint32_t kOverworldMapPaletteGroup; - uint32_t kOverlayPointers; - uint32_t kOverlayPointersBank; - uint32_t kOverworldTilesType; - uint32_t kOverworldGfxPtr1; - uint32_t kOverworldGfxPtr2; - uint32_t kOverworldGfxPtr3; - uint32_t kMap32TileTL; - uint32_t kMap32TileTR; - uint32_t kMap32TileBL; - uint32_t kMap32TileBR; - uint32_t kSpriteBlocksetPointer; - uint32_t kDungeonPalettesGroups; -}; +typedef enum zelda3_version { + ZELDA3_VERSION_UNKNOWN = 0, /**< Unknown or unsupported version */ + ZELDA3_VERSION_US = 1, /**< US/North American version */ + ZELDA3_VERSION_JP = 2, /**< Japanese version */ + ZELDA3_VERSION_EU = 3, /**< European version */ + ZELDA3_VERSION_PROTO = 4, /**< Prototype/development version */ + ZELDA3_VERSION_RANDOMIZER = 5, /**< Randomizer ROM (experimental) */ + + // Legacy aliases for backward compatibility + US = ZELDA3_VERSION_US, /**< @deprecated Use ZELDA3_VERSION_US */ + JP = ZELDA3_VERSION_JP, /**< @deprecated Use ZELDA3_VERSION_JP */ + SD = ZELDA3_VERSION_PROTO, /**< @deprecated Use ZELDA3_VERSION_PROTO */ + RANDO = ZELDA3_VERSION_RANDOMIZER, /**< @deprecated Use ZELDA3_VERSION_RANDOMIZER */ +} zelda3_version; + +/** + * @brief Detect ROM version from header data + * + * @param rom_data Pointer to ROM data + * @param size Size of ROM data in bytes + * @return Detected version, or ZELDA3_VERSION_UNKNOWN if not recognized + */ +zelda3_version zelda3_detect_version(const uint8_t* rom_data, size_t size); + +/** + * @brief Get version name as string + * + * @param version Version enum value + * @return Human-readable version name + */ +const char* zelda3_version_to_string(zelda3_version version); + +/** + * @brief ROM data pointers for different game versions + * + * Contains memory addresses where specific data structures are located + * within the ROM. These addresses vary between different regional versions. + */ +typedef struct zelda3_version_pointers { + // New Google C++ style names + uint32_t gfx_animated_pointer; /**< Animated graphics pointer */ + uint32_t overworld_gfx_groups1; /**< Overworld graphics group 1 */ + uint32_t overworld_gfx_groups2; /**< Overworld graphics group 2 */ + uint32_t compressed_map32_pointers_high; /**< Map32 high pointers */ + uint32_t compressed_map32_pointers_low; /**< Map32 low pointers */ + uint32_t overworld_map_palette_group; /**< Map palette groups */ + uint32_t overlay_pointers; /**< Overlay data pointers */ + uint32_t overlay_pointers_bank; /**< Overlay bank number */ + uint32_t overworld_tiles_type; /**< Tile type definitions */ + uint32_t overworld_gfx_ptr1; /**< Graphics pointer 1 */ + uint32_t overworld_gfx_ptr2; /**< Graphics pointer 2 */ + uint32_t overworld_gfx_ptr3; /**< Graphics pointer 3 */ + uint32_t map32_tile_tl; /**< 32x32 tile top-left */ + uint32_t map32_tile_tr; /**< 32x32 tile top-right */ + uint32_t map32_tile_bl; /**< 32x32 tile bottom-left */ + uint32_t map32_tile_br; /**< 32x32 tile bottom-right */ + uint32_t sprite_blockset_pointer; /**< Sprite graphics pointer */ + uint32_t dungeon_palettes_groups; /**< Dungeon palette groups */ + + // Legacy aliases for backward compatibility (deprecated) + uint32_t kGfxAnimatedPointer; /**< @deprecated Use gfx_animated_pointer */ + uint32_t kOverworldGfxGroups1; /**< @deprecated Use overworld_gfx_groups1 */ + uint32_t kOverworldGfxGroups2; /**< @deprecated Use overworld_gfx_groups2 */ + uint32_t kCompressedAllMap32PointersHigh; /**< @deprecated Use compressed_map32_pointers_high */ + uint32_t kCompressedAllMap32PointersLow; /**< @deprecated Use compressed_map32_pointers_low */ + uint32_t kOverworldMapPaletteGroup; /**< @deprecated Use overworld_map_palette_group */ + uint32_t kOverlayPointers; /**< @deprecated Use overlay_pointers */ + uint32_t kOverlayPointersBank; /**< @deprecated Use overlay_pointers_bank */ + uint32_t kOverworldTilesType; /**< @deprecated Use overworld_tiles_type */ + uint32_t kOverworldGfxPtr1; /**< @deprecated Use overworld_gfx_ptr1 */ + uint32_t kOverworldGfxPtr2; /**< @deprecated Use overworld_gfx_ptr2 */ + uint32_t kOverworldGfxPtr3; /**< @deprecated Use overworld_gfx_ptr3 */ + uint32_t kMap32TileTL; /**< @deprecated Use map32_tile_tl */ + uint32_t kMap32TileTR; /**< @deprecated Use map32_tile_tr */ + uint32_t kMap32TileBL; /**< @deprecated Use map32_tile_bl */ + uint32_t kMap32TileBR; /**< @deprecated Use map32_tile_br */ + uint32_t kSpriteBlocksetPointer; /**< @deprecated Use sprite_blockset_pointer */ + uint32_t kDungeonPalettesGroups; /**< @deprecated Use dungeon_palettes_groups */ +} zelda3_version_pointers; + +/** + * @brief Get version-specific pointers + * + * @param version ROM version + * @return Pointer to version-specific address structure + */ +const zelda3_version_pointers* zelda3_get_version_pointers(zelda3_version version); const static zelda3_version_pointers zelda3_us_pointers = { + // New style names + 0x10275, // gfx_animated_pointer + 0x5D97, // overworld_gfx_groups1 + 0x6073, // overworld_gfx_groups2 + 0x1794D, // compressed_map32_pointers_high + 0x17B2D, // compressed_map32_pointers_low + 0x75504, // overworld_map_palette_group + 0x77664, // overlay_pointers + 0x0E, // overlay_pointers_bank + 0x71459, // overworld_tiles_type + 0x4F80, // overworld_gfx_ptr1 + 0x505F, // overworld_gfx_ptr2 + 0x513E, // overworld_gfx_ptr3 + 0x18000, // map32_tile_tl + 0x1B400, // map32_tile_tr + 0x20000, // map32_tile_bl + 0x23400, // map32_tile_br + 0x5B57, // sprite_blockset_pointer + 0x75460, // dungeon_palettes_groups + + // Legacy k-prefixed names (same values for backward compatibility) 0x10275, // kGfxAnimatedPointer 0x5D97, // kOverworldGfxGroups1 0x6073, // kOverworldGfxGroups2 @@ -64,6 +161,27 @@ const static zelda3_version_pointers zelda3_us_pointers = { }; const static zelda3_version_pointers zelda3_jp_pointers = { + // New style names + 0x10624, // gfx_animated_pointer + 0x5DD7, // overworld_gfx_groups1 + 0x60B3, // overworld_gfx_groups2 + 0x176B1, // compressed_map32_pointers_high + 0x17891, // compressed_map32_pointers_low + 0x67E74, // overworld_map_palette_group + 0x3FAF4, // overlay_pointers + 0x07, // overlay_pointers_bank + 0x7FD94, // overworld_tiles_type + 0x4FC0, // overworld_gfx_ptr1 + 0x509F, // overworld_gfx_ptr2 + 0x517E, // overworld_gfx_ptr3 + 0x18000, // map32_tile_tl + 0x1B3C0, // map32_tile_tr + 0x20000, // map32_tile_bl + 0x233C0, // map32_tile_br + 0x5B97, // sprite_blockset_pointer + 0x67DD0, // dungeon_palettes_groups + + // Legacy k-prefixed names (same values for backward compatibility) 0x10624, // kGfxAnimatedPointer 0x5DD7, // kOverworldGfxGroups1 0x60B3, // kOverworldGfxGroups2 @@ -84,119 +202,296 @@ const static zelda3_version_pointers zelda3_jp_pointers = { 0x67DD0, // kDungeonPalettesGroups }; +/** + * @brief ROM data structure + * + * Represents a loaded Zelda 3 ROM with its data and metadata. + */ typedef struct zelda3_rom { - const char* filename; - uint8_t* data; - uint64_t size; - void* impl; // yaze::Rom* + const char* filename; /**< Original filename (can be NULL) */ + uint8_t* data; /**< ROM data (read-only for external users) */ + uint64_t size; /**< Size of ROM data in bytes */ + zelda3_version version; /**< Detected ROM version */ + bool is_modified; /**< True if ROM has been modified */ + void* impl; /**< Internal implementation pointer */ } zelda3_rom; -zelda3_rom* yaze_load_rom(const char* filename); -void yaze_unload_rom(zelda3_rom* rom); -void yaze_save_rom(zelda3_rom* rom, const char* filename); +/** @} */ /** - * @brief Primitive of a message. + * @defgroup rom_functions ROM File Operations + * @{ + */ + +/** + * @brief Load a ROM file * + * @param filename Path to ROM file + * @return Loaded ROM structure, or NULL on error + */ +zelda3_rom* yaze_load_rom(const char* filename); + +/** + * @brief Unload and free ROM data + * + * @param rom ROM to unload + */ +void yaze_unload_rom(zelda3_rom* rom); + +/** + * @brief Save ROM to file + * + * @param rom ROM to save + * @param filename Output filename + * @return YAZE_OK on success, error code on failure + */ +int yaze_save_rom(zelda3_rom* rom, const char* filename); + +/** + * @brief Create a copy of ROM data + * + * @param rom Source ROM + * @return Copy of ROM, or NULL on error + */ +zelda3_rom* yaze_copy_rom(const zelda3_rom* rom); + +/** @} */ + +/** + * @defgroup messages Message Data Structures + * @{ + */ + +/** + * @brief In-game text message data + * + * Represents a text message from the game, including both raw + * ROM data and parsed/decoded text content. */ typedef struct zelda3_message { - uint8_t id; - uint8_t address; - uint8_t *raw_string; - uint8_t *contents_parsed; - uint8_t *data; - uint8_t *data_parsed; + uint16_t id; /**< Message ID (0-65535) */ + uint32_t rom_address; /**< Address in ROM where message data starts */ + uint16_t length; /**< Length of message data in bytes */ + uint8_t* raw_data; /**< Raw message data from ROM */ + char* parsed_text; /**< Decoded text content (UTF-8) */ + bool is_compressed; /**< True if message uses compression */ + uint8_t encoding_type; /**< Text encoding type used */ } zelda3_message; +/** @} */ + /** - * @brief Primitive of an overworld map. + * @defgroup overworld Overworld Data Structures + * @{ + */ + +/** + * @brief Overworld map data + * + * Represents a single screen/area in the overworld, including + * graphics, palette, music, and sprite information. */ typedef struct zelda3_overworld_map { - uint8_t id; /**< ID of the overworld map. */ - uint8_t parent_id; - uint8_t quadrant_id; - uint8_t world_id; - uint8_t game_state; - uint8_t area_graphics; - uint8_t area_palette; - - uint8_t sprite_graphics[3]; - uint8_t sprite_palette[3]; - uint8_t area_music[4]; - uint8_t static_graphics[16]; + uint16_t id; /**< Map ID (0-159 for most ROMs) */ + uint8_t parent_id; /**< Parent map ID for sub-areas */ + uint8_t quadrant_id; /**< Quadrant within parent (0-3) */ + uint8_t world_id; /**< World number (Light/Dark) */ + uint8_t game_state; /**< Game state requirements */ + + /* Graphics and Visual Properties */ + uint8_t area_graphics; /**< Area graphics set ID */ + uint8_t area_palette; /**< Area palette set ID */ + uint8_t main_palette; /**< Main palette ID */ + uint8_t animated_gfx; /**< Animated graphics ID */ + + /* Sprite Configuration */ + uint8_t sprite_graphics[3]; /**< Sprite graphics sets */ + uint8_t sprite_palette[3]; /**< Sprite palette sets */ + + /* Audio Configuration */ + uint8_t area_music[4]; /**< Music tracks for different states */ + + /* Extended Graphics (ZSCustomOverworld) */ + uint8_t static_graphics[16]; /**< Static graphics assignments */ + uint8_t custom_tileset[8]; /**< Custom tileset assignments */ + + /* Screen Properties */ + uint16_t area_specific_bg_color; /**< Background color override */ + uint16_t subscreen_overlay; /**< Subscreen overlay settings */ + + /* Flags and Metadata */ + bool is_large_map; /**< True for 32x32 maps */ + bool has_special_gfx; /**< True if uses special graphics */ } zelda3_overworld_map; /** - * @brief Primitive of the overworld. + * @brief Complete overworld data + * + * Contains all overworld maps and related data for the entire game world. */ typedef struct zelda3_overworld { - void* impl; // yaze::Overworld* - zelda3_overworld_map** maps; /**< Pointer to the overworld maps. */ + void* impl; /**< Internal implementation pointer */ + zelda3_overworld_map** maps; /**< Array of overworld maps */ + int map_count; /**< Number of maps in array */ + zelda3_version rom_version; /**< ROM version this data came from */ + bool has_zsco_features; /**< True if ZSCustomOverworld features detected */ } zelda3_overworld; +/** @} */ + +/** + * @defgroup dungeon Dungeon Data Structures + * @{ + */ + +/** + * @brief Dungeon sprite definition + * + * Represents a sprite that can appear in dungeon rooms. + */ typedef struct dungeon_sprite { - const char* name; - uint8_t id; - uint8_t subtype; + const char* name; /**< Sprite name (for debugging/display) */ + uint8_t id; /**< Sprite type ID */ + uint8_t subtype; /**< Sprite subtype/variant */ + uint8_t x; /**< X position in room */ + uint8_t y; /**< Y position in room */ + uint8_t layer; /**< Layer (0=background, 1=foreground) */ + uint16_t properties; /**< Additional sprite properties */ } dungeon_sprite; -typedef enum background2 { - Off, - Parallax, - Dark, - OnTop, - Translucent, - Addition, - Normal, - Transparent, - DarkRoom -} background2; +/** + * @brief Background layer 2 effects + * + * Defines the different visual effects that can be applied to + * background layer 2 in dungeon rooms. + */ +typedef enum zelda3_bg2_effect { + ZELDA3_BG2_OFF = 0, /**< Background layer 2 disabled */ + ZELDA3_BG2_PARALLAX = 1, /**< Parallax scrolling effect */ + ZELDA3_BG2_DARK = 2, /**< Dark overlay effect */ + ZELDA3_BG2_ON_TOP = 3, /**< Layer appears on top */ + ZELDA3_BG2_TRANSLUCENT = 4, /**< Semi-transparent overlay */ + ZELDA3_BG2_ADDITION = 5, /**< Additive blending */ + ZELDA3_BG2_NORMAL = 6, /**< Normal blending */ + ZELDA3_BG2_TRANSPARENT = 7, /**< Fully transparent */ + ZELDA3_BG2_DARK_ROOM = 8 /**< Dark room effect */ +} zelda3_bg2_effect; +// Legacy aliases for backward compatibility +typedef zelda3_bg2_effect background2; +#define Off ZELDA3_BG2_OFF +#define Parallax ZELDA3_BG2_PARALLAX +#define Dark ZELDA3_BG2_DARK +#define OnTop ZELDA3_BG2_ON_TOP +#define Translucent ZELDA3_BG2_TRANSLUCENT +#define Addition ZELDA3_BG2_ADDITION +#define Normal ZELDA3_BG2_NORMAL +#define Transparent ZELDA3_BG2_TRANSPARENT +#define DarkRoom ZELDA3_BG2_DARK_ROOM + +/** + * @brief Dungeon door object + * + * Represents a door or passage between rooms. + */ typedef struct object_door { - short id; - uint8_t x; - uint8_t y; - uint8_t size; - uint8_t type; - uint8_t layer; + uint16_t id; /**< Door ID for reference */ + uint8_t x; /**< X position in room (0-63) */ + uint8_t y; /**< Y position in room (0-63) */ + uint8_t size; /**< Door size (width/height) */ + uint8_t type; /**< Door type (normal, locked, etc.) */ + uint8_t layer; /**< Layer (0=background, 1=foreground) */ + uint8_t key_type; /**< Required key type (0=none) */ + bool is_locked; /**< True if door requires key */ } object_door; +/** + * @brief Staircase connection + * + * Represents stairs or holes that connect different rooms or levels. + */ typedef struct staircase { - uint8_t id; - uint8_t room; - const char* label; + uint8_t id; /**< Staircase ID */ + uint8_t room; /**< Target room ID (for backward compatibility) */ + const char* label; /**< Description (for debugging) */ } staircase; +/** + * @brief Treasure chest + * + * Represents a chest containing an item. + */ typedef struct chest { - uint8_t x; - uint8_t y; - uint8_t item; - bool picker; - bool big_chest; + uint8_t x; /**< X position in room */ + uint8_t y; /**< Y position in room */ + uint8_t item; /**< Item ID (for backward compatibility) */ + bool picker; /**< Legacy field */ + bool big_chest; /**< True for large chests */ } chest; +/** + * @brief Legacy chest data structure + * + * @deprecated Use chest structure instead + */ typedef struct chest_data { - uint8_t id; - bool size; + uint8_t id; /**< Chest ID */ + bool size; /**< True for big chest */ } chest_data; +/** + * @brief Room transition destination + * + * Defines where the player goes when using stairs, holes, or other transitions. + */ typedef struct destination { - uint8_t index; - uint8_t target; - uint8_t target_layer; + uint8_t index; /**< Entrance index */ + uint8_t target; /**< Target room ID */ + uint8_t target_layer; /**< Target layer */ } destination; +/** @} */ + +/** + * @brief Complete dungeon room data + * + * Contains all objects, sprites, and properties for a single dungeon room. + */ typedef struct zelda3_dungeon_room { - background2 bg2; - dungeon_sprite* sprites; - object_door* doors; - staircase* staircases; - chest* chests; - chest_data* chests_in_room; - destination pits; - destination stairs[4]; + uint16_t id; /**< Room ID (0-295) */ + background2 bg2; /**< Background layer 2 effect (legacy) */ + + /* Room Contents */ + dungeon_sprite* sprites; /**< Array of sprites in room */ + int sprite_count; /**< Number of sprites */ + + object_door* doors; /**< Array of doors */ + int door_count; /**< Number of doors */ + + staircase* staircases; /**< Array of staircases */ + int staircase_count; /**< Number of staircases */ + + chest* chests; /**< Array of chests */ + int chest_count; /**< Number of chests */ + + /* Room Connections */ + destination pits; /**< Pit fall destination */ + destination stairs[4]; /**< Stair destinations (up to 4) */ + + /* Room Properties */ + uint8_t floor_type; /**< Floor graphics type */ + uint8_t wall_type; /**< Wall graphics type */ + uint8_t palette_id; /**< Room palette ID */ + uint8_t music_track; /**< Background music track */ + + /* Flags */ + bool is_dark; /**< True if room requires lamp */ + bool has_water; /**< True if room contains water */ + bool blocks_items; /**< True if room blocks certain items */ } zelda3_dungeon_room; +/** @} */ + #ifdef __cplusplus } #endif diff --git a/src/app/editor/overworld/map_properties.cc b/src/app/editor/overworld/map_properties.cc index e7c2265b..2be3b07f 100644 --- a/src/app/editor/overworld/map_properties.cc +++ b/src/app/editor/overworld/map_properties.cc @@ -351,6 +351,7 @@ void MapPropertiesSystem::DrawGraphicsPopup(int current_map, int game_state) { } } + ImGui::PopStyleVar(2); // Pop the 2 style variables we pushed ImGui::EndPopup(); } } @@ -394,6 +395,7 @@ void MapPropertiesSystem::DrawPalettesPopup(int current_map, int game_state, boo show_custom_bg_color_editor = !show_custom_bg_color_editor; } + ImGui::PopStyleVar(2); // Pop the 2 style variables we pushed ImGui::EndPopup(); } } @@ -472,7 +474,7 @@ void MapPropertiesSystem::DrawPropertiesPopup(int current_map, bool& show_map_pr } HOVER_HINT("Open comprehensive properties editor"); - ImGui::PopStyleVar(2); + ImGui::PopStyleVar(2); // Pop the 2 style variables we pushed ImGui::EndPopup(); } } @@ -905,7 +907,7 @@ void MapPropertiesSystem::DrawViewPopup() { } HOVER_HINT("Toggle fullscreen canvas (F11)"); - ImGui::PopStyleVar(2); + ImGui::PopStyleVar(2); // Pop the 2 style variables we pushed ImGui::EndPopup(); } } @@ -939,7 +941,7 @@ void MapPropertiesSystem::DrawQuickAccessPopup() { } HOVER_HINT("Lock/unlock current map (Ctrl+L)"); - ImGui::PopStyleVar(2); + ImGui::PopStyleVar(2); // Pop the 2 style variables we pushed ImGui::EndPopup(); } } diff --git a/src/app/gui/color.cc b/src/app/gui/color.cc index 4d12a06c..c74b4f16 100644 --- a/src/app/gui/color.cc +++ b/src/app/gui/color.cc @@ -13,7 +13,11 @@ ImVec4 ConvertSnesColorToImVec4(const gfx::SnesColor& color) { } gfx::SnesColor ConvertImVec4ToSnesColor(const ImVec4& color) { - return gfx::SnesColor(color.x, color.y, color.z); + // Convert from float (0.0-1.0) to uint8_t (0-255) + uint8_t r = static_cast(color.x * 255.0f); + uint8_t g = static_cast(color.y * 255.0f); + uint8_t b = static_cast(color.z * 255.0f); + return gfx::SnesColor(r, g, b); } IMGUI_API bool SnesColorButton(absl::string_view id, gfx::SnesColor& color, diff --git a/src/yaze.cc b/src/yaze.cc index 70c0f015..f2cf4c52 100644 --- a/src/yaze.cc +++ b/src/yaze.cc @@ -3,6 +3,8 @@ #include #include #include +#include +#include #include "app/core/controller.h" #include "app/core/platform/app_delegate.h" @@ -16,6 +18,9 @@ DEFINE_FLAG(std::string, rom_file, "", "Path to the ROM file to load. " "If not specified, the app will run without a ROM."); +// Static variables for library state +static bool g_library_initialized = false; + int yaze_app_main(int argc, char **argv) { yaze::util::FlagParser parser(yaze::util::global_flag_registry()); RETURN_IF_EXCEPTION(parser.Parse(argc, argv)); @@ -42,72 +47,161 @@ int yaze_app_main(int argc, char **argv) { return EXIT_SUCCESS; } -void yaze_check_version(const char *version) { - std::string current_version; - std::stringstream ss; - ss << YAZE_VERSION_MAJOR << "." << YAZE_VERSION_MINOR << "." - << YAZE_VERSION_PATCH; - ss >> current_version; +// Version and initialization functions +yaze_status yaze_library_init() { + if (g_library_initialized) { + return YAZE_OK; + } + + // Initialize SDL and other subsystems if needed + g_library_initialized = true; + return YAZE_OK; +} - if (version != current_version) { - std::cout << "Yaze version mismatch: expected " << current_version - << ", got " << version << std::endl; - exit(1); +void yaze_library_shutdown() { + if (!g_library_initialized) { + return; + } + + // Cleanup subsystems + g_library_initialized = false; + + return; +} + +const char* yaze_status_to_string(yaze_status status) { + switch (status) { + case YAZE_OK: + return "Success"; + case YAZE_ERROR_UNKNOWN: + return "Unknown error"; + case YAZE_ERROR_INVALID_ARG: + return "Invalid argument"; + case YAZE_ERROR_FILE_NOT_FOUND: + return "File not found"; + case YAZE_ERROR_MEMORY: + return "Memory allocation failed"; + case YAZE_ERROR_IO: + return "I/O operation failed"; + case YAZE_ERROR_CORRUPTION: + return "Data corruption detected"; + case YAZE_ERROR_NOT_INITIALIZED: + return "Component not initialized"; + default: + return "Unknown status code"; } } -yaze_status yaze_init(yaze_editor_context *yaze_ctx, char *rom_filename) { - yaze_ctx->rom = yaze_load_rom(rom_filename); - if (yaze_ctx->rom == nullptr) { - yaze_ctx->error_message = "Failed to load ROM"; - return yaze_status::YAZE_ERROR; - } - - return yaze_status::YAZE_OK; +const char* yaze_get_version_string() { + return YAZE_VERSION_STRING; } -yaze_status yaze_shutdown(yaze_editor_context *yaze_ctx) { - if (yaze_ctx->rom) { - yaze_unload_rom(yaze_ctx->rom); - } - return yaze_status::YAZE_OK; +int yaze_get_version_number() { + return YAZE_VERSION_NUMBER; } -zelda3_rom *yaze_load_rom(const char *filename) { - yaze::Rom *internal_rom; - internal_rom = new yaze::Rom(); +bool yaze_check_version_compatibility(const char* expected_version) { + if (expected_version == nullptr) { + return false; + } + return strcmp(expected_version, YAZE_VERSION_STRING) == 0; +} + +yaze_status yaze_init(yaze_editor_context* context, const char* rom_filename) { + if (context == nullptr) { + return YAZE_ERROR_INVALID_ARG; + } + + if (!g_library_initialized) { + yaze_status init_status = yaze_library_init(); + if (init_status != YAZE_OK) { + return init_status; + } + } + + context->rom = nullptr; + context->error_message = nullptr; + + if (rom_filename != nullptr && strlen(rom_filename) > 0) { + context->rom = yaze_load_rom(rom_filename); + if (context->rom == nullptr) { + context->error_message = "Failed to load ROM file"; + return YAZE_ERROR_FILE_NOT_FOUND; + } + } + + return YAZE_OK; +} + +yaze_status yaze_shutdown(yaze_editor_context* context) { + if (context == nullptr) { + return YAZE_ERROR_INVALID_ARG; + } + + if (context->rom != nullptr) { + yaze_unload_rom(context->rom); + context->rom = nullptr; + } + + context->error_message = nullptr; + return YAZE_OK; +} + +zelda3_rom* yaze_load_rom(const char* filename) { + if (filename == nullptr || strlen(filename) == 0) { + return nullptr; + } + + auto internal_rom = std::make_unique(); if (!internal_rom->LoadFromFile(filename).ok()) { - delete internal_rom; return nullptr; } - zelda3_rom *rom = new zelda3_rom(); + auto* rom = new zelda3_rom(); rom->filename = filename; - rom->impl = internal_rom; - rom->data = const_cast(internal_rom->data()); - rom->size = internal_rom->size(); + rom->impl = internal_rom.release(); // Transfer ownership + rom->data = const_cast(static_cast(rom->impl)->data()); + rom->size = static_cast(rom->impl)->size(); + rom->version = ZELDA3_VERSION_US; // Default, should be detected + rom->is_modified = false; return rom; } -void yaze_unload_rom(zelda3_rom *rom) { - if (rom->impl) { - delete static_cast(rom->impl); +void yaze_unload_rom(zelda3_rom* rom) { + if (rom == nullptr) { + return; + } + + if (rom->impl != nullptr) { + delete static_cast(rom->impl); + rom->impl = nullptr; } - if (rom) { - delete rom; - } + delete rom; } -void yaze_save_rom(zelda3_rom *rom, const char *filename) { - if (rom->impl) { - yaze::Rom *internal_rom = static_cast(rom->impl); - if (auto status = internal_rom->SaveToFile(yaze::Rom::SaveSettings{ - .backup = true, .save_new = false, .filename = filename}); - !status.ok()) { - throw std::runtime_error(status.message().data()); - } +int yaze_save_rom(zelda3_rom* rom, const char* filename) { + if (rom == nullptr || filename == nullptr) { + return YAZE_ERROR_INVALID_ARG; } + + if (rom->impl == nullptr) { + return YAZE_ERROR_NOT_INITIALIZED; + } + + auto* internal_rom = static_cast(rom->impl); + auto status = internal_rom->SaveToFile(yaze::Rom::SaveSettings{ + .backup = true, + .save_new = false, + .filename = filename + }); + + if (!status.ok()) { + return YAZE_ERROR_IO; + } + + rom->is_modified = false; + return YAZE_OK; } yaze_bitmap yaze_load_bitmap(const char *filename) { @@ -172,27 +266,122 @@ zelda3_dungeon_room *yaze_load_all_rooms(const zelda3_rom *rom) { return rooms; } -yaze_status yaze_load_messages(zelda3_rom *rom, zelda3_message **messages) { +yaze_status yaze_load_messages(const zelda3_rom* rom, zelda3_message** messages, int* message_count) { + if (rom == nullptr || messages == nullptr || message_count == nullptr) { + return YAZE_ERROR_INVALID_ARG; + } + if (rom->impl == nullptr) { - return yaze_status::YAZE_ERROR; + return YAZE_ERROR_NOT_INITIALIZED; } - // Use LoadAllTextData from message_data.h - std::vector message_data = - yaze::editor::ReadAllTextData(rom->data, 0); - for (const auto &message : message_data) { - messages[message.ID] = new zelda3_message(); - messages[message.ID]->id = message.ID; - messages[message.ID]->address = message.Address; - messages[message.ID]->raw_string = reinterpret_cast( - const_cast(message.RawString.data())); - messages[message.ID]->contents_parsed = reinterpret_cast( - const_cast(message.ContentsParsed.data())); - messages[message.ID]->data = - reinterpret_cast(const_cast(message.Data.data())); - messages[message.ID]->data_parsed = reinterpret_cast( - const_cast(message.DataParsed.data())); + try { + // Use LoadAllTextData from message_data.h + std::vector message_data = + yaze::editor::ReadAllTextData(rom->data, 0); + + *message_count = static_cast(message_data.size()); + *messages = new zelda3_message[*message_count]; + + for (size_t i = 0; i < message_data.size(); ++i) { + const auto& msg = message_data[i]; + (*messages)[i].id = msg.ID; + (*messages)[i].rom_address = msg.Address; + (*messages)[i].length = static_cast(msg.RawString.length()); + + // Allocate and copy string data + (*messages)[i].raw_data = new uint8_t[msg.Data.size()]; + std::memcpy((*messages)[i].raw_data, msg.Data.data(), msg.Data.size()); + + (*messages)[i].parsed_text = new char[msg.ContentsParsed.length() + 1]; + std::strcpy((*messages)[i].parsed_text, msg.ContentsParsed.c_str()); + + (*messages)[i].is_compressed = false; // TODO: Detect compression + (*messages)[i].encoding_type = 0; // TODO: Detect encoding + } + } catch (const std::exception& e) { + return YAZE_ERROR_MEMORY; } - return yaze_status::YAZE_OK; + return YAZE_OK; +} + +// Additional API functions implementation + +// Graphics functions +void yaze_free_bitmap(yaze_bitmap* bitmap) { + if (bitmap != nullptr && bitmap->data != nullptr) { + delete[] bitmap->data; + bitmap->data = nullptr; + bitmap->width = 0; + bitmap->height = 0; + bitmap->bpp = 0; + } +} + +yaze_bitmap yaze_create_bitmap(int width, int height, uint8_t bpp) { + yaze_bitmap bitmap = {}; + + if (width <= 0 || height <= 0 || (bpp != 1 && bpp != 2 && bpp != 4 && bpp != 8)) { + return bitmap; // Return empty bitmap on invalid args + } + + bitmap.width = width; + bitmap.height = height; + bitmap.bpp = bpp; + bitmap.data = new uint8_t[width * height](); + + return bitmap; +} + +snes_color yaze_rgb_to_snes_color(uint8_t r, uint8_t g, uint8_t b) { + snes_color color = {}; + color.red = r; // Store full 8-bit values (existing code expects this) + color.green = g; + color.blue = b; + return color; +} + +void yaze_snes_color_to_rgb(snes_color color, uint8_t* r, uint8_t* g, uint8_t* b) { + if (r != nullptr) *r = static_cast(color.red); + if (g != nullptr) *g = static_cast(color.green); + if (b != nullptr) *b = static_cast(color.blue); +} + +// Version detection functions +zelda3_version zelda3_detect_version(const uint8_t* rom_data, size_t size) { + if (rom_data == nullptr || size < 0x100000) { + return ZELDA3_VERSION_UNKNOWN; + } + + // TODO: Implement proper version detection based on ROM header + return ZELDA3_VERSION_US; // Default assumption +} + +const char* zelda3_version_to_string(zelda3_version version) { + switch (version) { + case ZELDA3_VERSION_US: + return "US/North American"; + case ZELDA3_VERSION_JP: + return "Japanese"; + case ZELDA3_VERSION_EU: + return "European"; + case ZELDA3_VERSION_PROTO: + return "Prototype"; + case ZELDA3_VERSION_RANDOMIZER: + return "Randomizer"; + default: + return "Unknown"; + } +} + +const zelda3_version_pointers* zelda3_get_version_pointers(zelda3_version version) { + switch (version) { + case ZELDA3_VERSION_US: + return &zelda3_us_pointers; + case ZELDA3_VERSION_JP: + return &zelda3_jp_pointers; + default: + return &zelda3_us_pointers; // Default fallback + } } \ No newline at end of file