// Copyright (c) 2023 Dominic Masters // // This software is released under the MIT License. // https://opensource.org/licenses/MIT #include "AssetLoader.hpp" #include "util/mathutils.hpp" using namespace Dawn; ssize_t assetLoaderArchiveRead( struct archive *archive, void *d, const void **buffer ) { assertNotNull(archive, "assetArchiveRead: Archive is NULL!"); assertNotNull(d, "assetArchiveRead: Data is NULL!"); assertNotNull(buffer, "assetArchiveRead: Buffer is NULL!"); AssetLoader *loader = (AssetLoader*)d; *buffer = loader->buffer; size_t read = fread( loader->buffer, 1, ASSET_LOADER_BUFFER_SIZE, loader->assetArchiveFile ); if(ferror(loader->assetArchiveFile)) return ARCHIVE_FATAL; return read; } int64_t assetLoaderArchiveSeek( struct archive *archive, void *d, int64_t offset, int32_t whence ) { assertNotNull(archive, "assetArchiveSeek: Archive is NULL!"); assertNotNull(d, "assetArchiveSeek: Data is NULL!"); assertTrue(offset > 0, "assetArchiveSeek: Offset must be greater than 0!"); AssetLoader *loader = (AssetLoader*)d; int32_t ret = fseek(loader->assetArchiveFile, offset, whence); assertTrue(ret == 0, "assetArchiveSeek: Failed to seek!"); return ftell(loader->assetArchiveFile); } int32_t assetLoaderArchiveOpen(struct archive *a, void *d) { assertNotNull(a, "assetArchiveOpen: Archive is NULL!"); assertNotNull(d, "assetArchiveOpen: Data is NULL!"); AssetLoader *loader = (AssetLoader*)d; int32_t ret = fseek(loader->assetArchiveFile, 0, SEEK_SET); assertTrue(ret == 0, "assetArchiveOpen: Failed to seek to start of file!"); return ARCHIVE_OK; } int32_t assetLoaderArchiveClose(struct archive *a, void *d) { assertNotNull(a, "assetArchiveClose: Archive is NULL!"); assertNotNull(d, "assetArchiveClose: Data is NULL!"); return assetLoaderArchiveOpen(a, d); } AssetLoader::AssetLoader(std::string fileName) : IAssetLoader(fileName) {} void AssetLoader::open() { assertNull(this->assetArchiveFile, "AssetLoader::open: File is already open"); assertNull(this->assetArchive, "AssetLoader::open: Archive is already open"); assertNull(this->assetArchiveEntry, "AssetLoader::open: Entry is already open"); this->assetArchiveFile = fopen(DAWN_ASSET_LOCATION, "rb"); assertNotNull(this->assetArchiveFile, "AssetLoader::open: Failed to open file %s", DAWN_ASSET_LOCATION); // Open archive reader assetArchive = archive_read_new(); assertNotNull(assetArchive, "AssetLoader::open: Failed to create archive reader"); // Set up the reader archive_read_support_format_tar(assetArchive); // Open reader archive_read_set_open_callback(assetArchive, &assetLoaderArchiveOpen); archive_read_set_read_callback(assetArchive, &assetLoaderArchiveRead); archive_read_set_seek_callback(assetArchive, &assetLoaderArchiveSeek); archive_read_set_close_callback(assetArchive, &assetLoaderArchiveClose); archive_read_set_callback_data(assetArchive, this); int32_t ret = archive_read_open1(assetArchive); assertTrue(ret == ARCHIVE_OK, "AssetLoader::open: Failed to open archive!"); position = 0; // Iterate over each file to find the one for this asset loader. while(archive_read_next_header(assetArchive, &assetArchiveEntry) == ARCHIVE_OK) { const char_t *headerFile = (char_t*)archive_entry_pathname(assetArchiveEntry); if(std::string(headerFile) == this->fileName) return; int32_t ret = archive_read_data_skip(assetArchive); assertTrue(ret == ARCHIVE_OK, "AssetLoader::open: Failed to skip data!"); } assertUnreachable("AssetLoader::open: Failed to find file!"); } int32_t AssetLoader::close() { assertNotNull(this->assetArchiveFile, "AssetLoader::close: File is NULL"); assertNotNull(this->assetArchive, "AssetLoader::close: Archive is NULL!"); assertNotNull(this->assetArchiveEntry, "AssetLoader::close: Entry is NULL!"); // Close the archive int32_t ret = archive_read_free(this->assetArchive); assertTrue(ret == ARCHIVE_OK, "AssetLoader::close: Failed to close archive!"); this->assetArchive = nullptr; this->assetArchiveEntry = nullptr; // Close the file int32_t res = fclose(this->assetArchiveFile); this->assetArchiveFile = nullptr; return res; } size_t AssetLoader::read(uint8_t *buffer, size_t size) { assertNotNull(buffer, "assetArchiveRead: Buffer is NULL!"); assertTrue(size > 0, "assetArchiveRead: Size must be greater than 0!"); assertNotNull(this->assetArchive, "assetRead: Archive is NULL!"); assertNotNull(this->assetArchiveEntry, "assetRead: Entry is NULL!"); ssize_t read = archive_read_data(this->assetArchive, buffer, size); this->position += read; if(read == ARCHIVE_FATAL) { assertUnreachable(archive_error_string(this->assetArchive)); } assertTrue(read != ARCHIVE_RETRY, "assetRead: Failed to read data (RETRY)!"); assertTrue(read != ARCHIVE_WARN, "assetRead: Failed to read data (WARN)!"); return read; } size_t AssetLoader::getSize() { assertTrue(this->assetArchiveEntry != nullptr, "AssetLoader::getSize: Entry is NULL!"); assertTrue(archive_entry_size_is_set(assetArchiveEntry), "assetGetSize: Entry size is not set!"); return archive_entry_size(assetArchiveEntry); } size_t AssetLoader::skip(size_t n) { assertTrue(n >= 0, "AssetLoader::skip: Byte count must be greater than 0."); uint8_t dumpBuffer[ASSET_LOADER_BUFFER_SIZE]; size_t skipped = 0; size_t n2, n3; while(n != 0) { n2 = mathMin(n, ASSET_LOADER_BUFFER_SIZE); n3 = this->read(dumpBuffer, n2); assertTrue(n3 == n2, "AssetLoader::skip: Failed to skip bytes!"); n -= n3; } return skipped; } void AssetLoader::rewind() { // TODO: See if I can optimize this this->close(); this->open(); } size_t AssetLoader::getPosition() { assertNotNull(this->assetArchiveFile, "AssetLoader::getPosition: File is not open!"); return this->position; } AssetLoader::~AssetLoader() { if(this->assetArchiveFile != nullptr) this->close(); }