aboutsummaryrefslogtreecommitdiff
path: root/engines
diff options
context:
space:
mode:
Diffstat (limited to 'engines')
-rw-r--r--engines/draci/barchive.cpp232
-rw-r--r--engines/draci/barchive.h12
-rw-r--r--engines/draci/draci.cpp12
3 files changed, 233 insertions, 23 deletions
diff --git a/engines/draci/barchive.cpp b/engines/draci/barchive.cpp
index d11087a809..7865b1b574 100644
--- a/engines/draci/barchive.cpp
+++ b/engines/draci/barchive.cpp
@@ -34,24 +34,111 @@
namespace Draci {
const char BArchive::_magicNumber[] = "BAR!";
+const char BArchive::_dfwMagicNumber[] = "BS";
+
+/**
+ * @brief Loads a DFW archive
+ * @param path Path to input file
+ *
+ * Tries to load the file as a DFW archive if opening as BAR fails. Should only be called
+ * from openArchive(). Only one of the game files appears to use this format (HRA.DFW)
+ * and this file is compressed using a simple run-length scheme.
+ *
+ * archive format: header
+ * index table
+ * file0, file1, ...
+ *
+ * header format: [uint16LE] file count
+ * [uint16LE] index table size
+ * [2 bytes] magic number "BS"
+ *
+ * index table format: entry0, entry1, ...
+ *
+ * entry<N> format: [uint16LE] compressed size (not including the 2 bytes for the
+ * "uncompressed size" field)
+ * [uint32LE] fileN offset from start of file
+ *
+ * file<N> format: [uint16LE] uncompressed size
+ * [uint16LE] compressed size (the same as in the index table entry)
+ * [byte] stopper mark (for run-length compression)
+ * [multiple bytes] compressed data
+ */
+
+void BArchive::openDFW(const Common::String &path) {
+ byte *table;
+ uint16 tableSize;
+ byte buf[2];
+
+ Common::File f;
+
+ f.open(path);
+ if (!f.isOpen()) {
+ debugC(2, kDraciArchiverDebugLevel, "Error opening file");
+ return;
+ }
+
+ _fileCount = f.readUint16LE();
+ tableSize = f.readUint16LE();
+
+ f.read(buf, 2);
+ if (memcmp(buf, _dfwMagicNumber, 2) == 0) {
+ debugC(2, kDraciArchiverDebugLevel, "Success");
+ _isDFW = true;
+ } else {
+ debugC(2, kDraciArchiverDebugLevel, "Not a DFW archive");
+ f.close();
+ return;
+ }
+
+ debugC(2, kDraciArchiverDebugLevel, "Archive info (DFW): %d files", _fileCount);
+
+ // Read in index table
+ table = new byte[tableSize];
+ f.read(table, tableSize);
+
+ // Read in file headers, but do not read the actual data yet
+ // The data will be read on demand to save memory
+ _files = new BAFile[_fileCount];
+ Common::MemoryReadStream tableReader(table, tableSize);
+ for (unsigned int i = 0; i < _fileCount; ++i) {
+ _files[i]._compLength = tableReader.readUint16LE();
+ _files[i]._offset = tableReader.readUint32LE();
+
+ // Seek to the current file
+ f.seek(_files[i]._offset);
+
+ _files[i]._length = f.readUint16LE(); // Read in uncompressed length
+ f.readUint16LE(); // Compressed length again (already read from the index table)
+ _files[i]._stopper = f.readByte();
+
+ _files[i]._data = NULL; // File data will be read in on demand
+ _files[i]._crc = 0; // Dummy value; not used in DFW archives
+ }
+
+ delete[] table;
+}
/**
* @brief BArchive open method
* @param path Path to input file
*
* Opens a BAR (Bob's Archiver) archive, which is the game's archiving format.
- * BAR archives have a .DFW file extension, due to an unused historical interface.
+ * BAR archives have a .DFW file extension, due to a historical interface.
*
* archive format: header,
* file0, file1, ...
* footer
+ *
* header format: [4 bytes] magic number "BAR!"
* [uint16LE] file count (number of archived streams),
* [uint32LE] footer offset from start of file
+ *
* file<N> format: [2 bytes] compressed length
* [2 bytes] original length
* [1 byte] compression type
* [1 byte] CRC
+ * [multiple bytes] actual data
+ *
* footer format: [array of uint32LE] offsets of individual files from start of archive
* (last entry is footer offset again)
*/
@@ -65,8 +152,7 @@ void BArchive::openArchive(const Common::String &path) {
// Close previously opened archive (if any)
closeArchive();
- debugCN(2, kDraciArchiverDebugLevel, "Loading BAR archive %s: ",
- path.c_str());
+ debugCN(2, kDraciArchiverDebugLevel, "Loading archive %s: ", path.c_str());
f.open(path);
if (f.isOpen()) {
@@ -80,14 +166,17 @@ void BArchive::openArchive(const Common::String &path) {
_path = path;
// Read archive header
- debugCN(2, kDraciArchiverDebugLevel, "Checking magic number: ");
+ debugCN(2, kDraciArchiverDebugLevel, "Checking for BAR magic number: ");
f.read(buf, 4);
if (memcmp(buf, _magicNumber, 4) == 0) {
debugC(2, kDraciArchiverDebugLevel, "Success");
+ _isDFW = false;
} else {
- debugC(2, kDraciArchiverDebugLevel, "Error");
+ debugC(2, kDraciArchiverDebugLevel, "Not a BAR archive");
+ debugCN(2, kDraciArchiverDebugLevel, "Retrying as DFW: ");
f.close();
+ openDFW(_path);
return;
}
@@ -113,7 +202,8 @@ void BArchive::openArchive(const Common::String &path) {
fileOffset = reader.readUint32LE();
f.seek(fileOffset); // Seek to next file in archive
- f.readUint16LE(); // Compressed size, not used
+ _files[i]._compLength = f.readUint16LE(); // Compressed size
+ // should be the same as uncompressed
_files[i]._length = f.readUint16LE(); // Original size
_files[i]._offset = fileOffset;
@@ -122,6 +212,7 @@ void BArchive::openArchive(const Common::String &path) {
_files[i]._crc = f.readByte(); // CRC checksum of the file
_files[i]._data = NULL; // File data will be read in on demand
+ _files[i]._stopper = 0; // Dummy value; not used in BAR files, needed in DFW
}
// Last footer item should be equal to footerOffset
@@ -153,23 +244,18 @@ void BArchive::closeArchive(void) {
_fileCount = 0;
}
-BAFile *BArchive::operator[](unsigned int i) const {
+/**
+ * @brief On-demand BAR file loader
+ * @param i Index of file inside an archive
+ * @return Pointer to a BAFile coresponding to the opened file or NULL (on failure)
+ *
+ * Loads individual BAR files from an archive to memory on demand.
+ * Should not be called directly. Instead, one should access files
+ * through the operator[] interface.
+ */
+BAFile *BArchive::loadFileBAR(unsigned int i) const {
Common::File f;
- // Check whether requested file exists
- if (i >= _fileCount) {
- return NULL;
- }
-
- debugCN(2, kDraciArchiverDebugLevel, "Accessing file %d from archive %s... ",
- i, _path.c_str());
-
- // Check if file has already been opened and return that
- if (_files[i]._data) {
- debugC(2, kDraciArchiverDebugLevel, "Success");
- return _files + i;
- }
-
// Else open archive and read in requested file
f.open(_path);
if (f.isOpen()) {
@@ -197,6 +283,110 @@ BAFile *BArchive::operator[](unsigned int i) const {
return _files + i;
}
+/**
+ * @brief On-demand DFW file loader
+ * @param i Index of file inside an archive
+ * @return Pointer to a BAFile coresponding to the opened file or NULL (on failure)
+ *
+ * Loads individual DFW files from an archive to memory on demand.
+ * Should not be called directly. Instead, one should access files
+ * through the operator[] interface.
+ */
+BAFile *BArchive::loadFileDFW(unsigned int i) const {
+ Common::File f;
+ byte *buf;
+
+ // Else open archive and read in requested file
+ f.open(_path);
+ if (f.isOpen()) {
+ debugC(2, kDraciArchiverDebugLevel, "Success");
+ } else {
+ debugC(2, kDraciArchiverDebugLevel, "Error");
+ return NULL;
+ }
+
+ // Seek to raw data of the file
+ // Five bytes are for the header (uncompressed and compressed length, stopper mark)
+ f.seek(_files[i]._offset + 5);
+
+ // Since we are seeking directly to raw data, we subtract 3 bytes from the length
+ // (to take account the compressed length and stopper mark)
+ uint16 compressedLength = _files[i]._compLength - 3;
+ uint16 uncompressedLength = _files[i]._length;
+
+ debugC(2, kDraciArchiverDebugLevel,
+ "File info (DFW): uncompressed %d bytes, compressed %d bytes",
+ uncompressedLength, compressedLength);
+
+ // Allocate a buffer for the file data
+ buf = new byte[compressedLength];
+
+ // Read in file data into the buffer
+ f.read(buf, compressedLength);
+
+ // Allocate the space for the uncompressed file
+ byte *dst;
+ dst = _files[i]._data = new byte[uncompressedLength];
+
+ Common::MemoryReadStream data(buf, compressedLength);
+
+ // Uncompress file
+ byte current, what;
+ byte stopper = _files[i]._stopper;
+ unsigned int repeat;
+ unsigned int len = 0; // Sanity check (counts uncompressed bytes)
+
+ current = data.readByte(); // Read initial byte
+ while (!data.eos()) {
+
+ if (current != stopper) {
+ *dst++ = current;
+ ++len;
+ } else {
+ // Inflate block
+ repeat = data.readByte();
+ what = data.readByte();
+ len += repeat;
+ for (unsigned int j = 0; j < repeat; ++j) {
+ *dst++ = what;
+ }
+ }
+
+ current = data.readByte();
+ }
+
+ assert(len == _files[i]._length && "Uncompressed file not of the expected length");
+
+ return _files + i;
+}
+
+
+BAFile *BArchive::operator[](unsigned int i) const {
+
+ // Check whether requested file exists
+ if (i >= _fileCount) {
+ return NULL;
+ }
+
+ debugCN(2, kDraciArchiverDebugLevel, "Accessing file %d from archive %s... ",
+ i, _path.c_str());
+
+ // Check if file has already been opened and return that
+ if (_files[i]._data) {
+ debugC(2, kDraciArchiverDebugLevel, "Success");
+ return _files + i;
+ }
+
+ BAFile *file;
+ if (_isDFW) {
+ file = loadFileDFW(i);
+ } else {
+ file = loadFileBAR(i);
+ }
+
+ return file;
+}
+
} // End of namespace Draci
diff --git a/engines/draci/barchive.h b/engines/draci/barchive.h
index 683ec50a67..0971375c7b 100644
--- a/engines/draci/barchive.h
+++ b/engines/draci/barchive.h
@@ -35,10 +35,12 @@ namespace Draci {
*/
struct BAFile {
- uint16 _length;
- uint32 _offset; //!< Offset of file inside archive
+ uint16 _compLength; //!< Compressed length (the same as _length if the file is uncompressed)
+ uint16 _length; //!< Uncompressed length
+ uint32 _offset; //!< Offset of file inside archive
byte *_data;
byte _crc;
+ byte _stopper; //!< Not used in BAR files, needed for DFW
/** Releases the file data (for memory considerations) */
void closeFile(void) {
@@ -62,6 +64,7 @@ public:
private:
// Archive header data
static const char _magicNumber[];
+ static const char _dfwMagicNumber[];
static const unsigned int _archiveHeaderSize = 10;
// File stream header data
@@ -70,6 +73,11 @@ private:
Common::String _path; //!< Path to file
BAFile *_files; //!< Internal array of files
uint16 _fileCount; //!< Number of files in archive
+ bool _isDFW; //!< True if the archive is in DFW format, false otherwise
+
+ void openDFW(const Common::String &path);
+ BAFile *loadFileDFW(unsigned int i) const;
+ BAFile *loadFileBAR(unsigned int i) const;
};
} // End of namespace Draci
diff --git a/engines/draci/draci.cpp b/engines/draci/draci.cpp
index 103f595a12..2f55906a2b 100644
--- a/engines/draci/draci.cpp
+++ b/engines/draci/draci.cpp
@@ -29,6 +29,7 @@
#include "common/events.h"
#include "common/file.h"
+#include "graphics/cursorman.h"
#include "graphics/font.h"
#include "draci/draci.h"
@@ -170,6 +171,15 @@ int DraciEngine::go() {
debugC(5, kDraciGeneralDebugLevel, "Finished frame %d", t);
}
+ path = "HRA.DFW";
+ ar.closeArchive();
+ ar.openArchive(path);
+ f = ar[0];
+ Sprite sp(f->_data, f->_length, 0, 0, true);
+ CursorMan.pushCursorPalette(palette, 0, 256);
+ CursorMan.pushCursor(sp._data, sp._width, sp._height, sp._width / 2, sp._height / 2);
+ CursorMan.showMouse(true);
+
Common::Event event;
bool quit = false;
while (!quit) {
@@ -177,6 +187,8 @@ int DraciEngine::go() {
switch (event.type) {
case Common::EVENT_QUIT:
quit = true;
+ case Common::EVENT_MOUSEMOVE:
+ _system->warpMouse(event.mouse.x, event.mouse.y);
default:
break;
}