2016-03-20 17:00:00 +01:00
|
|
|
|
|
|
|
#include "resource_aba.h"
|
|
|
|
#include "unpack.h"
|
|
|
|
#include "util.h"
|
|
|
|
|
|
|
|
ResourceAba::ResourceAba(FileSystem *fs)
|
|
|
|
: _fs(fs) {
|
2021-05-27 18:00:00 +02:00
|
|
|
_filesCount = 0;
|
2016-03-20 17:00:00 +01:00
|
|
|
_entries = 0;
|
|
|
|
_entriesCount = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
ResourceAba::~ResourceAba() {
|
|
|
|
free(_entries);
|
|
|
|
}
|
|
|
|
|
2019-12-28 17:00:00 +01:00
|
|
|
static int compareAbaEntry(const void *a, const void *b) {
|
|
|
|
return strcasecmp(((ResourceAbaEntry *)a)->name, ((ResourceAbaEntry *)b)->name);
|
|
|
|
}
|
|
|
|
|
2021-05-27 18:00:00 +02:00
|
|
|
void ResourceAba::readEntries(const char *aba) {
|
|
|
|
assert(_filesCount < 3);
|
|
|
|
if (_f[_filesCount].open(aba, "rb", _fs)) {
|
|
|
|
File &f = _f[_filesCount];
|
|
|
|
const int currentCount = _entriesCount;
|
|
|
|
const int entriesCount = f.readUint16BE();
|
|
|
|
_entries = (ResourceAbaEntry *)realloc(_entries, (currentCount + entriesCount) * sizeof(ResourceAbaEntry));
|
2016-03-20 17:00:00 +01:00
|
|
|
if (!_entries) {
|
2021-05-27 18:00:00 +02:00
|
|
|
error("Failed to allocate %d _entries", currentCount + entriesCount);
|
2016-03-20 17:00:00 +01:00
|
|
|
return;
|
|
|
|
}
|
2021-05-27 18:00:00 +02:00
|
|
|
_entriesCount = currentCount + entriesCount;
|
|
|
|
const int entrySize = f.readUint16BE();
|
2016-03-20 17:00:00 +01:00
|
|
|
assert(entrySize == 30);
|
|
|
|
uint32_t nextOffset = 0;
|
2021-05-27 18:00:00 +02:00
|
|
|
for (int i = 0; i < entriesCount; ++i) {
|
|
|
|
const int j = currentCount + i;
|
|
|
|
f.read(_entries[j].name, sizeof(_entries[j].name));
|
|
|
|
_entries[j].offset = f.readUint32BE();
|
|
|
|
_entries[j].compressedSize = f.readUint32BE();
|
|
|
|
_entries[j].size = f.readUint32BE();
|
|
|
|
_entries[j].fileIndex = _filesCount;
|
|
|
|
const uint32_t tag = f.readUint32BE();
|
2016-03-20 17:00:00 +01:00
|
|
|
assert(tag == TAG);
|
2021-05-27 18:00:00 +02:00
|
|
|
debug(DBG_RES, "'%s' offset 0x%X size %d/%d", _entries[j].name, _entries[j].offset, _entries[j].compressedSize, _entries[j].size);
|
2016-03-20 17:00:00 +01:00
|
|
|
if (i != 0) {
|
2021-05-27 18:00:00 +02:00
|
|
|
assert(nextOffset == _entries[j].offset);
|
2016-03-20 17:00:00 +01:00
|
|
|
}
|
2021-05-27 18:00:00 +02:00
|
|
|
nextOffset = _entries[j].offset + _entries[j].compressedSize;
|
2016-03-20 17:00:00 +01:00
|
|
|
}
|
2019-12-28 17:00:00 +01:00
|
|
|
qsort(_entries, _entriesCount, sizeof(ResourceAbaEntry), compareAbaEntry);
|
2021-05-27 18:00:00 +02:00
|
|
|
++_filesCount;
|
2016-03-20 17:00:00 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
const ResourceAbaEntry *ResourceAba::findEntry(const char *name) const {
|
2019-12-28 17:00:00 +01:00
|
|
|
ResourceAbaEntry tmp;
|
|
|
|
strcpy(tmp.name, name);
|
|
|
|
return (const ResourceAbaEntry *)bsearch(&tmp, _entries, _entriesCount, sizeof(ResourceAbaEntry), compareAbaEntry);
|
2016-03-20 17:00:00 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
uint8_t *ResourceAba::loadEntry(const char *name, uint32_t *size) {
|
|
|
|
uint8_t *dst = 0;
|
|
|
|
const ResourceAbaEntry *e = findEntry(name);
|
|
|
|
if (e) {
|
|
|
|
if (size) {
|
|
|
|
*size = e->size;
|
|
|
|
}
|
|
|
|
uint8_t *tmp = (uint8_t *)malloc(e->compressedSize);
|
|
|
|
if (!tmp) {
|
|
|
|
error("Failed to allocate %d bytes", e->compressedSize);
|
|
|
|
return 0;
|
|
|
|
}
|
2021-05-27 18:00:00 +02:00
|
|
|
_f[e->fileIndex].seek(e->offset);
|
|
|
|
_f[e->fileIndex].read(tmp, e->compressedSize);
|
2016-03-20 17:00:00 +01:00
|
|
|
if (e->compressedSize == e->size) {
|
|
|
|
dst = tmp;
|
|
|
|
} else {
|
|
|
|
dst = (uint8_t *)malloc(e->size);
|
|
|
|
if (!dst) {
|
|
|
|
error("Failed to allocate %d bytes", e->size);
|
|
|
|
free(tmp);
|
|
|
|
return 0;
|
|
|
|
}
|
2019-10-27 17:00:00 +01:00
|
|
|
const bool ret = bytekiller_unpack(dst, e->size, tmp, e->compressedSize);
|
2016-03-20 17:00:00 +01:00
|
|
|
if (!ret) {
|
|
|
|
error("Bad CRC for '%s'", name);
|
|
|
|
}
|
|
|
|
free(tmp);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return dst;
|
|
|
|
}
|