Dawn/src/dawn/asset/AssetDataLoader.cpp
2023-11-25 15:51:20 -06:00

200 lines
6.4 KiB
C++

// 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<size_t>(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();
}