200 lines
6.4 KiB
C++
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();
|
|
} |