Skip to content

Commit

Permalink
AURORA: Add preliminary support for BZF archives
Browse files Browse the repository at this point in the history
BZF are essentially BIF files with LZMA-compressed data.
They can be found in the iOS rerelease of Knights of the
Old Republic.

TODO: Hook them into the ResourceManager.
  • Loading branch information
DrMcCoy committed May 6, 2014
1 parent da0e975 commit a8ca4b6
Show file tree
Hide file tree
Showing 4 changed files with 337 additions and 1 deletion.
2 changes: 2 additions & 0 deletions src/aurora/Makefile.am
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ noinst_HEADERS = \
aurorafile.h \
keyfile.h \
biffile.h \
bzffile.h \
erffile.h \
rimfile.h \
ndsrom.h \
Expand Down Expand Up @@ -41,6 +42,7 @@ libaurora_la_SOURCES = \
aurorafile.cpp \
keyfile.cpp \
biffile.cpp \
bzffile.cpp \
erffile.cpp \
rimfile.cpp \
ndsrom.cpp \
Expand Down
222 changes: 222 additions & 0 deletions src/aurora/bzffile.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,222 @@
/* xoreos - A reimplementation of BioWare's Aurora engine
*
* xoreos is the legal property of its developers, whose names can be
* found in the AUTHORS 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 3
* 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
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
*
* The Infinity, Aurora, Odyssey, Eclipse and Lycium engines, Copyright (c) BioWare corp.
* The Electron engine, Copyright (c) Obsidian Entertainment and BioWare corp.
*/

/** @file aurora/bzffile.cpp
* Handling BioWare's BZFs (resource data files), used in the iOS version of
* Knights of the Old Republic.
*
* Essentially, they are BIF files with LZMA-compressed data.
*/

#include <lzma.h>

#include "common/util.h"
#include "common/error.h"
#include "common/stream.h"
#include "common/file.h"

#include "aurora/bzffile.h"
#include "aurora/keyfile.h"
#include "aurora/error.h"

static const uint32 kBZFID = MKTAG('B', 'I', 'F', 'F');
static const uint32 kVersion1 = MKTAG('V', '1', ' ', ' ');

namespace Aurora {

BZFFile::BZFFile(const Common::UString &fileName) : _fileName(fileName) {
load();
}

BZFFile::~BZFFile() {
}

void BZFFile::clear() {
_resources.clear();
}

void BZFFile::load() {
Common::File bzf;
open(bzf);

readHeader(bzf);

if (_id != kBZFID)
throw Common::Exception("Not a BZF file");

if (_version != kVersion1)
throw Common::Exception("Unsupported BZF file version %08X", _version);

uint32 varResCount = bzf.readUint32LE();
uint32 fixResCount = bzf.readUint32LE();

if (fixResCount != 0)
throw Common::Exception("TODO: Fixed BZF resources");

_iResources.resize(varResCount);

uint32 offVarResTable = bzf.readUint32LE();

try {

readVarResTable(bzf, offVarResTable);

if (bzf.err())
throw Common::Exception(Common::kReadError);

} catch (Common::Exception &e) {
e.add("Failed reading BZF file");
throw;
}

}

void BZFFile::readVarResTable(Common::SeekableReadStream &bzf, uint32 offset) {
if (!bzf.seek(offset))
throw Common::Exception(Common::kSeekError);

for (uint32_t i = 0; i < _iResources.size(); i++) {
bzf.skip(4); // ID

_iResources[i].offset = bzf.readUint32LE();
_iResources[i].size = bzf.readUint32LE();
_iResources[i].type = (FileType) bzf.readUint32LE();

if (i > 0)
_iResources[i - 1].packedSize = _iResources[i].offset - _iResources[i - 1].offset;
}

if (!_iResources.empty())
_iResources.back().packedSize = bzf.size() - _iResources.back().offset;
}

void BZFFile::mergeKEY(const KEYFile &key, uint32 bifIndex) {
const KEYFile::ResourceList &keyResList = key.getResources();

for (KEYFile::ResourceList::const_iterator keyRes = keyResList.begin(); keyRes != keyResList.end(); ++keyRes) {
if (keyRes->bifIndex != bifIndex)
continue;

if (keyRes->resIndex >= _iResources.size()) {
warning("Resource index out of range (%d/%d)", keyRes->resIndex, (int) _iResources.size());
continue;
}

if (keyRes->type != _iResources[keyRes->resIndex].type)
warning("KEY and BZF disagree on the type of the resource \"%s\" (%d, %d). Trusting the BZF",
keyRes->name.c_str(), keyRes->type, _iResources[keyRes->resIndex].type);

Resource res;

res.name = keyRes->name;
res.type = _iResources[keyRes->resIndex].type;
res.index = keyRes->resIndex;

_resources.push_back(res);
}

}

const Archive::ResourceList &BZFFile::getResources() const {
return _resources;
}

const BZFFile::IResource &BZFFile::getIResource(uint32 index) const {
if (index >= _iResources.size())
throw Common::Exception("Resource index out of range (%d/%d)", index, _iResources.size());

return _iResources[index];
}

uint32 BZFFile::getResourceSize(uint32 index) const {
return getIResource(index).size;
}

Common::SeekableReadStream *BZFFile::getResource(uint32 index) const {
const IResource &res = getIResource(index);
if ((res.packedSize == 0) || (res.size == 0))
return new Common::MemoryReadStream(0, 0);

Common::File bzf;
open(bzf);

if (!bzf.seek(res.offset))
throw Common::Exception(Common::kSeekError);

byte *compressedData = new byte[res.packedSize];

if (bzf.read(compressedData, res.packedSize) != res.packedSize) {
delete[] compressedData;
throw Common::Exception(Common::kReadError);
}

Common::SeekableReadStream *resStream = decompress(compressedData, res.packedSize, res.size);
delete[] compressedData;

return resStream;
}

void BZFFile::open(Common::File &file) const {
if (!file.open(_fileName))
throw Common::Exception(Common::kOpenError);
}

Common::SeekableReadStream *BZFFile::decompress(byte *compressedData, uint32 packedSize, uint32 unpackedSize) const {

lzma_filter filters[2];
filters[0].id = LZMA_FILTER_LZMA1;
filters[0].options = 0;
filters[1].id = LZMA_VLI_UNKNOWN;
filters[1].options = 0;

if (!lzma_filter_decoder_is_supported(filters[0].id))
throw Common::Exception("LZMA1 compression not supported");

uint32_t propsSize;
if (lzma_properties_size(&propsSize, &filters[0]) != LZMA_OK)
throw Common::Exception("Can't get LZMA1 properties size");

if (lzma_properties_decode(&filters[0], 0, compressedData, propsSize) != LZMA_OK)
throw Common::Exception("Failed to decode LZMA properties");

compressedData += propsSize;
packedSize -= propsSize;

byte *uncompressedData = new byte[unpackedSize];
size_t posIn = 0, posOut = 0;

lzma_ret decodeRet = lzma_raw_buffer_decode(filters, 0,
compressedData , &posIn , packedSize,
uncompressedData, &posOut, unpackedSize);

if (decodeRet != LZMA_OK) {
delete[] uncompressedData;
throw Common::Exception("Failed to uncompress LZMA data: %d", (int) decodeRet);
}

return new Common::MemoryReadStream(uncompressedData, unpackedSize, true);
}

} // End of namespace Aurora
108 changes: 108 additions & 0 deletions src/aurora/bzffile.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
/* xoreos - A reimplementation of BioWare's Aurora engine
*
* xoreos is the legal property of its developers, whose names can be
* found in the AUTHORS 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 3
* 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
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
*
* The Infinity, Aurora, Odyssey, Eclipse and Lycium engines, Copyright (c) BioWare corp.
* The Electron engine, Copyright (c) Obsidian Entertainment and BioWare corp.
*/

