mirror of
https://github.com/libretro/scummvm.git
synced 2025-01-27 05:32:45 +00:00
GLK: ADVSYS: Adding opcodes and message decoding
This commit is contained in:
parent
e8f0e9e998
commit
d5d801d1cb
@ -80,7 +80,7 @@ bool AdvSys::initialize() {
|
||||
return false;
|
||||
|
||||
// Load the game's header
|
||||
if (!Game::init(_gameFile))
|
||||
if (!Game::init(&_gameFile))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
|
@ -65,7 +65,7 @@ bool AdvSysMetaEngine::detectGames(const Common::FSList &fslist, DetectedGames &
|
||||
if (!gameFile.open(*file))
|
||||
continue;
|
||||
|
||||
Header hdr(gameFile);
|
||||
Header hdr(&gameFile);
|
||||
if (!hdr._valid)
|
||||
continue;
|
||||
|
||||
|
@ -36,12 +36,12 @@ void Decrypter::decrypt(byte *data, size_t size) {
|
||||
|
||||
#define HEADER_SIZE 62
|
||||
|
||||
bool Header::init(Common::ReadStream &s) {
|
||||
bool Header::init(Common::SeekableReadStream *s) {
|
||||
_valid = false;
|
||||
byte data[HEADER_SIZE];
|
||||
|
||||
// Read in the data
|
||||
if (s.read(data, HEADER_SIZE) != HEADER_SIZE)
|
||||
if (s->read(data, HEADER_SIZE) != HEADER_SIZE)
|
||||
return false;
|
||||
decrypt(data, HEADER_SIZE);
|
||||
Common::MemoryReadStream ms(data, HEADER_SIZE, DisposeAfterUse::NO);
|
||||
@ -98,9 +98,26 @@ enum LinkField {
|
||||
L_SIZE = 4
|
||||
};
|
||||
|
||||
bool Game::init(Common::SeekableReadStream &s) {
|
||||
Game::Game() : Header(), _stream(nullptr), _restartFlag(false), _residentOffset(0), _wordCount(0),
|
||||
_objectCount(0), _actionCount(0), _variableCount(0), _residentBase(nullptr),
|
||||
_wordTable(nullptr), _wordTypeTable(nullptr), _objectTable(nullptr), _actionTable(nullptr),
|
||||
_variableTable(nullptr), _saveArea(nullptr), _msgBlockNum(-1), _msgBlockOffset(0) {
|
||||
_msgCache.resize(MESSAGE_CACHE_SIZE);
|
||||
for (int idx = 0; idx < MESSAGE_CACHE_SIZE; ++idx)
|
||||
_msgCache[idx] = new CacheEntry();
|
||||
}
|
||||
|
||||
Game::~Game() {
|
||||
for (int idx = 0; idx < MESSAGE_CACHE_SIZE; ++idx)
|
||||
delete _msgCache[idx];
|
||||
}
|
||||
|
||||
bool Game::init(Common::SeekableReadStream *s) {
|
||||
// Store a copy of the game file stream
|
||||
_stream = s;
|
||||
|
||||
// Load the header
|
||||
s.seek(0);
|
||||
s->seek(0);
|
||||
if (!Header::init(s))
|
||||
return false;
|
||||
|
||||
@ -109,10 +126,10 @@ bool Game::init(Common::SeekableReadStream &s) {
|
||||
|
||||
// Load the needed resident game data and decrypt it
|
||||
_residentOffset = _dataBlockOffset * 512;
|
||||
s.seek(_residentOffset);
|
||||
s->seek(_residentOffset);
|
||||
|
||||
_data.resize(_size);
|
||||
if (!s.read(&_data[0], _size))
|
||||
if (!s->read(&_data[0], _size))
|
||||
return false;
|
||||
decrypt(&_data[0], _size);
|
||||
|
||||
@ -136,9 +153,9 @@ bool Game::init(Common::SeekableReadStream &s) {
|
||||
return true;
|
||||
}
|
||||
|
||||
void Game::restart(Common::SeekableReadStream &s) {
|
||||
s.seek(_residentOffset + _saveAreaOffset);
|
||||
s.read(_saveArea, _saveSize);
|
||||
void Game::restart() {
|
||||
_stream->seek(_residentOffset + _saveAreaOffset);
|
||||
_stream->read(_saveArea, _saveSize);
|
||||
decrypt(_saveArea, _saveSize);
|
||||
|
||||
setVariable(V_OCOUNT, _objectCount);
|
||||
@ -316,5 +333,69 @@ bool Game::inList(int link, int word) const {
|
||||
return false;
|
||||
}
|
||||
|
||||
Common::String Game::readString(int msg) {
|
||||
// Get the block to use, and ensure it's loaded
|
||||
_msgBlockNum = msg >> 7;
|
||||
_msgBlockOffset = (msg & 0x7f) << 2;
|
||||
readMsgBlock();
|
||||
|
||||
// Read the string
|
||||
Common::String result;
|
||||
char c;
|
||||
|
||||
while ((c = readMsgChar()) != '\0')
|
||||
result += c;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
char Game::readMsgChar() {
|
||||
if (_msgBlockOffset >= MESSAGE_BLOCK_SIZE) {
|
||||
// Move to the next block
|
||||
++_msgBlockNum;
|
||||
_msgBlockOffset = 0;
|
||||
readMsgBlock();
|
||||
}
|
||||
|
||||
// Return next character
|
||||
return _msgCache[0]->_data[_msgBlockOffset++];
|
||||
}
|
||||
|
||||
void Game::readMsgBlock() {
|
||||
CacheEntry *ce;
|
||||
|
||||
// Check to see if the specified block is in the cache
|
||||
for (int idx = 0; idx < MESSAGE_CACHE_SIZE; ++idx) {
|
||||
if (_msgCache[idx]->_blockNum == _msgBlockNum) {
|
||||
// If it's not already at the top of the list, move it there to ensure
|
||||
// it'll be last to be unloaded as new blocks are loaded in
|
||||
if (idx != 0) {
|
||||
ce = _msgCache[idx];
|
||||
_msgCache.remove_at(idx);
|
||||
_msgCache.insert_at(0, ce);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// At this point we need to load a new block in. Discard the block at the end
|
||||
// and move it to the start for storing the new block to load
|
||||
ce = _msgCache.back();
|
||||
_msgCache.remove_at(_msgCache.size() - 1);
|
||||
_msgCache.insert_at(0, ce);
|
||||
|
||||
// Load the new block
|
||||
ce->_blockNum = _msgBlockNum;
|
||||
_stream->seek((_messageBlockOffset + _msgBlockNum) << 9);
|
||||
if (_stream->read(&ce->_data[0], MESSAGE_BLOCK_SIZE) != MESSAGE_BLOCK_SIZE)
|
||||
error("Error reading message block");
|
||||
|
||||
// Decode the loaded block
|
||||
for (int idx = 0; idx < MESSAGE_BLOCK_SIZE; ++idx)
|
||||
ce->_data[idx] = (ce->_data[idx] + 30) & 0xff;
|
||||
}
|
||||
|
||||
|
||||
} // End of namespace AdvSys
|
||||
} // End of namespace Glk
|
||||
|
@ -30,6 +30,8 @@ namespace Glk {
|
||||
namespace AdvSys {
|
||||
|
||||
#define NIL 0
|
||||
#define MESSAGE_CACHE_SIZE 8
|
||||
#define MESSAGE_BLOCK_SIZE 512
|
||||
|
||||
/**
|
||||
* Actions
|
||||
@ -118,22 +120,36 @@ public:
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
Header(Common::ReadStream &s) {
|
||||
Header(Common::SeekableReadStream *s) {
|
||||
init(s);
|
||||
}
|
||||
|
||||
/**
|
||||
* init the header
|
||||
*/
|
||||
bool init(Common::ReadStream &s);
|
||||
bool init(Common::SeekableReadStream *s);
|
||||
};
|
||||
|
||||
/**
|
||||
* Game abstraction class
|
||||
*/
|
||||
class Game : public Header {
|
||||
struct CacheEntry {
|
||||
int _blockNum;
|
||||
char _data[MESSAGE_BLOCK_SIZE];
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
CacheEntry() : _blockNum(-1) {
|
||||
Common::fill(&_data[0], &_data[MESSAGE_BLOCK_SIZE], '\0');
|
||||
}
|
||||
};
|
||||
private:
|
||||
bool _restartFlag;
|
||||
Common::SeekableReadStream *_stream;
|
||||
Common::Array<CacheEntry *> _msgCache;
|
||||
int _msgBlockNum, _msgBlockOffset;
|
||||
private:
|
||||
/**
|
||||
* Find an object property field
|
||||
@ -166,6 +182,16 @@ private:
|
||||
* Check if a word is in an element of a given list
|
||||
*/
|
||||
bool inList(int link, int word) const;
|
||||
|
||||
/**
|
||||
* Reads in a message block from the game file
|
||||
*/
|
||||
void readMsgBlock();
|
||||
|
||||
/**
|
||||
* Read the next character for a string
|
||||
*/
|
||||
char readMsgChar();
|
||||
public:
|
||||
Common::Array<byte> _data;
|
||||
int _residentOffset;
|
||||
@ -187,20 +213,22 @@ public:
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
Game() : Header(), _restartFlag(false), _residentOffset(0), _wordCount(0), _objectCount(0),
|
||||
_actionCount(0), _variableCount(0), _residentBase(nullptr), _wordTable(nullptr),
|
||||
_wordTypeTable(nullptr), _objectTable(nullptr), _actionTable(nullptr),
|
||||
_variableTable(nullptr), _saveArea(nullptr) {}
|
||||
Game();
|
||||
|
||||
/**
|
||||
* Destructor
|
||||
*/
|
||||
~Game();
|
||||
|
||||
/**
|
||||
* init data for the game
|
||||
*/
|
||||
bool init(Common::SeekableReadStream &s);
|
||||
bool init(Common::SeekableReadStream *s);
|
||||
|
||||
/**
|
||||
* Restore savegame data from the game to it's initial state
|
||||
*/
|
||||
void restart(Common::SeekableReadStream &s);
|
||||
void restart();
|
||||
|
||||
/**
|
||||
* Returns true if the game is restarting, and resets the flag
|
||||
@ -330,6 +358,11 @@ public:
|
||||
void writeWord(int offset, int val) {
|
||||
WRITE_LE_UINT16(_residentBase + offset, val);
|
||||
}
|
||||
|
||||
/**
|
||||
* Read a string from the messages section
|
||||
*/
|
||||
Common::String readString(int msg);
|
||||
};
|
||||
|
||||
} // End of namespace AdvSys
|
||||
|
@ -25,6 +25,13 @@
|
||||
namespace Glk {
|
||||
namespace AdvSys {
|
||||
|
||||
void GlkInterface::printString(int offset) {
|
||||
// TODO
|
||||
}
|
||||
|
||||
void GlkInterface::printNumber(int number) {
|
||||
// TODO
|
||||
}
|
||||
|
||||
} // End of namespace AdvSys
|
||||
} // End of namespace Glk
|
||||
|
@ -33,6 +33,18 @@ namespace AdvSys {
|
||||
* input and output
|
||||
*/
|
||||
class GlkInterface : public GlkAPI {
|
||||
protected:
|
||||
/**
|
||||
* Print a string
|
||||
* @param offset String offset
|
||||
*/
|
||||
void printString(int offset);
|
||||
|
||||
/**
|
||||
* Print a number
|
||||
* @param number Number to print
|
||||
*/
|
||||
void printNumber(int number);
|
||||
public:
|
||||
/**
|
||||
* Constructor
|
||||
|
@ -108,23 +108,23 @@ void VM::executeOpcode() {
|
||||
if (opcode >= OP_BRT && opcode <= OP_VOWEL) {
|
||||
(this->*_METHODS[(int)opcode - 1])();
|
||||
} else if (opcode >= OP_XVAR && opcode < OP_XSET) {
|
||||
_stack.back() = getVariable((int)opcode - OP_XVAR);
|
||||
_stack.top() = getVariable((int)opcode - OP_XVAR);
|
||||
} else if (opcode >= OP_XSET && opcode < OP_XPLIT) {
|
||||
setVariable((int)opcode - OP_XSET, _stack.back());
|
||||
setVariable((int)opcode - OP_XSET, _stack.top());
|
||||
} else if (opcode >= OP_XPLIT && opcode < OP_XNLIT) {
|
||||
_stack.back() = (int)opcode - OP_XPLIT;
|
||||
_stack.top() = (int)opcode - OP_XPLIT;
|
||||
} else if (opcode >= OP_XNLIT && (int)opcode < 256) {
|
||||
_stack.back() = OP_XNLIT - opcode;
|
||||
_stack.top() = OP_XNLIT - opcode;
|
||||
} else {
|
||||
error("Unknown opcode %x at offset %d", opcode, _pc);
|
||||
}
|
||||
}
|
||||
void VM::opBRT() {
|
||||
_pc = _stack.back() ? readCodeWord() : _pc + 2;
|
||||
_pc = _stack.top() ? readCodeWord() : _pc + 2;
|
||||
}
|
||||
|
||||
void VM::opBRF() {
|
||||
_pc = !_stack.back() ? readCodeWord() : _pc + 2;
|
||||
_pc = !_stack.top() ? readCodeWord() : _pc + 2;
|
||||
}
|
||||
|
||||
void VM::opBR() {
|
||||
@ -132,11 +132,11 @@ void VM::opBR() {
|
||||
}
|
||||
|
||||
void VM::opT() {
|
||||
_stack.back() = TRUE;
|
||||
_stack.top() = TRUE;
|
||||
}
|
||||
|
||||
void VM::opNIL() {
|
||||
_stack.back() = NIL;
|
||||
_stack.top() = NIL;
|
||||
}
|
||||
|
||||
void VM::opPUSH() {
|
||||
@ -144,83 +144,84 @@ void VM::opPUSH() {
|
||||
}
|
||||
|
||||
void VM::opNOT() {
|
||||
_stack.back() = _stack.back() ? NIL : TRUE;
|
||||
_stack.top() = _stack.top() ? NIL : TRUE;
|
||||
}
|
||||
|
||||
void VM::opADD() {
|
||||
int v = _stack.pop();
|
||||
_stack.back() += v;
|
||||
_stack.top() += v;
|
||||
}
|
||||
|
||||
void VM::opSUB() {
|
||||
int v = _stack.pop();
|
||||
_stack.back() -= v;
|
||||
_stack.top() -= v;
|
||||
}
|
||||
|
||||
void VM::opMUL() {
|
||||
int v = _stack.pop();
|
||||
_stack.back() *= v;
|
||||
_stack.top() *= v;
|
||||
}
|
||||
|
||||
void VM::opDIV() {
|
||||
int v = _stack.pop();
|
||||
_stack.back() = (v == 0) ? 0 : _stack.back() / v;
|
||||
_stack.top() = (v == 0) ? 0 : _stack.top() / v;
|
||||
}
|
||||
|
||||
void VM::opREM() {
|
||||
int v = _stack.pop();
|
||||
_stack.back() = (v == 0) ? 0 : _stack.back() % v;
|
||||
_stack.top() = (v == 0) ? 0 : _stack.top() % v;
|
||||
}
|
||||
|
||||
void VM::opBAND() {
|
||||
int v = _stack.pop();
|
||||
_stack.back() &= v;
|
||||
_stack.top() &= v;
|
||||
}
|
||||
|
||||
void VM::opBOR() {
|
||||
int v = _stack.pop();
|
||||
_stack.back() |= v;
|
||||
_stack.top() |= v;
|
||||
}
|
||||
|
||||
void VM::opBNOT() {
|
||||
_stack.back() = ~_stack.back();
|
||||
_stack.top() = ~_stack.top();
|
||||
}
|
||||
|
||||
void VM::opLT() {
|
||||
int v = _stack.pop();
|
||||
_stack.back() = (_stack.back() < v) ? TRUE : NIL;
|
||||
_stack.top() = (_stack.top() < v) ? TRUE : NIL;
|
||||
}
|
||||
|
||||
void VM::opEQ() {
|
||||
int v = _stack.pop();
|
||||
_stack.back() = (_stack.back() == v) ? TRUE : NIL;
|
||||
_stack.top() = (_stack.top() == v) ? TRUE : NIL;
|
||||
}
|
||||
|
||||
void VM::opGT() {
|
||||
int v = _stack.pop();
|
||||
_stack.back() = (_stack.back() > v) ? TRUE : NIL;
|
||||
_stack.top() = (_stack.top() > v) ? TRUE : NIL;
|
||||
}
|
||||
|
||||
void VM::opLIT() {
|
||||
_stack.back() = readCodeWord();
|
||||
_stack.top() = readCodeWord();
|
||||
}
|
||||
|
||||
void VM::opVAR() {
|
||||
_stack.back() = getVariable(readCodeWord());
|
||||
_stack.top() = getVariable(readCodeWord());
|
||||
}
|
||||
|
||||
void VM::opGETP() {
|
||||
int v = _stack.pop();
|
||||
_stack.back() = getObjectProperty(_stack.back(), v);
|
||||
_stack.top() = getObjectProperty(_stack.top(), v);
|
||||
}
|
||||
|
||||
void VM::opSETP() {
|
||||
int v3 = _stack.pop();
|
||||
int v2 = _stack.pop();
|
||||
_stack.back() = setObjectProperty(_stack.back(), v2, v3);
|
||||
_stack.top() = setObjectProperty(_stack.top(), v2, v3);
|
||||
}
|
||||
|
||||
void VM::opSET() {
|
||||
setVariable(readCodeWord(), _stack.top());
|
||||
}
|
||||
|
||||
void VM::opPRINT() {
|
||||
@ -251,7 +252,7 @@ void VM::opCALL() {
|
||||
}
|
||||
|
||||
void VM::opSVAR() {
|
||||
_stack.back() = getVariable(readCodeByte());
|
||||
_stack.top() = getVariable(readCodeByte());
|
||||
}
|
||||
|
||||
void VM::opSSET() {
|
||||
|
@ -127,6 +127,11 @@ class VM : public GlkInterface, public Game {
|
||||
pop_back();
|
||||
return v;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the top of the stack (the most recently added value
|
||||
*/
|
||||
int &top() { return back(); }
|
||||
};
|
||||
private:
|
||||
static OpcodeMethod _METHODS[0x34];
|
||||
|
Loading…
x
Reference in New Issue
Block a user