// Copyright (c) 2023 Dominic Masters // // This software is released under the MIT License. // https://opensource.org/licenses/MIT #include "assert/assert.hpp" #include "AssetDataLoader.hpp" #include "util/Math.hpp" using namespace Dawn; ssize_t assetDataLoaderArchiveRead( struct archive *archive, void *d, const void **buffer ) { assertNotNull(archive, "Archive is NULL!"); assertNotNull(d, "Data is NULL!"); assertNotNull(buffer, "Buffer is NULL!"); AssetDataLoader *loader = (AssetDataLoader*)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 assetDataLoaderArchiveSeek( struct archive *archive, void *d, int64_t offset, int32_t whence ) { assertNotNull(archive, "Archive is NULL!"); assertNotNull(d, "Data is NULL!"); assertTrue(offset > 0, "Offset must be greater than 0!"); AssetDataLoader *loader = (AssetDataLoader*)d; int32_t ret = fseek(loader->assetArchiveFile, offset, whence); assertTrue(ret == 0, "Failed to seek!"); return ftell(loader->assetArchiveFile); } int32_t assetDataLoaderArchiveOpen(struct archive *a, void *d) { assertNotNull(a, "Archive is NULL!"); assertNotNull(d, "Data is NULL!"); AssetDataLoader *loader = (AssetDataLoader*)d; int32_t ret = fseek(loader->assetArchiveFile, 0, SEEK_SET); assertTrue(ret == 0, "Failed to seek to start of file!"); return ARCHIVE_OK; } int32_t assetDataLoaderArchiveClose(struct archive *a, void *d) { assertNotNull(a, "Archive is NULL!"); assertNotNull(d, "Data is NULL!"); return assetDataLoaderArchiveOpen(a, d); } // // // // // // // // // // // // // // // // // // // // // // // // // // // AssetDataLoader::AssetDataLoader(std::string fileName) : fileName(fileName) { assertTrue( fileName.size() > 0, "IAssetDataLoader::IAssetDataLoader: fileName must be greater than 0" ); } void AssetDataLoader::open() { assertNull(this->assetArchiveFile, "AssetDataLoader::open: File is already open"); assertNull(this->assetArchive, "AssetDataLoader::open: Archive is already open"); assertNull(this->assetArchiveEntry, "AssetDataLoader::open: Entry is already open"); this->assetArchiveFile = this->openAssetArchiveFile(); assertNotNull(this->assetArchiveFile, "AssetDataLoader::open: Failed to open archive file!"); // Open archive reader assetArchive = archive_read_new(); assertNotNull(assetArchive, "AssetDataLoader::open: Failed to create archive reader"); // Set up the reader archive_read_support_format_tar(assetArchive); // Open reader archive_read_set_open_callback(assetArchive, &assetDataLoaderArchiveOpen); archive_read_set_read_callback(assetArchive, &assetDataLoaderArchiveRead); archive_read_set_seek_callback(assetArchive, &assetDataLoaderArchiveSeek); archive_read_set_close_callback(assetArchive, &assetDataLoaderArchiveClose); archive_read_set_callback_data(assetArchive, this); int32_t ret = archive_read_open1(assetArchive); assertTrue(ret == ARCHIVE_OK, "AssetDataLoader::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, "AssetDataLoader::open: Failed to skip data!"); } assertUnreachable("AssetDataLoader::open: Failed to find file!"); } int32_t AssetDataLoader::close() { assertNotNull(this->assetArchiveFile, "AssetDataLoader::close: File is NULL"); assertNotNull(this->assetArchive, "AssetDataLoader::close: Archive is NULL!"); assertNotNull(this->assetArchiveEntry, "AssetDataLoader::close: Entry is NULL!"); // Close the archive int32_t ret = archive_read_free(this->assetArchive); assertTrue(ret == ARCHIVE_OK, "AssetDataLoader::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 AssetDataLoader::read(uint8_t *buffer, size_t size) { assertNotNull(buffer, "Buffer is NULL!"); assertTrue(size > 0, "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 AssetDataLoader::readUntil( uint8_t *buffer, const size_t maxSize, const char_t delimiter ) { size_t totalRead = this->read(buffer, maxSize); size_t i = 0; while(i < totalRead) { if(buffer[i] == delimiter) break; i++; } buffer[i] = '\0'; return i; } size_t AssetDataLoader::getSize() { assertTrue(this->assetArchiveEntry != nullptr, "AssetDataLoader::getSize: Entry is NULL!"); assertTrue(archive_entry_size_is_set(assetArchiveEntry), "assetGetSize: Entry size is not set!"); return archive_entry_size(assetArchiveEntry); } size_t AssetDataLoader::skip(size_t n) { assertTrue(n >= 0, "AssetDataLoader::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 = Math::min(n, ASSET_LOADER_BUFFER_SIZE); n3 = this->read(dumpBuffer, n2); assertTrue(n3 == n2, "AssetDataLoader::skip: Failed to skip bytes!"); n -= n3; } return skipped; } size_t AssetDataLoader::setPosition(const size_t position) { assertTrue(position >= 0, "Position must be greater than or equal to 0"); this->rewind(); return this->skip(position); } void AssetDataLoader::rewind() { // TODO: See if I can optimize this this->close(); this->open(); } size_t AssetDataLoader::getPosition() { assertNotNull(this->assetArchiveFile, "AssetDataLoader::getPosition: File is not open!"); return this->position; } AssetDataLoader::~AssetDataLoader() { if(this->assetArchiveFile != nullptr) this->close(); }