diff --git a/src/dusk/asset/asset.c b/src/dusk/asset/asset.c index 06f8b38..b5ec6d9 100644 --- a/src/dusk/asset/asset.c +++ b/src/dusk/asset/asset.c @@ -14,208 +14,14 @@ #include "debug/debug.h" #include "util/string.h" +asset_t ASSET; + errorret_t assetInit(void) { memoryZero(&ASSET, sizeof(asset_t)); - - #if DOLPHIN - // Init FAT driver. - if(!fatInitDefault()) errorThrow("Failed to initialize FAT filesystem."); - - char_t **dolphinSearchPath = (char_t **)ASSET_DOLPHIN_PATHS; - char_t foundPath[FILENAME_MAX]; - foundPath[0] = '\0'; - do { - // Try open dir - DIR *pdir = opendir(*dolphinSearchPath); - if(pdir == NULL) continue; - - - // Scan if file is present - while(true) { - struct dirent* pent = readdir(pdir); - if(pent == NULL) break; - - if(stringCompareInsensitive(pent->d_name, ASSET_FILE) != 0) { - continue; - } - - // Copy out filename - snprintf( - foundPath, - FILENAME_MAX, - "%s/%s", - *dolphinSearchPath, - ASSET_FILE - ); - break; - } - - // Close dir. - closedir(pdir); - - // Did we find the file here? - if(foundPath[0] != '\0') break; - } while(*(++dolphinSearchPath) != NULL); - - if(foundPath[0] != '\0') { - } - - // Did we find the asset file? - if(foundPath[0] == '\0') { - errorThrow("Failed to find asset file on FAT filesystem."); - } - - ASSET.zip = zip_open(foundPath, ZIP_RDONLY, NULL); - if(ASSET.zip == NULL) { - errorThrow("Failed to open asset file on FAT filesystem."); - } - errorOk(); - #endif - - // Engine may have been provided the launch path - if(ENGINE.argc > 0) { - // Get the directory of the executable - char_t buffer[FILENAME_MAX]; - stringCopy(buffer, ENGINE.argv[0], FILENAME_MAX); - size_t len = strlen(buffer); - - // Normalize slashes - for(size_t i = 0; i < FILENAME_MAX; i++) { - if(buffer[i] == '\0') break; - if(buffer[i] == '\\') buffer[i] = '/'; - } - - // Now find the last slash - char_t *end = buffer + len - 1; - do { - end--; - if(*end == '/') { - *end = '\0'; - break; - } - } while(end != buffer); - - - // Did we find a slash? - if(end != buffer) { - // We found the directory, set as system path - stringCopy(ASSET.systemPath, buffer, FILENAME_MAX); - } - } - - // Default system path, intended to be overridden by the platform - stringCopy(ASSET.systemPath, ".", FILENAME_MAX); - - // PSP specific asset loading. - #if PSP - assertTrue(ENGINE.argc >= 1, "PSP requires launch argument."); - - // PSP is given either the prx OR the PBP file. - // In the format of "ms0:/PSP/GAME/DUSK/EBOOT.PBP" or "host0:/Dusk.prx" - // IF the file is the PBP file, we are loading directly on the PSP itself. - // IF the file is the .prx then we are debugging and fopen will return - // relative filepaths correctly, e.g. host0:/dusk.dsk will be on host. - if( - stringEndsWithCaseInsensitive(ENGINE.argv[0], ".pbp") || - ASSET_PBP_READ_PBP_FROM_HOST - ) { - const char_t *pbpPath = ( - ASSET_PBP_READ_PBP_FROM_HOST ? "./EBOOT.PBP" : ENGINE.argv[0] - ); - ASSET.pbpFile = fopen(pbpPath, "rb"); - if(ASSET.pbpFile == NULL) { - errorThrow("Failed to open PBP file: %s", pbpPath); - } - - // Get size of PBP file. - if(fseek(ASSET.pbpFile, 0, SEEK_END) != 0) { - fclose(ASSET.pbpFile); - errorThrow("Failed to seek to end of PBP file : %s", pbpPath); - } - size_t pbpSize = ftell(ASSET.pbpFile); - - // Rewind to start - if(fseek(ASSET.pbpFile, 0, SEEK_SET) != 0) { - fclose(ASSET.pbpFile); - errorThrow("Failed to seek to start of PBP file : %s", pbpPath); - } - - // Read the PBP header - size_t read = fread( - &ASSET.pbpHeader, - 1, - sizeof(assetpbp_t), - ASSET.pbpFile - ); - if(read != sizeof(assetpbp_t)) { - fclose(ASSET.pbpFile); - errorThrow("Failed to read PBP header", pbpPath); - } - if(memoryCompare( - ASSET.pbpHeader.signature, - ASSET_PBP_SIGNATURE, - sizeof(ASSET_PBP_SIGNATURE) - ) != 0) { - fclose(ASSET.pbpFile); - errorThrow("Invalid PBP signature in file: %s", pbpPath); - } - - // If we seek to the PSAR offset, we can read the WAD file from there - if(fseek(ASSET.pbpFile, ASSET.pbpHeader.psarOffset, SEEK_SET) != 0) { - fclose(ASSET.pbpFile); - errorThrow("Failed to seek to PSAR offset in PBP file: %s", pbpPath); - } - - zip_uint64_t zipPsarOffset = (zip_uint64_t)ASSET.pbpHeader.psarOffset; - zip_int64_t zipPsarSize = (zip_int64_t)( - pbpSize - ASSET.pbpHeader.psarOffset - ); - - zip_source_t *psarSource = zip_source_filep_create( - ASSET.pbpFile, - zipPsarOffset, - zipPsarSize, - NULL - ); - if(psarSource == NULL) { - fclose(ASSET.pbpFile); - errorThrow("Failed to create zip source in PBP file: %s", pbpPath); - } - - ASSET.zip = zip_open_from_source( - psarSource, - ZIP_RDONLY, - NULL - ); - if(ASSET.zip == NULL) { - zip_source_free(psarSource); - fclose(ASSET.pbpFile); - errorThrow("Failed to open zip from PBP file: %s", pbpPath); - } - - errorOk(); - } - #endif - - // Open zip file - char_t searchPath[FILENAME_MAX]; - const char_t **path = ASSET_SEARCH_PATHS; - do { - sprintf( - searchPath, - *path, - ASSET.systemPath, - ASSET_FILE - ); - - // 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."); + // assetInitPlatform must either define ASSET.zip or throw an error. + errorChain(assetInitPlatform()); + assertNotNull(ASSET.zip, "Asset zip null without error."); errorOk(); } @@ -330,16 +136,14 @@ errorret_t assetLoad(const char_t *filename, void *output) { errorOk(); } -void assetDispose(void) { +errorret_t assetDispose(void) { if(ASSET.zip != NULL) { - zip_close(ASSET.zip); + if(zip_close(ASSET.zip) != 0) { + errorThrow("Failed to close asset zip archive."); + } ASSET.zip = NULL; } - - #if PSP - if(ASSET.pbpFile != NULL) { - fclose(ASSET.pbpFile); - ASSET.pbpFile = NULL; - } - #endif + + errorChain(assetDisposePlatform()); + errorOk(); } \ No newline at end of file diff --git a/src/dusk/asset/asset.h b/src/dusk/asset/asset.h index b786952..26bc203 100644 --- a/src/dusk/asset/asset.h +++ b/src/dusk/asset/asset.h @@ -8,83 +8,24 @@ #pragma once #include "error/error.h" #include "assettype.h" +#include "asset/assetplatform.h" -#if PSP - #define ASSET_PBP_READ_PBP_FROM_HOST 0 - #define ASSET_PBP_SIGNATURE_SIZE 4 - #define ASSET_PBP_SIGNATURE "\0PBP" - - typedef struct { - char_t signature[ASSET_PBP_SIGNATURE_SIZE]; - uint32_t version; - uint32_t sfoOffset; - uint32_t icon0Offset; - uint32_t icon1Offset; - uint32_t pic0Offset; - uint32_t pic1Offset; - uint32_t snd0Offset; - uint32_t pspOffset; - uint32_t psarOffset; - } assetpbp_t; - -#elif DOLPHIN - #include - #include - #include - #include - #include - #include - #include - #include - - static const char_t *ASSET_DOLPHIN_PATHS[] = { - "/", - "/Dusk", - "/dusk", - "/DUSK", - "/apps", - "/apps/Dusk", - "/apps/dusk", - "/apps/DUSK", - ".", - "./", - "./Dusk", - "./dusk", - "./DUSK", - "./apps", - "./apps/Dusk", - "./apps/dusk", - "./apps/DUSK", - NULL - }; +#ifndef assetInitPlatform + #error "Platform must define assetInitPlatform function." +#endif +#ifndef assetDisposePlatform + #error "Platform must define assetDisposePlatform function." #endif -#define ASSET_FILE "dusk.dsk" +#define ASSET_FILE_NAME "dusk.dsk" #define ASSET_HEADER_SIZE 3 -static const char_t *ASSET_SEARCH_PATHS[] = { - "%s/%s", - "%s", - "../%s", - "../../%s", - "data/%s", - "../data/%s", - NULL -}; - typedef struct { zip_t *zip; - char_t systemPath[FILENAME_MAX]; - uint8_t assetCount; - - // PSP specific information. - #if PSP - FILE *pbpFile; - assetpbp_t pbpHeader; - #endif + assetplatform_t platform; } asset_t; -static asset_t ASSET; +extern asset_t ASSET; /** * Initializes the asset system. @@ -110,5 +51,7 @@ errorret_t assetLoad(const char_t *filename, void *output); /** * Disposes/cleans up the asset system. + * + * @return An error code if the asset system could not be disposed properly. */ -void assetDispose(void); \ No newline at end of file +errorret_t assetDispose(void); \ No newline at end of file diff --git a/src/dusk/engine/engine.c b/src/dusk/engine/engine.c index 9dc2cd3..80001a6 100644 --- a/src/dusk/engine/engine.c +++ b/src/dusk/engine/engine.c @@ -77,6 +77,6 @@ errorret_t engineDispose(void) { localeManagerDispose(); uiDispose(); errorChain(displayDispose()); - assetDispose(); + errorChain(assetDispose()); errorOk(); } \ No newline at end of file diff --git a/src/duskdolphin/asset/assetdolphin.c b/src/duskdolphin/asset/assetdolphin.c new file mode 100644 index 0000000..36c1ded --- /dev/null +++ b/src/duskdolphin/asset/assetdolphin.c @@ -0,0 +1,53 @@ + + // Init FAT driver. + if(!fatInitDefault()) errorThrow("Failed to initialize FAT filesystem."); + + char_t **dolphinSearchPath = (char_t **)ASSET_DOLPHIN_PATHS; + char_t foundPath[FILENAME_MAX]; + foundPath[0] = '\0'; + do { + // Try open dir + DIR *pdir = opendir(*dolphinSearchPath); + if(pdir == NULL) continue; + + + // Scan if file is present + while(true) { + struct dirent* pent = readdir(pdir); + if(pent == NULL) break; + + if(stringCompareInsensitive(pent->d_name, ASSET_FILE) != 0) { + continue; + } + + // Copy out filename + snprintf( + foundPath, + FILENAME_MAX, + "%s/%s", + *dolphinSearchPath, + ASSET_FILE + ); + break; + } + + // Close dir. + closedir(pdir); + + // Did we find the file here? + if(foundPath[0] != '\0') break; + } while(*(++dolphinSearchPath) != NULL); + + if(foundPath[0] != '\0') { + } + + // Did we find the asset file? + if(foundPath[0] == '\0') { + errorThrow("Failed to find asset file on FAT filesystem."); + } + + ASSET.zip = zip_open(foundPath, ZIP_RDONLY, NULL); + if(ASSET.zip == NULL) { + errorThrow("Failed to open asset file on FAT filesystem."); + } + errorOk(); \ No newline at end of file diff --git a/src/duskdolphin/asset/assetdolphin.h b/src/duskdolphin/asset/assetdolphin.h new file mode 100644 index 0000000..0d7e5cd --- /dev/null +++ b/src/duskdolphin/asset/assetdolphin.h @@ -0,0 +1,37 @@ +/** + * Copyright (c) 2026 Dominic Masters + * + * This software is released under the MIT License. + * https://opensource.org/licenses/MIT + */ + +#pragma once +#include +#include +#include +#include +#include +#include +#include +#include + +static const char_t *ASSET_DOLPHIN_PATHS[] = { + "/", + "/Dusk", + "/dusk", + "/DUSK", + "/apps", + "/apps/Dusk", + "/apps/dusk", + "/apps/DUSK", + ".", + "./", + "./Dusk", + "./dusk", + "./DUSK", + "./apps", + "./apps/Dusk", + "./apps/dusk", + "./apps/DUSK", + NULL +}; \ No newline at end of file diff --git a/src/dusklinux/CMakeLists.txt b/src/dusklinux/CMakeLists.txt index 6b8f187..ee7f65c 100644 --- a/src/dusklinux/CMakeLists.txt +++ b/src/dusklinux/CMakeLists.txt @@ -10,4 +10,5 @@ target_include_directories(${DUSK_LIBRARY_TARGET_NAME} ) # Subdirs +add_subdirectory(asset) add_subdirectory(debug) \ No newline at end of file diff --git a/src/dusklinux/asset/CMakeLists.txt b/src/dusklinux/asset/CMakeLists.txt new file mode 100644 index 0000000..3910a3b --- /dev/null +++ b/src/dusklinux/asset/CMakeLists.txt @@ -0,0 +1,10 @@ +# Copyright (c) 2026 Dominic Masters +# +# This software is released under the MIT License. +# https://opensource.org/licenses/MIT + +# Sources +target_sources(${DUSK_LIBRARY_TARGET_NAME} + PUBLIC + assetlinux.c +) \ No newline at end of file diff --git a/src/dusklinux/asset/assetlinux.c b/src/dusklinux/asset/assetlinux.c new file mode 100644 index 0000000..6f1e228 --- /dev/null +++ b/src/dusklinux/asset/assetlinux.c @@ -0,0 +1,81 @@ +/** + * Copyright (c) 2026 Dominic Masters + * + * This software is released under the MIT License. + * https://opensource.org/licenses/MIT + */ + +#include "asset/asset.h" +#include "engine/engine.h" +#include "util/string.h" +#include "assert/assert.h" + +errorret_t assetInitLinux(void) { + // Engine may have been provided the launch path + if(ENGINE.argc > 0) { + // Get the directory of the executable + char_t buffer[FILENAME_MAX]; + stringCopy(buffer, ENGINE.argv[0], FILENAME_MAX); + size_t len = strlen(buffer); + + // Normalize slashes + for(size_t i = 0; i < FILENAME_MAX; i++) { + if(buffer[i] == '\0') break; + if(buffer[i] == '\\') buffer[i] = '/'; + } + + // Now find the last slash + char_t *end = buffer + len - 1; + do { + end--; + if(*end == '/') { + *end = '\0'; + break; + } + } while(end != buffer); + + + // Did we find a slash? + if(end != buffer) { + // We found the directory, set as system path + stringCopy(ASSET.platform.systemPath, buffer, FILENAME_MAX); + } + } + + // Default system path, intended to be overridden by the platform + stringCopy(ASSET.platform.systemPath, ".", FILENAME_MAX); + + // Open zip file + char_t searchPath[FILENAME_MAX]; + const char_t **path = ASSET_LINUX_SEARCH_PATHS; + int32_t error; + do { + sprintf( + searchPath, + *path, + ASSET.platform.systemPath, + ASSET_FILE_NAME + ); + + // Try open + ASSET.zip = zip_open(searchPath, ZIP_RDONLY, &error); + if(ASSET.zip == NULL) continue; + if(error != 0) { + printf("Warning: Opened asset file with non-zero error code: %d\n", error); + ASSET.zip = NULL; + continue; + } + break;// Found! + } while(*(++path) != NULL); + + // Did we open the asset? + if(ASSET.zip == NULL) { + errorThrow("Failed to open asset file."); + } + + errorOk(); +} + +errorret_t assetDisposeLinux(void) { + errorOk(); +} \ No newline at end of file diff --git a/src/dusklinux/asset/assetlinux.h b/src/dusklinux/asset/assetlinux.h new file mode 100644 index 0000000..eda85a4 --- /dev/null +++ b/src/dusklinux/asset/assetlinux.h @@ -0,0 +1,37 @@ +/** + * Copyright (c) 2026 Dominic Masters + * + * This software is released under the MIT License. + * https://opensource.org/licenses/MIT + */ + +#pragma once +#include "error/error.h" + +static const char_t *ASSET_LINUX_SEARCH_PATHS[] = { + "%s/%s", + "%s", + "../%s", + "../../%s", + "data/%s", + "../data/%s", + NULL +}; + +typedef struct { + char_t systemPath[FILENAME_MAX]; +} assetlinux_t; + +/** + * Initializes the asset system on Linux. + * + * @return Error state if failed, otherwise returns success. + */ +errorret_t assetInitLinux(void); + +/** + * Disposes the asset system on Linux. + * + * @return Error state if failed, otherwise returns success. + */ +errorret_t assetDisposeLinux(void); \ No newline at end of file diff --git a/src/dusklinux/asset/assetplatform.h b/src/dusklinux/asset/assetplatform.h new file mode 100644 index 0000000..669b870 --- /dev/null +++ b/src/dusklinux/asset/assetplatform.h @@ -0,0 +1,13 @@ +/** + * Copyright (c) 2026 Dominic Masters + * + * This software is released under the MIT License. + * https://opensource.org/licenses/MIT + */ + +#pragma once +#include "asset/assetlinux.h" + +typedef assetlinux_t assetplatform_t; +#define assetInitPlatform assetInitLinux +#define assetDisposePlatform assetDisposeLinux \ No newline at end of file diff --git a/src/duskpsp/asset/assetpbp.c b/src/duskpsp/asset/assetpbp.c new file mode 100644 index 0000000..e242056 --- /dev/null +++ b/src/duskpsp/asset/assetpbp.c @@ -0,0 +1,87 @@ +assertTrue(ENGINE.argc >= 1, "PSP requires launch argument."); + + // PSP is given either the prx OR the PBP file. + // In the format of "ms0:/PSP/GAME/DUSK/EBOOT.PBP" or "host0:/Dusk.prx" + // IF the file is the PBP file, we are loading directly on the PSP itself. + // IF the file is the .prx then we are debugging and fopen will return + // relative filepaths correctly, e.g. host0:/dusk.dsk will be on host. + if( + stringEndsWithCaseInsensitive(ENGINE.argv[0], ".pbp") || + ASSET_PBP_READ_PBP_FROM_HOST + ) { + const char_t *pbpPath = ( + ASSET_PBP_READ_PBP_FROM_HOST ? "./EBOOT.PBP" : ENGINE.argv[0] + ); + ASSET.pbpFile = fopen(pbpPath, "rb"); + if(ASSET.pbpFile == NULL) { + errorThrow("Failed to open PBP file: %s", pbpPath); + } + + // Get size of PBP file. + if(fseek(ASSET.pbpFile, 0, SEEK_END) != 0) { + fclose(ASSET.pbpFile); + errorThrow("Failed to seek to end of PBP file : %s", pbpPath); + } + size_t pbpSize = ftell(ASSET.pbpFile); + + // Rewind to start + if(fseek(ASSET.pbpFile, 0, SEEK_SET) != 0) { + fclose(ASSET.pbpFile); + errorThrow("Failed to seek to start of PBP file : %s", pbpPath); + } + + // Read the PBP header + size_t read = fread( + &ASSET.pbpHeader, + 1, + sizeof(assetpbp_t), + ASSET.pbpFile + ); + if(read != sizeof(assetpbp_t)) { + fclose(ASSET.pbpFile); + errorThrow("Failed to read PBP header", pbpPath); + } + if(memoryCompare( + ASSET.pbpHeader.signature, + ASSET_PBP_SIGNATURE, + sizeof(ASSET_PBP_SIGNATURE) + ) != 0) { + fclose(ASSET.pbpFile); + errorThrow("Invalid PBP signature in file: %s", pbpPath); + } + + // If we seek to the PSAR offset, we can read the WAD file from there + if(fseek(ASSET.pbpFile, ASSET.pbpHeader.psarOffset, SEEK_SET) != 0) { + fclose(ASSET.pbpFile); + errorThrow("Failed to seek to PSAR offset in PBP file: %s", pbpPath); + } + + zip_uint64_t zipPsarOffset = (zip_uint64_t)ASSET.pbpHeader.psarOffset; + zip_int64_t zipPsarSize = (zip_int64_t)( + pbpSize - ASSET.pbpHeader.psarOffset + ); + + zip_source_t *psarSource = zip_source_filep_create( + ASSET.pbpFile, + zipPsarOffset, + zipPsarSize, + NULL + ); + if(psarSource == NULL) { + fclose(ASSET.pbpFile); + errorThrow("Failed to create zip source in PBP file: %s", pbpPath); + } + + ASSET.zip = zip_open_from_source( + psarSource, + ZIP_RDONLY, + NULL + ); + if(ASSET.zip == NULL) { + zip_source_free(psarSource); + fclose(ASSET.pbpFile); + errorThrow("Failed to open zip from PBP file: %s", pbpPath); + } + + errorOk(); + } \ No newline at end of file diff --git a/src/duskpsp/asset/assetpbp.h b/src/duskpsp/asset/assetpbp.h new file mode 100644 index 0000000..512251d --- /dev/null +++ b/src/duskpsp/asset/assetpbp.h @@ -0,0 +1,31 @@ +/** + * Copyright (c) 2026 Dominic Masters + * + * This software is released under the MIT License. + * https://opensource.org/licenses/MIT + */ + +#pragma once +#include "dusk.h" + +#define ASSET_PBP_READ_PBP_FROM_HOST 0 +#define ASSET_PBP_SIGNATURE_SIZE 4 +#define ASSET_PBP_SIGNATURE "\0PBP" + +typedef struct { + char_t signature[ASSET_PBP_SIGNATURE_SIZE]; + uint32_t version; + uint32_t sfoOffset; + uint32_t icon0Offset; + uint32_t icon1Offset; + uint32_t pic0Offset; + uint32_t pic1Offset; + uint32_t snd0Offset; + uint32_t pspOffset; + uint32_t psarOffset; +} assetpbpheader_t; + +typedef struct { + FILE *pbpFile; + assetpbpheader_t pbpHeader; +} assetpbp_t; \ No newline at end of file diff --git a/src/dusksdl2/display/displaysdl2.c b/src/dusksdl2/display/displaysdl2.c index f5e5473..0b03325 100644 --- a/src/dusksdl2/display/displaysdl2.c +++ b/src/dusksdl2/display/displaysdl2.c @@ -48,10 +48,6 @@ errorret_t displaySDL2Init(void) { errorChain(errorGLCheck()); errorChain(displayOpenGLInit()); - - // #if DUSK_PSP - // errorChain(displayPSPInit()); - // #endif errorChain(errorGLCheck()); errorOk();