/** @file aurora/bzffile.h
* Handling BioWare's BZFs (resource data files), used in the iOS version of
* Knights of the Old Republic.
*
* Essentially, they are BIF files with LZMA-compressed data.
*/

#ifndef AURORA_BZFFILE_H
#define AURORA_BZFFILE_H

#include <vector>

#include "common/types.h"

#include "aurora/types.h"
#include "aurora/archive.h"
#include "aurora/aurorafile.h"

namespace Common {
class SeekableReadStream;
class File;
}

namespace Aurora {

class KEYFile;

/** Class to hold resource data information of a bzf file. */
class BZFFile : public Archive, public AuroraBase {
public:
BZFFile(const Common::UString &fileName);
~BZFFile();

/** Clear the resource list. */
void clear();

/** Return the list of resources. */
const ResourceList &getResources() const;

/** Return the size of a resource. */
uint32 getResourceSize(uint32 index) const;

/** Return a stream of the resource's contents. */
Common::SeekableReadStream *getResource(uint32 index) const;

/** Merge information from the KEY into the BZF. */
void mergeKEY(const KEYFile &key, uint32 bifIndex);

private:
/** Internal resource information. */
struct IResource {
FileType type; ///< The resource's type.

uint32 offset; ///< The offset of the resource within the BZF.
uint32 size; ///< The resource's size.

uint32 packedSize; ///< Raw, uncompressed data size.
};

typedef std::vector<IResource> IResourceList;

/** External list of resource names and types. */
ResourceList _resources;

/** Internal list of resource offsets and sizes. */
IResourceList _iResources;

/** The name of the BZF file. */
Common::UString _fileName;

void open(Common::File &file) const;

void load();
void readVarResTable(Common::SeekableReadStream &bzf, uint32 offset);

const IResource &getIResource(uint32 index) const;

Common::SeekableReadStream *decompress(byte *compressedData, uint32 packedSize, uint32 unpackedSize) const;
};

} // End of namespace Aurora

#endif // AURORA_BZFFILE_H
6 changes: 5 additions & 1 deletion src/aurora/types.h
Original file line number Diff line number Diff line change
Expand Up @@ -306,7 +306,10 @@ enum FileType {
kFileTypeVLM = 25020,
kFileTypeWBD = 25021,
kFileTypeXBX = 25022,
kFileTypeXLS = 25023
kFileTypeXLS = 25023,

// Found in the iOS version of Knights of the Old Republic
kFileTypeBZF = 26000 ///< Game resource data, LZMA-compressed BIF
};

enum GameID {
Expand Down Expand Up @@ -381,6 +384,7 @@ enum ResourceType {
enum ArchiveType {
kArchiveKEY = 0, ///< KEY archive.
kArchiveBIF, ///< BIF archive.
kArchiveBZF, ///< BZF archive.
kArchiveERF, ///< ERF archive.
kArchiveRIM, ///< RIM archive.
kArchiveZIP, ///< ZIP archive.
Expand Down

0 comments on commit a8ca4b6

Please sign in to comment.