aboutsummaryrefslogtreecommitdiff
path: root/engines/glk/advsys
diff options
context:
space:
mode:
authorPaul Gilbert2019-06-09 17:10:27 -0700
committerPaul Gilbert2019-06-10 19:08:58 -0700
commitd5d801d1cba3ee95a9be48bd2a505f2e2906ead8 (patch)
tree83b4c7c3f3e2e28f6af90e274bd23bd92d037b5b /engines/glk/advsys
parente8f0e9e998a8d16ff037ed48ae7676592179d5c6 (diff)
downloadscummvm-rg350-d5d801d1cba3ee95a9be48bd2a505f2e2906ead8.tar.gz
scummvm-rg350-d5d801d1cba3ee95a9be48bd2a505f2e2906ead8.tar.bz2
scummvm-rg350-d5d801d1cba3ee95a9be48bd2a505f2e2906ead8.zip
GLK: ADVSYS: Adding opcodes and message decoding
Diffstat (limited to 'engines/glk/advsys')
-rw-r--r--engines/glk/advsys/advsys.cpp2
-rw-r--r--engines/glk/advsys/detection.cpp2
-rw-r--r--engines/glk/advsys/game.cpp99
-rw-r--r--engines/glk/advsys/game.h49
-rw-r--r--engines/glk/advsys/glk_interface.cpp7
-rw-r--r--engines/glk/advsys/glk_interface.h12
-rw-r--r--engines/glk/advsys/vm.cpp51
-rw-r--r--engines/glk/advsys/vm.h5
8 files changed, 183 insertions, 44 deletions
diff --git a/engines/glk/advsys/advsys.cpp b/engines/glk/advsys/advsys.cpp
index b745222ea1..fe02e25181 100644
--- a/engines/glk/advsys/advsys.cpp
+++ b/engines/glk/advsys/advsys.cpp
@@ -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;
diff --git a/engines/glk/advsys/detection.cpp b/engines/glk/advsys/detection.cpp
index 921be4eef8..d3b376f0b6 100644
--- a/engines/glk/advsys/detection.cpp
+++ b/engines/glk/advsys/detection.cpp
@@ -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;
diff --git a/engines/glk/advsys/game.cpp b/engines/glk/advsys/game.cpp
index 17a4104404..fe1221baf7 100644
--- a/engines/glk/advsys/game.cpp
+++ b/engines/glk/advsys/game.cpp
@@ -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
diff --git a/engines/glk/advsys/game.h b/engines/glk/advsys/game.h
index a5c7f41f55..99cfe26068 100644
--- a/engines/glk/advsys/game.h
+++ b/engines/glk/advsys/game.h
@@ -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
diff --git a/engines/glk/advsys/glk_interface.cpp b/engines/glk/advsys/glk_interface.cpp
index 3bc6800d0c..fff6bd12ed 100644
--- a/engines/glk/advsys/glk_interface.cpp
+++ b/engines/glk/advsys/glk_interface.cpp
@@ -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
diff --git a/engines/glk/advsys/glk_interface.h b/engines/glk/advsys/glk_interface.h
index 58ff831509..b54a35b250 100644
--- a/engines/glk/advsys/glk_interface.h
+++ b/engines/glk/advsys/glk_interface.h
@@ -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
diff --git a/engines/glk/advsys/vm.cpp b/engines/glk/advsys/vm.cpp
index 754a86a530..be2fed9ee7 100644
--- a/engines/glk/advsys/vm.cpp
+++ b/engines/glk/advsys/vm.cpp
@@ -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() {
diff --git a/engines/glk/advsys/vm.h b/engines/glk/advsys/vm.h
index e1b217fd31..9718b044f4 100644
--- a/engines/glk/advsys/vm.h
+++ b/engines/glk/advsys/vm.h
@@ -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];