PRINCE: databank.ptc base archive with decompression

This commit is contained in:
Kamil Zbróg 2013-11-09 23:07:40 +00:00
parent 6c0cd59dff
commit cdc1409dc7
12 changed files with 459 additions and 64 deletions

View File

@ -21,3 +21,125 @@
*/
#include "prince/archive.h"
#include "prince/decompress.h"
#include "common/stream.h"
#include "common/debug.h"
#include "common/memstream.h"
namespace Prince {
PtcArchive::PtcArchive() : _stream(NULL) {
}
PtcArchive::~PtcArchive() {
close();
}
static void decrypt(byte *buffer, uint32 size) {
uint32 key = 0xDEADF00D;
while (size--) {
*buffer++ += key & 0xFF;
key ^= 0x2E84299A;
key += 0x424C4148;
key = ((key & 1) << 31) | (key >> 1);
}
}
bool PtcArchive::open(const Common::String &filename) {
_stream = SearchMan.createReadStreamForMember(filename);
if (!_stream)
return false;
uint32 magic = _stream->readUint32LE();
uint32 fileTableOffset = _stream->readUint32LE() ^ 0x4D4F4B2D; // MOK-
uint32 fileTableSize = _stream->readUint32LE() ^ 0x534F4654; // SOFT
debug("fileTableOffset : %08X", fileTableOffset);
debug("fileTableSize: %08X", fileTableSize);
_stream->seek(fileTableOffset);
byte *fileTable = new byte[fileTableSize];
byte *fileTableEnd = fileTable + fileTableSize;
_stream->read(fileTable, fileTableSize);
decrypt(fileTable, fileTableSize);
for (byte *fileItem = fileTable; fileItem < fileTableEnd; fileItem += 32) {
FileEntry item;
Common::String name = (const char*)fileItem;
item._offset = READ_LE_UINT32(fileItem + 24);
item._size = READ_LE_UINT32(fileItem + 28);
debug("%12s %8X %d", name.c_str(), item._offset, item._size);
_items[name] = item;
}
delete[] fileTable;
return true;
}
void PtcArchive::close() {
delete _stream;
_stream = NULL;
_items.clear();
}
bool PtcArchive::hasFile(const Common::String &name) const {
return _items.contains(name);
}
int PtcArchive::listMembers(Common::ArchiveMemberList &list) const {
int matches = 0;
for (FileMap::const_iterator it = _items.begin(); it != _items.end(); ++it) {
list.push_back(Common::ArchiveMemberList::value_type(new Common::GenericArchiveMember(it->_key, this)));
matches++;
}
return matches;
}
const Common::ArchiveMemberPtr PtcArchive::getMember(const Common::String &name) const {
if (!_items.contains(name)) {
Common::ArchiveMemberPtr();
}
return Common::ArchiveMemberList::value_type(new Common::GenericArchiveMember(name, this));
}
Common::SeekableReadStream *PtcArchive::createReadStreamForMember(const Common::String &name) const {
if (!_items.contains(name)) {
return 0;
}
const FileEntry &entryHeader = _items[name];
if (entryHeader._size < 4)
return 0;
uint32 size = entryHeader._size;
_stream->seek(entryHeader._offset);
// This *HAS* to be malloc (not new[]) because MemoryReadStream uses free() to free the memory
byte* buffer = (byte *)malloc(size);
_stream->read(buffer, size);
if (READ_BE_UINT32(buffer) == 0x4D41534D) {
Decompressor dec;
uint32 decompLen = READ_BE_UINT32(buffer + 14);
byte *decompData = (byte*)malloc(decompLen);
dec.decompress(buffer + 18, decompData, decompLen);
free(buffer);
size = decompLen;
buffer = decompData;
}
debug("PtcArchive::createReadStreamForMember name %s", name.c_str());
return new Common::MemoryReadStream(buffer, size, DisposeAfterUse::YES);
}
}
/* vim: set tabstop=4 noexpandtab: */

View File

