/** * Copyright (c) 2024 Dominic Masters * * This software is released under the MIT License. * https://opensource.org/licenses/MIT */ #include "asset.h" #include "assetarchive.h" #include "assert/assert.h" #include "util/math.h" void assetInit() { // TODO: Works on Windows? path sep probs wrong. // const char_t *assetFilename = "dawn.tar"; // char_t *assetPath = malloc(sizeof(char_t) * ( // strlen(SYSTEM.executableDirectory) + strlen(assetFilename) + 1 // )); // sprintf(assetPath, "%s/%s", SYSTEM.executableDirectory, assetFilename); char_t *assetPath = "/home/yourwishes/htdocs/Dawn/build/dawn.tar"; ASSET_FILE = fopen(assetPath, "rb"); // free(assetPath); assertNotNull(ASSET_FILE, "assetInit: Failed to open asset file!"); ASSET_ARCHIVE = NULL; ASSET_ENTRY = NULL; } size_t assetReadUntil(uint8_t *buffer, const char_t c, const size_t maxLength) { if(buffer == NULL) { assertTrue( maxLength == -1, "If no buffer is provided, maxLength must be -1." ); uint8_t tBuffer[1]; size_t read = 0; while(assetRead(tBuffer, 1) == 1 && (char_t)tBuffer[0] != c) read++; return read; } else { size_t read = 0; while(read < maxLength) { // TODO: Read more than 1 char at a time. read += assetRead(buffer + read, 1); if((char_t)buffer[read-1] == c) return read - 1; } return -1; } } void assetOpen(const char_t *path) { assertNull(ASSET_ARCHIVE, "assetOpenFile: Archive is not NULL!"); assertNull(ASSET_ENTRY, "assetOpenFile: Entry is not NULL!"); assertStringValid(path, 1024, "assetOpenFile: Path is not valid!"); // Store path strcpy(ASSET_PATH_CURRENT, path); // Prepare data ASSET_ARCHIVE = archive_read_new(); assertNotNull(ASSET_ARCHIVE, "assetOpenFile: Failed to create archive!"); // Set up the reader // archive_read_support_filter_bzip2(ASSET_ARCHIVE); archive_read_support_format_tar(ASSET_ARCHIVE); // Open reader archive_read_set_open_callback(ASSET_ARCHIVE, &assetArchiveOpen); archive_read_set_read_callback(ASSET_ARCHIVE, &assetArchiveRead); archive_read_set_seek_callback(ASSET_ARCHIVE, &assetArchiveSeek); archive_read_set_close_callback(ASSET_ARCHIVE, &assetArchiveOpen); archive_read_set_callback_data(ASSET_ARCHIVE, ASSET_ARCHIVE_BUFFER); int32_t ret = archive_read_open1(ASSET_ARCHIVE); assertTrue(ret == ARCHIVE_OK, "assetOpenFile: Failed to open archive!"); // Iterate over each file. while(archive_read_next_header(ASSET_ARCHIVE, &ASSET_ENTRY) == ARCHIVE_OK) { const char_t *headerFile = (char_t*)archive_entry_pathname(ASSET_ENTRY); if(strcmp(headerFile, ASSET_PATH_CURRENT) == 0) return; int32_t ret = archive_read_data_skip(ASSET_ARCHIVE); assertTrue(ret == ARCHIVE_OK, "assetOpenFile: Failed to skip data!"); } assertUnreachable("assetOpenFile: Failed to find file!"); } size_t assetGetSize() { assertNotNull(ASSET_ARCHIVE, "assetGetSize: Archive is NULL!"); assertNotNull(ASSET_ENTRY, "assetGetSize: Entry is NULL!"); assertTrue( archive_entry_size_is_set(ASSET_ENTRY), "assetGetSize: Entry size is not set!" ); size_t n = archive_entry_size(ASSET_ENTRY); // Remnant when get size was doing some incorrect stuff. // char_t path[2048]; // sprintf( // path, "/home/yourwishes/htdocs/dusk/build/assets/%s", ASSET_PATH_CURRENT // ); // FILE *temp = fopen(path, "rb"); // assertNotNull(temp, "assetGetSize: Failed to open temp file!"); // fseek(temp, 0, SEEK_END); // size_t size = ftell(temp); // assertTrue(size == n, "assetGetSize: Size is not equal!"); // fclose(temp); return n; } size_t assetRead(uint8_t *buffer, size_t bufferSize) { assertNotNull(ASSET_ARCHIVE, "assetRead: Archive is NULL!"); assertNotNull(ASSET_ENTRY, "assetRead: Entry is NULL!"); assertNotNull(buffer, "assetRead: Buffer is NULL!"); assertTrue(bufferSize > 0, "assetRead: Buffer size must be greater than 0!"); ssize_t read = archive_read_data(ASSET_ARCHIVE, buffer, bufferSize); if(read == ARCHIVE_FATAL) { assertUnreachable(archive_error_string(ASSET_ARCHIVE)); } assertTrue(read != ARCHIVE_RETRY, "assetRead: Failed to read data (RETRY)!"); assertTrue(read != ARCHIVE_WARN, "assetRead: Failed to read data (WARN)!"); return read; } void assetSkip(const size_t length) { assertNotNull(ASSET_ARCHIVE, "assetSkip: Archive is NULL!"); assertNotNull(ASSET_ENTRY, "assetSkip: Entry is NULL!"); assertTrue(length > 0, "assetSkip: Length must be greater than 0!"); // Asset archive does not support skipping, so we have to read and discard. uint8_t buffer[1024]; size_t remaining = length; do { size_t toRead = mathMin(remaining, 1024); size_t read = assetRead(buffer, toRead); assertTrue(read == toRead, "assetSkip: Failed to skip data! (overskip?)"); remaining -= read; } while(remaining > 0); } void assetClose() { assertNotNull(ASSET_ARCHIVE, "assetClose: Archive is NULL!"); assertNotNull(ASSET_ENTRY, "assetClose: Entry is NULL!"); int32_t ret = archive_read_free(ASSET_ARCHIVE); assertTrue(ret == ARCHIVE_OK, "assetClose: Failed to close archive!"); ASSET_ARCHIVE = NULL; ASSET_ENTRY = NULL; } void assetDispose() { assertNull(ASSET_ARCHIVE, "assetDestroy: Archive is not NULL!"); assertNull(ASSET_ENTRY, "assetDestroy: Entry is not NULL!"); int32_t result = fclose(ASSET_FILE); assertTrue(result == 0, "assetDestroy: Failed to close asset file!"); }