diff --git a/archive/asset/CMakeLists.txt b/archive/asset/CMakeLists.txt new file mode 100644 index 0000000..709261f --- /dev/null +++ b/archive/asset/CMakeLists.txt @@ -0,0 +1,14 @@ +# Copyright (c) 2025 Dominic Masters +# +# This software is released under the MIT License. +# https://opensource.org/licenses/MIT + +# Sources +target_sources(${DUSK_TARGET_NAME} + PRIVATE + asset.c + assetmanager.c +) + +# Subdirs +add_subdirectory(type) \ No newline at end of file diff --git a/archive/asset/asset.c b/archive/asset/asset.c new file mode 100644 index 0000000..217584f --- /dev/null +++ b/archive/asset/asset.c @@ -0,0 +1,139 @@ +/** + * Copyright (c) 2025 Dominic Masters + * + * This software is released under the MIT License. + * https://opensource.org/licenses/MIT + */ + +#include "asset.h" +#include "assetmanager.h" +#include "util/memory.h" +#include "assert/assert.h" + +assetdef_t ASSET_DEFINITIONS[ASSET_TYPE_COUNT] = { + [ASSET_TYPE_PALETTE_IMAGE] = { + "DPI", assetPaletteImageLoad, assetPaletteImageDispose + }, + [ASSET_TYPE_ALPHA_IMAGE] = { + "DAI", assetAlphaImageLoad, assetAlphaImageDispose + }, +}; + +errorret_t assetInit(asset_t *asset, const char_t *filename) { + assertNotNull(asset, "Asset cannot be NULL."); + assertNotNull(filename, "Filename cannot be NULL."); + assertTrue(strlen(filename) < FILENAME_MAX, "Filename too long."); + assertTrue(strlen(filename) > 0, "Filename cannot be empty."); + + memoryZero(asset, sizeof(asset_t)); + memoryCopy(asset->filename, filename, strlen(filename) + 1); + + // Initialze the reference list. + refListInit( + &asset->refList, + asset->refListArray, + ASSET_REFERENCE_COUNT_MAX + ); + + // Test the file can be opened. In future I may make the handle stay open for + // a while to see if it increases performance and stops disk thrashing. + asset->file = zip_fopen(ASSET_MANAGER.zip, filename, 0); + if(asset->file == NULL) errorThrow("Failed to open asset file: %s", filename); + zip_fclose(asset->file); + asset->file = NULL; + errorOk(); +} + +ref_t assetLock(asset_t *asset) { + assertNotNull(asset, "Asset cannot be NULL."); + + return refListLock(&asset->refList); +} + +void assetUnlock(asset_t *asset, const ref_t ref) { + assertNotNull(asset, "Asset cannot be NULL."); + + // Just unlock the reference in the reference list. + refListUnlock(&asset->refList, ref); +} + +errorret_t assetLoad(asset_t *asset) { + assertNotNull(asset, "Asset cannot be NULL."); + assertTrue( + asset->state == ASSET_STATE_NOT_LOADED, + "Asset is already loaded or loading." + ); + + // Mark as loading. + asset->state = ASSET_STATE_LOADING; + + // Open the file. + if(asset->file == NULL) { + asset->file = zip_fopen(ASSET_MANAGER.zip, asset->filename, 0); + if(asset->file == NULL) { + asset->state = ASSET_STATE_ERROR; + errorThrow("Failed to open asset file: %s", asset->filename); + } + } + + // Read header. + char_t header[ASSET_HEADER_SIZE + 1]; + memoryZero(header, ASSET_HEADER_SIZE + 1); + zip_int64_t bytesRead = zip_fread(asset->file, header, ASSET_HEADER_SIZE); + if(bytesRead != ASSET_HEADER_SIZE) { + asset->state = ASSET_STATE_ERROR; + zip_fclose(asset->file); + asset->file = NULL; + errorThrow("Failed to read asset header for: %s", asset->filename); + } + + // Check header. + if(strlen(header) != ASSET_HEADER_SIZE) { + asset->state = ASSET_STATE_ERROR; + zip_fclose(asset->file); + asset->file = NULL; + errorThrow("Invalid asset header for: %s", asset->filename); + } + + // Get the asset definition based on the header. + assetdef_t *def; + for(uint_fast8_t i = 0; i < ASSET_TYPE_COUNT; i++) { + if(strcmp(header, ASSET_DEFINITIONS[i].header) == 0) { + def = &ASSET_DEFINITIONS[i]; + asset->type = i; + break; + } + } + assertNotNull(def, "Failed to find asset definition for header."); + + // Load the asset + errorret_t ret = def->load(asset); + if(ret.code != ERROR_OK) { + asset->state = ASSET_STATE_ERROR; + zip_fclose(asset->file); + asset->file = NULL; + errorChain(ret); + } + + // Finished loading. + asset->state = ASSET_STATE_LOADED; + zip_fclose(asset->file); + asset->file = NULL; + errorOk(); +} + +errorret_t assetDispose(asset_t *asset) { + assertNotNull(asset, "Asset cannot be NULL."); + + if(asset->state == ASSET_STATE_LOADED) { + // Dispose of the asset based on its type. + assetdef_t *def = &ASSET_DEFINITIONS[asset->type]; + if(def->dispose) errorChain(def->dispose(asset)); + asset->state = ASSET_STATE_NOT_LOADED; + } + + if(asset->file) { + zip_fclose(asset->file); + asset->file = NULL; + } +} \ No newline at end of file diff --git a/archive/asset/asset.h b/archive/asset/asset.h new file mode 100644 index 0000000..1e43cef --- /dev/null +++ b/archive/asset/asset.h @@ -0,0 +1,104 @@ +/** + * Copyright (c) 2025 Dominic Masters + * + * This software is released under the MIT License. + * https://opensource.org/licenses/MIT + */ + +#pragma once +#include "error/error.h" +#include "util/reflist.h" +#include + +#include "asset/type/assetpaletteimage.h" +#include "asset/type/assetalphaimage.h" + +#define ASSET_HEADER_SIZE 3 +#define ASSET_REFERENCE_COUNT_MAX 8 + +typedef struct { + void (*load)(); +} assetcallback_t; + +typedef enum { + ASSET_STATE_NOT_LOADED, + ASSET_STATE_LOADING, + ASSET_STATE_LOADED, + ASSET_STATE_ERROR, +} assetstate_t; + +typedef enum { + ASSET_TYPE_UNKNOWN, + ASSET_TYPE_PALETTE_IMAGE, + ASSET_TYPE_ALPHA_IMAGE, + + ASSET_TYPE_COUNT +} assettype_t; + +typedef struct asset_s { + char_t filename[FILENAME_MAX]; + ref_t refListArray[ASSET_REFERENCE_COUNT_MAX]; + reflist_t refList; + assetstate_t state; + assettype_t type; + zip_file_t *file; + + union { + assetpaletteimage_t paletteImage; + assetalphaimage_t alphaImage; + }; +} asset_t; + +typedef struct { + const char_t header[ASSET_HEADER_SIZE + 1]; + errorret_t (*load)(asset_t *asset); + errorret_t (*dispose)(asset_t *asset); +} assetdef_t; + +extern assetdef_t ASSET_DEFINITIONS[ASSET_TYPE_COUNT]; + +/** + * Initializes an asset structure. This should be called by the asset manager + * only. + * + * @param asset The asset structure to initialize. + * @param filename The filename of the asset. + * @return An error code. + */ +errorret_t assetInit(asset_t *asset, const char_t *filename); + +/** + * Requests a lock on the given asset. This will increase the reference count + * of the asset, and prevent it from being unloaded until all locks are + * released. + * + * @param asset The asset to lock. + * @return A unique reference ID for the lock. + */ +ref_t assetLock(asset_t *asset); + +/** + * Releases a lock on the given asset. This will decrease the reference count + * of the asset, and allow it to be unloaded if there are no more locks. + * + * @param asset The asset to unlock. + * @param ref The reference ID of the lock to release. + */ +void assetUnlock(asset_t *asset, const ref_t ref); + +/** + * Permission has been granted to load the asset data from disk. This should + * only be called by the asset manager. + * + * @param asset The asset to load. + * @return An error code. + */ +errorret_t assetLoad(asset_t *asset); + +/** + * Disposes of the asset, freeing any allocated memory and closing any open + * file handles. This should only be called by the asset manager. + * + * @param asset The asset to dispose of. + */ +errorret_t assetDispose(asset_t *asset); \ No newline at end of file diff --git a/src/asset/assetmanager.c b/archive/asset/assetmanager.c similarity index 100% rename from src/asset/assetmanager.c rename to archive/asset/assetmanager.c diff --git a/src/asset/assetmanager.h b/archive/asset/assetmanager.h similarity index 100% rename from src/asset/assetmanager.h rename to archive/asset/assetmanager.h diff --git a/src/asset/type/CMakeLists.txt b/archive/asset/type/CMakeLists.txt similarity index 100% rename from src/asset/type/CMakeLists.txt rename to archive/asset/type/CMakeLists.txt diff --git a/src/asset/type/assetalphaimage.c b/archive/asset/type/assetalphaimage.c similarity index 100% rename from src/asset/type/assetalphaimage.c rename to archive/asset/type/assetalphaimage.c diff --git a/src/asset/type/assetalphaimage.h b/archive/asset/type/assetalphaimage.h similarity index 100% rename from src/asset/type/assetalphaimage.h rename to archive/asset/type/assetalphaimage.h diff --git a/src/asset/type/assetpaletteimage.c b/archive/asset/type/assetpaletteimage.c similarity index 100% rename from src/asset/type/assetpaletteimage.c rename to archive/asset/type/assetpaletteimage.c diff --git a/src/asset/type/assetpaletteimage.h b/archive/asset/type/assetpaletteimage.h similarity index 100% rename from src/asset/type/assetpaletteimage.h rename to archive/asset/type/assetpaletteimage.h diff --git a/src/locale/language.c b/archive/language.c similarity index 100% rename from src/locale/language.c rename to archive/language.c diff --git a/src/locale/language.h b/archive/language.h similarity index 100% rename from src/locale/language.h rename to archive/language.h diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 928ad27..3bdc568 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -34,7 +34,7 @@ add_subdirectory(display) add_subdirectory(engine) add_subdirectory(error) add_subdirectory(input) -# add_subdirectory(locale) +add_subdirectory(locale) add_subdirectory(rpg) add_subdirectory(scene) add_subdirectory(thread) diff --git a/src/asset/CMakeLists.txt b/src/asset/CMakeLists.txt index 709261f..cb7cd1f 100644 --- a/src/asset/CMakeLists.txt +++ b/src/asset/CMakeLists.txt @@ -6,9 +6,9 @@ # Sources target_sources(${DUSK_TARGET_NAME} PRIVATE - asset.c - assetmanager.c + # asset.c + # assetmanager.c ) # Subdirs -add_subdirectory(type) \ No newline at end of file +# add_subdirectory(type) \ No newline at end of file diff --git a/src/asset/asset.c b/src/asset/asset.c index 217584f..5fae8ea 100644 --- a/src/asset/asset.c +++ b/src/asset/asset.c @@ -6,134 +6,46 @@ */ #include "asset.h" -#include "assetmanager.h" #include "util/memory.h" +#include "util/string.h" #include "assert/assert.h" -assetdef_t ASSET_DEFINITIONS[ASSET_TYPE_COUNT] = { - [ASSET_TYPE_PALETTE_IMAGE] = { - "DPI", assetPaletteImageLoad, assetPaletteImageDispose - }, - [ASSET_TYPE_ALPHA_IMAGE] = { - "DAI", assetAlphaImageLoad, assetAlphaImageDispose - }, -}; +errorret_t assetInit(void) { + memoryZero(&ASSET_MANAGER, sizeof(assetmanager_t)); + + // Default system path, intended to be overridden by the paltform + stringCopy(ASSET_MANAGER.systemPath, ".", FILENAME_MAX); -errorret_t assetInit(asset_t *asset, const char_t *filename) { - assertNotNull(asset, "Asset cannot be NULL."); - assertNotNull(filename, "Filename cannot be NULL."); - assertTrue(strlen(filename) < FILENAME_MAX, "Filename too long."); - assertTrue(strlen(filename) > 0, "Filename cannot be empty."); + // Open zip file + char_t searchPath[FILENAME_MAX]; + char_t **path = ASSET_SEARCH_PATHS; + do { + sprintf( + searchPath, + *path, + ASSET_MANAGER.systemPath, + ASSET_ASSET_FILE + ); - memoryZero(asset, sizeof(asset_t)); - memoryCopy(asset->filename, filename, strlen(filename) + 1); + // Try open + ASSET.zip = zip_open(searchPath, ZIP_RDONLY, NULL); + if(ASSET.zip == NULL) continue; + break;// Found! + } while(*(++path) != NULL); + + // Did we open the asset? + if(ASSET.zip == NULL) errorThrow("Failed to open asset file."); - // Initialze the reference list. - refListInit( - &asset->refList, - asset->refListArray, - ASSET_REFERENCE_COUNT_MAX - ); - - // Test the file can be opened. In future I may make the handle stay open for - // a while to see if it increases performance and stops disk thrashing. - asset->file = zip_fopen(ASSET_MANAGER.zip, filename, 0); - if(asset->file == NULL) errorThrow("Failed to open asset file: %s", filename); - zip_fclose(asset->file); - asset->file = NULL; errorOk(); } -ref_t assetLock(asset_t *asset) { - assertNotNull(asset, "Asset cannot be NULL."); - - return refListLock(&asset->refList); -} - -void assetUnlock(asset_t *asset, const ref_t ref) { - assertNotNull(asset, "Asset cannot be NULL."); - - // Just unlock the reference in the reference list. - refListUnlock(&asset->refList, ref); -} - -errorret_t assetLoad(asset_t *asset) { - assertNotNull(asset, "Asset cannot be NULL."); - assertTrue( - asset->state == ASSET_STATE_NOT_LOADED, - "Asset is already loaded or loading." - ); - - // Mark as loading. - asset->state = ASSET_STATE_LOADING; - - // Open the file. - if(asset->file == NULL) { - asset->file = zip_fopen(ASSET_MANAGER.zip, asset->filename, 0); - if(asset->file == NULL) { - asset->state = ASSET_STATE_ERROR; - errorThrow("Failed to open asset file: %s", asset->filename); - } - } - - // Read header. - char_t header[ASSET_HEADER_SIZE + 1]; - memoryZero(header, ASSET_HEADER_SIZE + 1); - zip_int64_t bytesRead = zip_fread(asset->file, header, ASSET_HEADER_SIZE); - if(bytesRead != ASSET_HEADER_SIZE) { - asset->state = ASSET_STATE_ERROR; - zip_fclose(asset->file); - asset->file = NULL; - errorThrow("Failed to read asset header for: %s", asset->filename); - } - - // Check header. - if(strlen(header) != ASSET_HEADER_SIZE) { - asset->state = ASSET_STATE_ERROR; - zip_fclose(asset->file); - asset->file = NULL; - errorThrow("Invalid asset header for: %s", asset->filename); - } - - // Get the asset definition based on the header. - assetdef_t *def; - for(uint_fast8_t i = 0; i < ASSET_TYPE_COUNT; i++) { - if(strcmp(header, ASSET_DEFINITIONS[i].header) == 0) { - def = &ASSET_DEFINITIONS[i]; - asset->type = i; - break; - } - } - assertNotNull(def, "Failed to find asset definition for header."); - - // Load the asset - errorret_t ret = def->load(asset); - if(ret.code != ERROR_OK) { - asset->state = ASSET_STATE_ERROR; - zip_fclose(asset->file); - asset->file = NULL; - errorChain(ret); - } - - // Finished loading. - asset->state = ASSET_STATE_LOADED; - zip_fclose(asset->file); - asset->file = NULL; +erroret_t assetLoad(const char_t *filename, void *output) { errorOk(); } -errorret_t assetDispose(asset_t *asset) { - assertNotNull(asset, "Asset cannot be NULL."); - - if(asset->state == ASSET_STATE_LOADED) { - // Dispose of the asset based on its type. - assetdef_t *def = &ASSET_DEFINITIONS[asset->type]; - if(def->dispose) errorChain(def->dispose(asset)); - asset->state = ASSET_STATE_NOT_LOADED; - } - - if(asset->file) { - zip_fclose(asset->file); - asset->file = NULL; +void assetDispose(void) { + if(ASSET_MANAGER.zip != NULL) { + zip_close(ASSET_MANAGER.zip); + ASSET_MANAGER.zip = NULL; } } \ No newline at end of file diff --git a/src/asset/asset.h b/src/asset/asset.h index 1e43cef..8f5be42 100644 --- a/src/asset/asset.h +++ b/src/asset/asset.h @@ -7,98 +7,48 @@ #pragma once #include "error/error.h" -#include "util/reflist.h" #include -#include "asset/type/assetpaletteimage.h" -#include "asset/type/assetalphaimage.h" +#if ASSET_TYPE == wad +#else + #error "Unsupported ASSET_TYPE" +#endif -#define ASSET_HEADER_SIZE 3 -#define ASSET_REFERENCE_COUNT_MAX 8 +#define ASSET_FILE "dusk.dsk" + +static const char_t *ASSET_SEARCH_PATHS[] = { + "%s/%s", + "%s", + "../%s", + "../../%s", + "data/%s", + "../data/%s", + NULL +}; typedef struct { - void (*load)(); -} assetcallback_t; - -typedef enum { - ASSET_STATE_NOT_LOADED, - ASSET_STATE_LOADING, - ASSET_STATE_LOADED, - ASSET_STATE_ERROR, -} assetstate_t; - -typedef enum { - ASSET_TYPE_UNKNOWN, - ASSET_TYPE_PALETTE_IMAGE, - ASSET_TYPE_ALPHA_IMAGE, - - ASSET_TYPE_COUNT -} assettype_t; - -typedef struct asset_s { - char_t filename[FILENAME_MAX]; - ref_t refListArray[ASSET_REFERENCE_COUNT_MAX]; - reflist_t refList; - assetstate_t state; - assettype_t type; - zip_file_t *file; - - union { - assetpaletteimage_t paletteImage; - assetalphaimage_t alphaImage; - }; + zip_t *zip; + char_t systemPath[FILENAME_MAX]; + uint8_t assetCount; } asset_t; -typedef struct { - const char_t header[ASSET_HEADER_SIZE + 1]; - errorret_t (*load)(asset_t *asset); - errorret_t (*dispose)(asset_t *asset); -} assetdef_t; - -extern assetdef_t ASSET_DEFINITIONS[ASSET_TYPE_COUNT]; +static asset_t ASSET; /** - * Initializes an asset structure. This should be called by the asset manager - * only. - * - * @param asset The asset structure to initialize. - * @param filename The filename of the asset. - * @return An error code. + * Initializes the asset system. */ -errorret_t assetInit(asset_t *asset, const char_t *filename); +errorret_t assetInit(void); /** - * Requests a lock on the given asset. This will increase the reference count - * of the asset, and prevent it from being unloaded until all locks are - * released. + * Loads an asset by its filename, the output type depends on the asset type. * - * @param asset The asset to lock. - * @return A unique reference ID for the lock. + * @param filename The filename of the asset to retrieve. + * @param output The output pointer to store the loaded asset data. + * @return An error code if the asset could not be loaded. */ -ref_t assetLock(asset_t *asset); +errorret_t assetLoad(const char_t *filename, void *output); /** - * Releases a lock on the given asset. This will decrease the reference count - * of the asset, and allow it to be unloaded if there are no more locks. - * - * @param asset The asset to unlock. - * @param ref The reference ID of the lock to release. + * Disposes/cleans up the asset system. */ -void assetUnlock(asset_t *asset, const ref_t ref); - -/** - * Permission has been granted to load the asset data from disk. This should - * only be called by the asset manager. - * - * @param asset The asset to load. - * @return An error code. - */ -errorret_t assetLoad(asset_t *asset); - -/** - * Disposes of the asset, freeing any allocated memory and closing any open - * file handles. This should only be called by the asset manager. - * - * @param asset The asset to dispose of. - */ -errorret_t assetDispose(asset_t *asset); \ No newline at end of file +void assetDispose(void); \ No newline at end of file diff --git a/src/engine/engine.c b/src/engine/engine.c index 81514f5..a226644 100644 --- a/src/engine/engine.c +++ b/src/engine/engine.c @@ -9,6 +9,7 @@ #include "util/memory.h" #include "time/time.h" #include "input/input.h" +#include "locale/localemanager.h" #include "display/display.h" #include "scene/scenemanager.h" #include "asset/assetmanager.h" @@ -27,6 +28,7 @@ errorret_t engineInit(void) { // Init systems. Order is important. timeInit(); inputInit(); + localeManagerInit(); errorChain(assetManagerInit()); errorChain(displayInit()); errorChain(uiInit()); diff --git a/src/error/error.h b/src/error/error.h index ebefd2c..85e60fb 100644 --- a/src/error/error.h +++ b/src/error/error.h @@ -21,7 +21,6 @@ typedef struct { errorstate_t *state; } errorret_t; - static const errorcode_t ERROR_OK = 0; static const errorcode_t ERROR_NOT_OK = 1; diff --git a/src/locale/CMakeLists.txt b/src/locale/CMakeLists.txt index ef60e6d..6ba5c6d 100644 --- a/src/locale/CMakeLists.txt +++ b/src/locale/CMakeLists.txt @@ -6,5 +6,5 @@ # Sources target_sources(${DUSK_TARGET_NAME} PRIVATE - language.c + localemanager.c ) \ No newline at end of file diff --git a/src/locale/localemanager.c b/src/locale/localemanager.c new file mode 100644 index 0000000..45a78d8 --- /dev/null +++ b/src/locale/localemanager.c @@ -0,0 +1,15 @@ +/** + * Copyright (c) 2025 Dominic Masters + * + * This software is released under the MIT License. + * https://opensource.org/licenses/MIT + */ + +#include "localemanager.h" +#include "util/memory.h" + +localemanager_t LOCALE; + +void localeManagerInit() { + memoryZero(&LOCALE, sizeof(localemanager_t)); +} \ No newline at end of file diff --git a/src/locale/localemanager.h b/src/locale/localemanager.h new file mode 100644 index 0000000..557fc90 --- /dev/null +++ b/src/locale/localemanager.h @@ -0,0 +1,20 @@ +/** + * Copyright (c) 2025 Dominic Masters + * + * This software is released under the MIT License. + * https://opensource.org/licenses/MIT + */ + +#pragma once +#include "dusk.h" + +typedef struct { + void *nothing; +} localemanager_t; + +extern localemanager_t LOCALE; + +/** + * Initialize the locale system. + */ +void localeManagerInit(); \ No newline at end of file diff --git a/src/main.c b/src/main.c index e32d29d..51025ae 100644 --- a/src/main.c +++ b/src/main.c @@ -6,7 +6,7 @@ */ #include "engine/engine.h" -#include "asset/assetmanager.h" +#include "asset/asset.h" #include "util/string.h" #include "input/input.h" @@ -23,8 +23,8 @@ int main(int argc, char **argv) { // Setup system path on asset manager if(argc > 0) { stringCopy( - ASSET_MANAGER.systemPath, argv[0], - sizeof(ASSET_MANAGER.systemPath) / sizeof(char_t) + ASSET.systemPath, argv[0], + sizeof(ASSET.systemPath) / sizeof(char_t) ); } diff --git a/src/ui/ui.h b/src/ui/ui.h index 529aa86..ccc55de 100644 --- a/src/ui/ui.h +++ b/src/ui/ui.h @@ -7,7 +7,7 @@ #pragma once #include "dusk.h" -#include "asset/assetmanager.h" +#include "asset/asset.h" #include "display/tileset/tileset.h" #include "display/camera.h" diff --git a/src/ui/uitext.h b/src/ui/uitext.h index 8fa63d3..70a25e6 100644 --- a/src/ui/uitext.h +++ b/src/ui/uitext.h @@ -6,7 +6,7 @@ */ #pragma once -#include "asset/assetmanager.h" +#include "asset/asset.h" #include "display/tileset/tileset_minogram.h" #define UI_TEXT_CHAR_START '!' diff --git a/tools/assetstool/processpalette.py b/tools/assetstool/processpalette.py index ae8d22c..7abbd50 100644 --- a/tools/assetstool/processpalette.py +++ b/tools/assetstool/processpalette.py @@ -36,7 +36,6 @@ def processPalette(asset): # PSP requires that the palette size be a power of two, so we will pad the # palette with transparent colors if needed. - # if args.platform == "psp": def mathNextPowTwo(x): return 1 << (x - 1).bit_length() @@ -53,7 +52,7 @@ def processPalette(asset): data += f"static const color_t PALETTE_{paletteIndex}_COLORS[PALETTE_{paletteIndex}_COLOR_COUNT] = {{\n" for pixel in pixels: data += f" {{ 0x{pixel[0]:02X}, 0x{pixel[1]:02X}, 0x{pixel[2]:02X}, 0x{pixel[3]:02X} }},\n" - data += f"}};\n\n" + data += f"}};\n" data += f"#pragma pack(pop)\n\n" data += f"static const palette_t PALETTE_{paletteIndex} = {{\n" data += f" .colorCount = PALETTE_{paletteIndex}_COLOR_COUNT,\n"