scummvm/engines/saga2/hresmgr.cpp
2021-07-01 01:37:11 +02:00

640 lines
14 KiB
C++

/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* aint32 with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
*
* Based on the original sources
* Faery Tale II -- The Halls of the Dead
* (c) 1993-1996 The Wyrmkeep Entertainment Co.
*/
#include "common/debug.h"
#include "saga2/std.h"
#include "saga2/hresmgr.h"
#include "saga2/fta.h"
namespace Saga2 {
/* ===================================================================== *
Calls which do disk access
bool hResContext::seek(hResID id)
fseek
void hResContext::rest(void)
fclose
bool hResContext::read(void *buffer, int32 size)
fread
uint32 hResContext::readbytes(void *buffer, uint32 size)
fseek
fread
bool hResContext::skip(int32 amount)
fseek
RHANDLE hResContext::loadIndex( int16 index, const char desc[], bool cacheable )
fseek
hResource::hResource(char *resname, char *extname, const char desc[])
fopen
fseek
fread
hResource::~hResource()
fclose
Calls which deal with external files
HR_FILE *hResContext::openExternal(HR_FILE *fh)
* ===================================================================== */
/* ===================================================================== *
Resource context
* ===================================================================== */
hResContext::hResContext() {
_valid = false;
_base = nullptr;
_parent = nullptr;
_numEntries = 0;
_handle = &_file;
}
hResContext::hResContext(hResContext *sire, hResID id, const char desc[]) {
hResEntry *entry;
const uint32 resourceSize = 12;
_valid = false;
_res = sire->_res;
_numEntries = 0;
_bytecount = 0;
_bytepos = 0;
_handle = &_file;
if (!_res->_valid)
return;
_parent = sire;
debugC(3, kDebugResources, "Creating context %x (%s)", id, tag2str(id));
if ((entry = _parent->findEntry(id)) == nullptr) {
debugC(3, kDebugResources, "Could not create context");
return;
}
_numEntries = entry->size / resourceSize;
_base = (hResEntry *)((uint8*)_res->_table + entry->offset - _res->_firstGroupOffset);
debugC(3, kDebugResources, "- _numEntries = %d, _base = %p, entry->offset = %d",
_numEntries, _base, entry->offset);
_valid = true;
}
hResContext::~hResContext() {
releaseIndexData();
}
hResEntry *hResContext::findEntry(hResID id, RHANDLE **capture) {
hResEntry *entry;
int16 i;
_bytecount = 0;
_bytepos = 0;
if (!_valid) return nullptr;
debugC(3, kDebugResources, "findEntry: looking for %x (%s)", id, tag2str(id));
for (i = 0, entry = _base; i < _numEntries; i++, entry++) {
debugC(3, kDebugResources, "%d: Trying ID: %x (%s)", i, entry->id, tag2str(entry->id));
if (entry->id == id) {
debugC(3, kDebugResources, "findEntry: found %x (%s)", entry->id, tag2str(entry->id));
return entry;
}
}
debugC(3, kDebugResources, "findEntry: No entry found");
return nullptr;
}
uint32 hResContext::size(hResID id) {
hResEntry *entry;
if (!_valid)
return 0;
if ((entry = findEntry(id)) == nullptr)
return 0;
return entry->size;
}
uint32 hResContext::count(void) {
return _numEntries;
}
uint32 hResContext::count(hResID id) {
uint32 count = 0;
hResEntry *entry;
int16 i;
_bytecount = 0;
_bytepos = 0;
if (!_valid) return 0;
for (i = 0, entry = _base; i < _numEntries; i++, entry++) {
if ((entry->id & 0x00ffffff) == (id & 0x00ffffff)) count++;
}
return count;
}
Common::File *hResContext::openExternal(Common::File *fh) {
char name[160];
int len;
uint16 size;
Common::File *file;
file = &_file;
_bytecount = 0;
_bytepos = 0;
strcpy(name, _res->_externalPath);
len = strlen(name);
size = fh->readUint32LE();
fh->read(&name, sizeof(name));
name[len + size] = 0;
file->open(name);
return file;
}
// this function sets _handle
bool hResContext::seek(hResID id) {
hResEntry *entry;
_bytecount = 0;
_bytepos = 0;
if (!_valid)
return false;
if ((entry = findEntry(id)) == nullptr)
return false;
_bytecount = entry->size;
_bytepos = entry->resOffset();
_res->_handle->seek(_bytepos, SEEK_SET);
if (entry->isExternal()) {
// resource _data is actually a path name
_handle = openExternal(_res->_handle);
return (_handle != nullptr);
}
_handle = _res->_handle;
return true;
}
void hResContext::rest(void) {
_bytecount = 0;
_bytepos = 0;
if (_valid && _handle && _handle != _res->_handle) {
_handle->close();
_handle = nullptr;
}
}
uint8 hResContext::readByte() {
if (!_handle)
return 0;
if (!_handle->isOpen())
return 0;
return _handle->readByte();
}
int8 hResContext::readSByte() {
if (!_handle)
return 0;
if (!_handle->isOpen())
return 0;
return _handle->readSByte();
}
uint16 hResContext::readU16LE() {
if (!_handle)
return 0;
if (!_handle->isOpen())
return 0;
return _handle->readUint16LE();
}
int16 hResContext::readS16LE() {
if (!_handle)
return 0;
if (!_handle->isOpen())
return 0;
return _handle->readSint16LE();
}
uint32 hResContext::readU32LE() {
if (!_handle)
return 0;
if (!_handle->isOpen())
return 0;
return _handle->readUint32LE();
}
int32 hResContext::readS32LE() {
if (!_handle)
return 0;
if (!_handle->isOpen())
return 0;
return _handle->readSint32LE();
}
int32 hResContext::readS32BE() {
if (!_handle)
return 0;
if (!_handle->isOpen())
return 0;
return _handle->readSint32BE();
}
bool hResContext::read(void *buffer, uint32 size) {
assert(_handle);
if (!_valid) return false;
_bytecount = 0;
_bytepos = 0;
return (_handle->read(buffer, size) != 0);
}
bool hResContext::eor(void) {
return (_bytecount < 1);
}
uint32 hResContext::readbytes(void *buffer, uint32 size) {
int32 bytesRead;
if (!_valid || _bytecount < 1) return 0;
_res->_handle->seek(_bytepos, SEEK_SET);
bytesRead = _handle->read(buffer, MIN(size, _bytecount));
_bytecount -= bytesRead;
_bytepos += bytesRead;
return bytesRead;
}
bool hResContext::skip(uint32 amount) {
if (!_valid) return false;
_res->_handle->seek(amount, SEEK_CUR);
_bytecount -= amount;
_bytepos -= amount;
return true;
}
bool hResContext::get(hResID id, void *buffer, uint32 size) {
bool result = false;
if (!_valid) return false;
_bytecount = 0;
_bytepos = 0;
if (size == NATURAL_SIZE) {
hResEntry *entry;
if ((entry = findEntry(id)) == nullptr) return false;
size = entry->size;
}
if (seek(id)) {
result = read(buffer, size);
rest();
}
return result;
}
uint32 hResContext::getSize(hResID id, const char desc[]) {
hResEntry *entry;
if ((entry = findEntry(id)) == nullptr) {
warning("Resource %d, %s not found", id, desc);
return 0;
}
debugC(3, kDebugResources, "Size for %x (%s): %d", id, desc, entry->size);
return entry->size;
}
byte *hResContext::loadResource(hResID id, const char desc[], Common::String filename) {
hResEntry *entry;
debugC(3, kDebugResources, "Loading resource %x (%s)", id, desc);
if ((entry = findEntry(id)) == nullptr) {
warning("Resource %d, %s not found", id, desc);
return nullptr;
}
byte *res = (byte*)malloc(entry->size);
if (filename.equalsIgnoreCase(""))
filename = _filename;
if (!_file.isOpen())
_file.open(filename);
_file.seek(entry->offset, SEEK_SET);
_file.read(res, entry->size);
return res;
}
RHANDLE hResContext::load(hResID id, const char desc[], bool async, bool cacheable) {
hResEntry *entry;
RHANDLE *capture;
if (!_valid)
return nullptr;
_bytecount = 0;
_bytepos = 0;
if ((entry = findEntry(id, &capture)) == nullptr)
return nullptr;
if (*capture != nullptr && **capture != nullptr) {
entry->use();
} else {
if (*capture == nullptr)
*capture = (RHANDLE)malloc(entry->size);
if (*capture == nullptr) return nullptr;
// If it's an external resource, then load synchronously
#ifdef WINKLUDGE
async = false;
#endif
if (entry->isExternal() || async == false) {
if (seek(id) && read(**capture, entry->size)) {
entry->use();
} else {
free(*capture);
*capture = nullptr;
}
rest();
} else {
#ifndef WINKLUDGE
RequestResource(*capture,
entry->offset,
entry->size);
#endif
entry->use();
}
}
return (*capture);
}
byte *hResContext::loadIndexResource(int16 index, const char desc[], Common::String filename) {
hResEntry *entry;
entry = &_base[index];
debugC(5, kDebugResources, "Loading indexed resource: %d (%s)", index, desc);
if (!_valid || entry == nullptr)
return nullptr;
if (_indexData.contains(index))
return _indexData.getVal(index);
byte *res = (byte*)malloc(entry->size);
if (res == nullptr) {
debugC(5, kDebugResources, "Could not allocate resources");
return nullptr;
}
debugC(5, kDebugResources, "_indexData: pushing (%d, %p)", index, (void*)res);
_indexData.setVal(index, res);
if (filename.equalsIgnoreCase(""))
filename = _filename;
if (!_file.isOpen())
_file.open(filename);
_file.seek(entry->offset, SEEK_SET);
_file.read(res, entry->size);
return res;
}
RHANDLE hResContext::loadIndex(int16 index, const char desc[], bool cacheable) {
hResEntry *entry;
RHANDLE *capture; //, _handle;
if (!_valid)
return nullptr;
_bytecount = 0;
_bytepos = 0;
entry = &_base[index];
if (*capture != nullptr && **capture != nullptr) {
entry->use();
} else {
if (*capture == nullptr)
*capture = (RHANDLE)malloc(entry->size);
if (*capture == nullptr) return nullptr;
_res->_handle->seek(entry->resOffset(), SEEK_SET);
if (read(**capture, entry->size) == false) {
free(*capture);
*capture = nullptr;
}
entry->use();
rest();
}
return (*capture);
}
void hResContext::release(RHANDLE p) {
_bytecount = 0;
_bytepos = 0;
hResEntry *entry;
RHANDLE *d;
if (_valid && p != nullptr) {
entry = _base;
while (entry->id != BAD_ID) {
if ((RHANDLE)p == *d) {
entry->abandon();
if (!entry->isUsed()) {
RDisposeHandle(p);
*d = nullptr;
}
return;
}
entry++;
d++;
}
}
}
void hResContext::releaseIndexData() {
debugC(4, kDebugResources, "releaseIndexData():");
for (DataMap::iterator i = _indexData.begin(); i != _indexData.end(); ++i) {
debugC(4, kDebugResources, "... %d, %p", i->_key, (void*)i->_value);
if (i->_value) {
free(i->_value);
i->_value = nullptr;
_indexData.erase(i);
}
}
}
/* ===================================================================== *
Resource file
* ===================================================================== */
void hResource::readEntry(hResEntry &element) {
element.id = _file.readUint32BE();
element.offset = _file.readUint32LE();
element.size = _file.readUint32LE();
uint32 id = element.id;
debugC(2, kDebugResources, "%s, offset: %x, size: %d", tag2str(id), element.offset, element.size);
}
void hResource::readResource(hResEntry &element) {
element.id = _file.readUint32BE();
element.offset = _file.readUint32LE();
element.size = _file.readUint32LE();
uint32 id = element.id;
debugC(3, kDebugResources, "%s, offset: %x, size: %d", tag2str(id), element.offset, element.size);
}
hResource::hResource(char *resname, char *extname, const char desc[]) {
hResEntry origin;
int32 tableSize;
const int32 resourceSize = 4 + 4 + 4; // id, offset, size
_valid = false;
_base = nullptr;
_parent = nullptr;
_numEntries = 0;
_filename = resname;
strncpy(_externalPath, extname ? extname : "", EXTERNAL_PATH_SIZE);
debugC(1, kDebugResources, "Opening resource: %s", resname);
if (!_file.open(resname))
warning("Unable to open file %s", resname);
readResource(origin);
if (origin.id != HRES_ID)
return;
_file.seek(origin.offset - sizeof(uint32), SEEK_SET);
_firstGroupOffset = _file.readUint32LE();
// allocate buffers for root, groups and data
_numEntries = origin.size / resourceSize;
_base = new hResEntry[_numEntries]();
tableSize = origin.offset - _firstGroupOffset - sizeof(uint32);
_table = new hResEntry[tableSize / resourceSize]();
if (_base == nullptr || _table == nullptr)
return;
debugC(1, kDebugResources, "Reading %d entries:", _numEntries);
for (int i = 0; i < _numEntries; ++i)
readEntry(_base[i]);
debugC(1, kDebugResources, "Reading %d groups:", tableSize / resourceSize);
_file.seek(_firstGroupOffset, SEEK_SET);
for (int i = 0; i < tableSize / resourceSize; ++i) {
readResource(_table[i]);
}
_res = this;
_valid = true;
}
hResource::~hResource() {
if (_base)
delete[] _base;
if (_table)
delete[] _table;
//if (_handle) free(_handle);
}
hResContext *hResource::newContext(hResID id, const char desc[]) {
hResContext *result;
result = new hResContext(this, id, desc);
if (result == nullptr || !result->_valid) {
error("Error accessing resource group.");
}
result->_filename = _filename;
return result;
}
void hResource::disposeContext(hResContext *con) {
if (con) delete con;
}
/* ===================================================================== *
Assorted functions
* ===================================================================== */
//-----------------------------------------------------------------------
// For handles which have been purged, but the handle structure is
// still hanging around, we can free the handle structure and
// set the actual handle pointer to nullptr.
void washHandle(RHANDLE &handle) {
if (handle != nullptr && *handle == nullptr) {
RDisposeHandle(handle);
handle = nullptr;
}
}
} // end of namespace Saga2