@ -24,13 +24,40 @@
#define PRINCE_ARCHIVE_H
#include "common/archive.h"
#include "common/hashmap.h"
#include "common/hash-str.h"
// This is here just as remainder that archive support is missing
namespace Price {
namespace Prince {
class PtcArchive : public Common::Archive {
public:
PtcArchive();
~PtcArchive();
bool open(const Common::String &filename);
void close();
bool isOpen() const { return _stream != 0; }
// Common::Archive API implementation
bool hasFile(const Common::String &name) const;
int listMembers(Common::ArchiveMemberList &list) const;
const Common::ArchiveMemberPtr getMember(const Common::String &name) const;
Common::SeekableReadStream *createReadStreamForMember(const Common::String &name) const;
private:
struct FileEntry {
uint32 _offset;
uint32 _size;
};
Common::SeekableReadStream *_stream;
typedef Common::HashMap<Common::String, FileEntry, Common::IgnoreCase_Hash, Common::IgnoreCase_EqualTo> FileMap;
FileMap _items;
};
}
} // End of namespace Prince
#endif
/* vim: set tabstop=4 noexpandtab: */

View File

@ -25,7 +25,7 @@
namespace Prince {
Debugger::Debugger(PrinceEngine *vm) : GUI::Debugger(), _vm(vm) {
Debugger::Debugger(PrinceEngine *vm) : GUI::Debugger(), _vm(vm), _locationNr(0) {
DCmd_Register("continue", WRAP_METHOD(Debugger, Cmd_Exit));
DCmd_Register("level", WRAP_METHOD(Debugger, Cmd_DebugLevel));
DCmd_Register("setflag", WRAP_METHOD(Debugger, Cmd_SetFlag));
@ -135,8 +135,7 @@ bool Debugger::Cmd_InitRoom(int argc, const char **argv) {
return true;
}
int flagNum = strToInt(argv[1]);
_vm->loadLocation(flagNum);
_locationNr = strToInt(argv[1]);
return true;
}

View File

@ -35,6 +35,8 @@ public:
Debugger(PrinceEngine *vm);
virtual ~Debugger() {} // we need this for __SYMBIAN32__ archaic gcc/UIQ
uint32 _locationNr;
private:
bool Cmd_DebugLevel(int argc, const char **argv);
bool Cmd_SetFlag(int argc, const char **argv);

View File

@ -0,0 +1,169 @@
/* 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
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
*/
#include "prince/decompress.h"
namespace Prince {
static const uint16 table1[] = {
0x8000, 0x0002,
0x4000, 0x0004,
0x2000, 0x0008,
0x1000, 0x0010,
0x0800, 0x0020,
0x0400, 0x0040,
0x0200, 0x0080,
0x0100, 0x0100,
0x0080, 0x0200,
0x0040, 0x0400
};
static const uint32 table2[] = {
0x0000F000,
0x0020FC00,
0x00A0FF00,
0x02A0FF80,
0x06A0FFC0,
0x0EA0FFE0,
0x1EA0FFF0,
0x3EA0FFF8
};
static const uint16 table3[] = {
0x8000, 0x0000,
0x4000, 0x0002,
0x2000, 0x0006,
0x1000, 0x000E,
0x0800, 0x001E,
0x0400, 0x003E,
0x0200, 0x007E,
0x0100, 0x00FE,
0x0080, 0x01FE,
0x0040, 0x03FE,
0x0020, 0x07FE,
0x0010, 0x0FFE,
0x0008, 0x1FFE,
0x0004, 0x3FFE,
0x0002, 0x7FFE,
0x0001, 0xFFFE
};
void Decompressor::decompress(byte *source, byte *dest, uint32 destSize) {
byte *destEnd = dest + destSize;
int more;
_src = source;
_dst = dest;
_bitBuffer = 0x80;
while (_dst < destEnd) {
uint32 ebp;
uint16 offset, length;
if (getBit()) {
if (getBit()) {
if (getBit()) {
if (getBit()) {
if (getBit()) {
if (getBit()) {
uint32 tableIndex = 0;
while (getBit())
tableIndex++;
length = table3[tableIndex * 2 + 0];
do {
more = !(length & 0x8000);
length = (length << 1) | getBit();
} while (more);
length += table3[tableIndex * 2 + 1];
length++;
memcpy(_dst, _src, length);
_src += length;
_dst += length;
}
*_dst++ = *_src++;
}
*_dst++ = *_src++;
}
*_dst++ = *_src++;
}
*_dst++ = *_src++;
}
*_dst++ = *_src++;
}
if (!getBit()) {
if (getBit()) {
uint32 tableIndex = getBit();
tableIndex = (tableIndex << 1) | getBit();
tableIndex = (tableIndex << 1) | getBit();
ebp = table2[tableIndex];
length = 1;
} else {
ebp = 0x0000FF00;
length = 0;
}
} else {
uint32 tableIndex = 0;
while (getBit())
tableIndex++;
length = table1[tableIndex * 2 + 0];
do {
more = !(length & 0x8000);
length = (length << 1) | getBit();
} while (more);
length += table1[tableIndex * 2 + 1];
tableIndex = getBit();
tableIndex = (tableIndex << 1) | getBit();
tableIndex = (tableIndex << 1) | getBit();
ebp = table2[tableIndex];
}
offset = ebp & 0xFFFF;
do {
if (_bitBuffer == 0x80) {
if (offset >= 0xFF00) {
offset = (offset << 8) | *_src++;
}
}
more = offset & 0x8000;
offset = (offset << 1) | getBit();
} while (more);
offset += (ebp >> 16);
length += 2;
while (length--) {
if (_dst >= destEnd) {
return;
}
*_dst = *(_dst - offset);
_dst++;
}
}
}
int Decompressor::getBit() {
int bit = (_bitBuffer & 0x80) >> 7;
_bitBuffer <<= 1;
if (_bitBuffer == 0) {
_bitBuffer = *_src++;
bit = (_bitBuffer & 0x80) >> 7;
_bitBuffer <<= 1;
_bitBuffer |= 1;
}
return bit;
}
} // End of namespace Prince

View File

@ -0,0 +1,42 @@
/* 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
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
*/
#ifndef PRINCE_DECOMPRESS_H
#define PRINCE_DECOMPRESS_H
#include "engines/util.h"
namespace Prince {
class Decompressor {
public:
void decompress(byte *source, byte *dest, uint32 destSize);
protected:
byte *_src, *_dst;
byte _bitBuffer;
int _bitsLeft;
int getBit();
};
} // End of namespace Prince
#endif

View File

@ -35,7 +35,8 @@ namespace Prince {
class Mob {
public:
Mob() {}
Mob() : _name(""), _examText("") {}
bool loadFromStream(Common::SeekableReadStream &stream);

View File

@ -12,8 +12,10 @@ MODULE_OBJS = \
sound.o \
flags.o \
variatxt.o \
cursor.o \
prince.o
prince.o \
archive.o \
decompress.o \
cursor.o
# This module can be built as a plugin
ifeq ($(ENABLE_PRINCE), DYNAMIC_PLUGIN)

View File

@ -36,7 +36,9 @@ Object::Object() : _surface(NULL), _x(0), _y(0), _z(0) {
}
Object::~Object() {
_surface->free();
delete _surface;
_surface = NULL;
}
void Object::loadSurface(Common::SeekableReadStream &stream) {

View File

@ -55,6 +55,7 @@
#include "prince/font.h"
#include "prince/mhwanh.h"
#include "prince/cursor.h"
#include "prince/archive.h"
namespace Prince {
@ -71,7 +72,7 @@ void PrinceEngine::debugEngine(const char *s, ...) {
PrinceEngine::PrinceEngine(OSystem *syst, const PrinceGameDescription *gameDesc) :
Engine(syst), _gameDescription(gameDesc), _graph(NULL), _script(NULL),
_locationNr(0), _debugger(NULL), _objectList(NULL), _mobList(NULL), _midiPlayer(NULL),
_locationNr(0), _debugger(NULL), _midiPlayer(NULL),
_cameraX(0), _newCameraX(0), _frameNr(0), _cursor1(NULL), _cursor2(NULL), _font(NULL),
_walizkaBmp(NULL), _roomBmp(NULL), _voiceStream(NULL) {
@ -82,8 +83,6 @@ PrinceEngine::PrinceEngine(OSystem *syst, const PrinceGameDescription *gameDesc)
DebugMan.enableDebugChannel("script");
gDebugLevel = 10;
}
PrinceEngine::~PrinceEngine() {
@ -101,10 +100,6 @@ PrinceEngine::~PrinceEngine() {
delete _variaTxt;
delete[] _talkTxt;
delete _graph;
delete _mobList;
delete _objectList;
_midiPlayer->killMidi();
delete _midiPlayer;
}
GUI::Debugger *PrinceEngine::getDebugger() {
@ -127,11 +122,12 @@ bool loadFromStream<Graphics::BitmapDecoder>(Graphics::BitmapDecoder &image, Com
}
template<typename T>
bool loadResource(T *resource, const char *resourceName) {
bool loadResource(T *resource, const char *resourceName, bool required = true) {
Common::SeekableReadStream *stream = SearchMan.createReadStreamForMember(resourceName);
if (!stream) {
error("Can't load %s", resourceName);
return NULL;
if (required)
error("Can't load %s", resourceName);
return false;
}
bool ret = loadFromStream(*resource, *stream);
@ -141,44 +137,73 @@ bool loadResource(T *resource, const char *resourceName) {
return ret;
}
template <typename T>
bool loadResource(Common::Array<T> &array, const char *resourceName, bool required = true) {
Common::SeekableReadStream *stream = SearchMan.createReadStreamForMember(resourceName);
if (!stream) {
if (required) {
error("Can't load %s", resourceName);
return false;
}
}
typename Common::Array<T>::value_type t;
while (t.loadFromStream(*stream))
array.push_back(t);
delete stream;
return true;
}
void PrinceEngine::init() {
_graph = new GraphicsMan(this);
_rnd = new Common::RandomSource("prince");
_debugger = new Debugger(this);
_midiPlayer = new MusicPlayer(this);
const Common::FSNode gameDataDir(ConfMan.get("path"));
debugEngine("Adding all path: %s", gameDataDir.getPath().c_str());
PtcArchive *all = new PtcArchive();
if (!all->open("all/databank.ptc"))
error("Can't open all/databank.ptc");
PtcArchive *voices = new PtcArchive();
if (!voices->open("data/voices/databank.ptc"))
error("Can't open data/voices/databank.ptc");
SearchMan.add("all", all);
SearchMan.add("data/voices", voices);
_graph = new GraphicsMan(this);
_rnd = new Common::RandomSource("prince");
_debugger = new Debugger(this);
SearchMan.addSubDirectoryMatching(gameDataDir, "all", 0, 2);
SearchMan.addSubDirectoryMatching(gameDataDir, "data/voices/output", 0, 2);
SearchMan.addSubDirectoryMatching(gameDataDir, "data/voices", 0, 2);
_midiPlayer = new MusicPlayer(this);
_font = new Font();
loadResource(_font, "font1.raw");
loadResource(_font, "all/font1.raw");
_walizkaBmp = new MhwanhDecoder();
loadResource(_walizkaBmp, "walizka");
loadResource(_walizkaBmp, "all/walizka");
_script = new Script(this);
loadResource(_script, "skrypt.dat");
loadResource(_script, "all/skrypt.dat");
_variaTxt = new VariaTxt();
loadResource(_variaTxt, "variatxt.dat");
loadResource(_variaTxt, "all/variatxt.dat");
_cursor1 = new Cursor();
loadResource(_cursor1, "mouse1.cur");
loadResource(_cursor1, "all/mouse1.cur");
_cursor2 = new Cursor();
loadResource(_cursor2, "mouse2.cur");
loadResource(_cursor2, "all/mouse2.cur");
Common::SeekableReadStream *talkTxtStream = SearchMan.createReadStreamForMember("talktxt.dat");
Common::SeekableReadStream *talkTxtStream = SearchMan.createReadStreamForMember("all/talktxt.dat");
if (!talkTxtStream) {
error("Can't load talkTxtStream");
return;
}
_talkTxtSize = talkTxtStream->size();
_talkTxt = new byte[_talkTxtSize];
talkTxtStream->read(_talkTxt, _talkTxtSize);
@ -202,12 +227,15 @@ Common::Error PrinceEngine::run() {
showLogo();
// return Common::kNoError;
mainLoop();
return Common::kNoError;
}
bool PrinceEngine::loadLocation(uint16 locationNr) {
_debugger->_locationNr = locationNr;
debugEngine("PrinceEngine::loadLocation %d", locationNr);
const Common::FSNode gameDataDir(ConfMan.get("path"));
SearchMan.remove(Common::String::format("%02d", _locationNr));
@ -215,7 +243,12 @@ bool PrinceEngine::loadLocation(uint16 locationNr) {
const Common::String locationNrStr = Common::String::format("%02d", _locationNr);
debugEngine("loadLocation %s", locationNrStr.c_str());
SearchMan.addSubDirectoryMatching(gameDataDir, locationNrStr, 0, 2);
PtcArchive *locationArchive = new PtcArchive();
if (!locationArchive->open(locationNrStr + "/databank.ptc"))
error("Can't open location %s", locationNrStr.c_str());
SearchMan.add(locationNrStr, locationArchive);
delete _roomBmp;
// load location background
@ -225,14 +258,9 @@ bool PrinceEngine::loadLocation(uint16 locationNr) {
_sceneWidth = _roomBmp->getSurface()->w;
}
delete _mobList;
_mobList = new MobList();
_mobList.clear();
loadResource(_mobList, "mob.lst");
delete _objectList;
_objectList = new ObjectList();
loadResource(_objectList, "obj.lst");
const char *musName = MusicPlayer::_musTable[MusicPlayer::_musRoomTable[locationNr]];
_midiPlayer->loadMidi(musName);
@ -395,19 +423,18 @@ void PrinceEngine::keyHandler(Common::Event event) {
}
void PrinceEngine::hotspot() {
if (!_mobList)
return;
Common::Point mousepos = _system->getEventManager()->getMousePos();
Common::Point mousePosCamera(mousepos.x + _cameraX, mousepos.y);
for (Common::Array<Mob>::const_iterator it = _mobList->_list.begin()
; it != _mobList->_list.end() ; ++it) {
if (it->_visible)
for (Common::Array<Mob>::const_iterator it = _mobList.begin()
; it != _mobList.end() ; ++it) {
const Mob& mob = *it;
if (mob._visible)
continue;
if (it->_rect.contains(mousePosCamera)) {
if (mob._rect.contains(mousePosCamera)) {
uint16 textW = 0;
for (uint16 i = 0; i < it->_name.size(); ++i)
textW += _font->getCharWidth(it->_name[i]);
for (uint16 i = 0; i < mob._name.size(); ++i)
textW += _font->getCharWidth(mob._name[i]);
uint16 x = mousepos.x - textW/2;
if (x > _graph->_frontScreen->w)
@ -418,7 +445,7 @@ void PrinceEngine::hotspot() {
_font->drawString(
_graph->_frontScreen,
it->_name,
mob._name,
x,
mousepos.y - _font->getFontHeight(),
_graph->_frontScreen->w,
@ -500,6 +527,9 @@ void PrinceEngine::drawScreen() {
void PrinceEngine::mainLoop() {
loadLocation(2);
changeCursor(1);
while (!shouldQuit()) {
uint32 currentTime = _system->getMillis();
@ -530,7 +560,7 @@ void PrinceEngine::mainLoop() {
if (shouldQuit())
return;
_script->step();
//_script->step();
drawScreen();
// Calculate the frame delay based off a desired frame time
@ -541,6 +571,10 @@ void PrinceEngine::mainLoop() {
_cameraX = _newCameraX;
++_frameNr;
if (_debugger->_locationNr != _locationNr) {
loadLocation(_debugger->_locationNr);
}
}
}

View File

@ -47,21 +47,14 @@
namespace Prince {
template <typename ResourceType>
struct ResourceList {
#if 0
bool loadFromStream(Common::SeekableReadStream &stream) {
ResourceType resource;
while (resource.loadFromStream(stream))
ResourceType *resource = new ResourceType();
while (resource->loadFromStream(stream))
_list.push_back(resource);
return true;
}
Common::Array<ResourceType> _list;
};
typedef ResourceList<Mob> MobList;
typedef ResourceList<Object> ObjectList;
#endif
struct PrinceGameDescription;
class PrinceEngine;
@ -157,12 +150,12 @@ private:
GraphicsMan *_graph;
Script *_script;
Font *_font;
ObjectList *_objectList;
MobList *_mobList;
MusicPlayer *_midiPlayer;
Audio::SoundHandle _soundHandle;
Common::SeekableReadStream *_voiceStream;
Common::Array<Mob> _mobList;
Common::Array<Object *> _objectList;
uint16 _cameraX;
uint16 _newCameraX;

View File

@ -168,6 +168,8 @@ void MusicPlayer::loadMidi(const char * name) {
_data = (byte *)malloc(_dataSize);
stream->read(_data, _dataSize);
delete stream;
// Start playing the music
sndMidiStart();
}