C -> CPP
This commit is contained in:
272
src/asset/asset.cpp
Normal file
272
src/asset/asset.cpp
Normal file
@@ -0,0 +1,272 @@
|
||||
/**
|
||||
* Copyright (c) 2025 Dominic Masters
|
||||
*
|
||||
* This software is released under the MIT License.
|
||||
* https://opensource.org/licenses/MIT
|
||||
*/
|
||||
|
||||
#include "asset.hpp"
|
||||
#include "util/memory.hpp"
|
||||
#include "util/string.hpp"
|
||||
#include "assert/assert.hpp"
|
||||
#include "asset/assettype.h"
|
||||
#include "engine/engine.hpp"
|
||||
#include "debug/debug.hpp"
|
||||
|
||||
errorret_t assetInit(void) {
|
||||
memoryZero(&ASSET, sizeof(asset_t));
|
||||
|
||||
// Engine may have been provided the launch path
|
||||
if(ENGINE.argc > 0) {
|
||||
// This first arg is the executable, so on most platforms it is say
|
||||
// "/path/file" or "C:\Path\file.exe". On PSP this would be something
|
||||
// like "ms0:/PSP/GAME/DUSK/EBOOT.PBP" or if we are debugging it is
|
||||
// "host0:/Dusk.prx"
|
||||
|
||||
// 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 time.
|
||||
#if PSP
|
||||
assertTrue(ENGINE.argc >= 1, "PSP requires launch argument.");
|
||||
|
||||
// PSP is given either host0:/Dusk.prx (debugging) OR the PBP file.
|
||||
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.");
|
||||
|
||||
errorOk();
|
||||
}
|
||||
|
||||
bool_t assetFileExists(const char_t *filename) {
|
||||
assertStrLenMax(filename, FILENAME_MAX, "Filename too long.");
|
||||
|
||||
zip_int64_t idx = zip_name_locate(ASSET.zip, filename, 0);
|
||||
if(idx < 0) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
errorret_t assetLoad(const char_t *filename, void *output) {
|
||||
assertStrLenMax(filename, FILENAME_MAX, "Filename too long.");
|
||||
assertNotNull(output, "Output pointer cannot be NULL.");
|
||||
|
||||
// Try to open the file
|
||||
zip_file_t *file = zip_fopen(ASSET.zip, filename, 0);
|
||||
if(file == NULL) {
|
||||
errorThrow("Failed to open asset file: %s", filename);
|
||||
}
|
||||
|
||||
// Read the header.
|
||||
assetheader_t header;
|
||||
memoryZero(&header, sizeof(assetheader_t));
|
||||
zip_int64_t bytesRead = zip_fread(file, &header, sizeof(assetheader_t));
|
||||
if(bytesRead != sizeof(assetheader_t)) {
|
||||
zip_fclose(file);
|
||||
errorThrow("Failed to read asset header for: %s", filename);
|
||||
}
|
||||
|
||||
// Find the asset type based on the header
|
||||
const assettypedef_t *def = NULL;
|
||||
for(uint_fast8_t i = 0; i < ASSET_TYPE_COUNT; i++) {
|
||||
const assettypedef_t *cmp = &ASSET_TYPE_DEFINITIONS[i];
|
||||
if(cmp->header == NULL) continue;
|
||||
|
||||
// strcmp didn't work because it's a fixed char_t[3] I think, or maybe
|
||||
// because of the packed struct?
|
||||
bool_t match = true;
|
||||
for(size_t h = 0; h < ASSET_HEADER_SIZE; h++) {
|
||||
if(header.header[h] == cmp->header[h]) continue;
|
||||
match = false;
|
||||
break;
|
||||
}
|
||||
if(!match) continue;
|
||||
|
||||
def = cmp;
|
||||
break;
|
||||
}
|
||||
if(def == NULL) {
|
||||
zip_fclose(file);
|
||||
errorThrow("Unknown asset type for file: %s", filename);
|
||||
}
|
||||
|
||||
// We found the asset type, now load the asset data
|
||||
switch(def->loadStrategy) {
|
||||
case ASSET_LOAD_STRAT_ENTIRE: {
|
||||
assertNotNull(def->entire, "Asset load function cannot be NULL.");
|
||||
void *data = memoryAllocate(def->dataSize);
|
||||
bytesRead = zip_fread(file, data, def->dataSize);
|
||||
if(bytesRead == 0 || bytesRead > def->dataSize) {
|
||||
memoryFree(data);
|
||||
zip_fclose(file);
|
||||
errorThrow("Failed to read asset data for file: %s", filename);
|
||||
}
|
||||
|
||||
// Close the file now we have the data
|
||||
zip_fclose(file);
|
||||
|
||||
// Pass to the asset type loader
|
||||
errorret_t ret = def->entire(data, output);
|
||||
memoryFree(data);
|
||||
errorChain(ret);
|
||||
break;
|
||||
}
|
||||
|
||||
case ASSET_LOAD_STRAT_CUSTOM: {
|
||||
assertNotNull(def->custom, "Asset load function cannot be NULL.");
|
||||
assetcustom_t customData = {
|
||||
.zipFile = file,
|
||||
.output = output
|
||||
};
|
||||
errorChain(def->custom(customData));
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
assertUnreachable("Unknown asset load strategy.");
|
||||
}
|
||||
|
||||
errorOk();
|
||||
}
|
||||
|
||||
void assetDispose(void) {
|
||||
if(ASSET.zip != NULL) {
|
||||
zip_close(ASSET.zip);
|
||||
ASSET.zip = NULL;
|
||||
}
|
||||
|
||||
#if PSP
|
||||
if(ASSET.pbpFile != NULL) {
|
||||
fclose(ASSET.pbpFile);
|
||||
ASSET.pbpFile = NULL;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
Reference in New Issue
Block a user