/* 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 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, see . * */ #include "common/debug.h" #include "common/stream.h" #include "common/hash-str.h" #include "vcruise/script.h" #include "vcruise/textparser.h" namespace VCruise { enum ScriptDialect { kScriptDialectReah, kScriptDialectSchizm, kScriptDialectAD2044, }; class LogicUnscrambleStream : public Common::ReadStream { public: LogicUnscrambleStream(Common::ReadStream *stream, uint streamSize); bool eos() const override; uint32 read(void *dataPtr, uint32 dataSize) override; private: byte _cipher[255]; uint _cipherOffset; Common::ReadStream *_stream; }; LogicUnscrambleStream::LogicUnscrambleStream(Common::ReadStream *stream, uint streamSize) : _stream(stream) { int key = 255; for (int i = 0; i < 255; i++) { int parityBit = ((key ^ (key >> 1) ^ (key >> 6) ^ (key >> 7))) & 1; key = (key >> 1) | (parityBit << 7); _cipher[254 - i] = key; } _cipherOffset = 255u - (streamSize % 255u); } bool LogicUnscrambleStream::eos() const { return _stream->eos(); } uint32 LogicUnscrambleStream::read(void *dataPtr, uint32 dataSize) { uint32 numRead = _stream->read(dataPtr, dataSize); byte *decipher = static_cast(dataPtr); uint cipherOffset = _cipherOffset; uint32 remaining = numRead; while (remaining) { if (cipherOffset == 255) cipherOffset = 0; (*decipher++) ^= _cipher[cipherOffset++]; remaining--; } _cipherOffset = cipherOffset; return numRead; } Instruction::Instruction() : op(ScriptOps::kInvalid), arg(0) { } Instruction::Instruction(ScriptOps::ScriptOp paramOp) : op(paramOp), arg(0) { } Instruction::Instruction(ScriptOps::ScriptOp paramOp, int32 paramArg) : op(paramOp), arg(paramArg) { } enum ProtoOp { kProtoOpScript, // Use script opcode kProtoOpNoop, kProtoOpJumpToLabel, kProtoOpLabel, kProtoOpIf, kProtoOpElse, kProtoOpEndIf, kProtoOpSwitch, kProtoOpCase, kProtoOpEndSwitch, kProtoOpDefault, kProtoOpBreak, }; struct ProtoInstruction { ProtoInstruction(); explicit ProtoInstruction(ScriptOps::ScriptOp op); ProtoInstruction(ScriptOps::ScriptOp paramOp, int32 paramArg); ProtoInstruction(ProtoOp paramProtoOp, ScriptOps::ScriptOp paramOp, int32 paramArg); ProtoOp protoOp; ScriptOps::ScriptOp op; int32 arg; }; ProtoInstruction::ProtoInstruction() : protoOp(kProtoOpScript), op(ScriptOps::kInvalid), arg(0) { } ProtoInstruction::ProtoInstruction(ScriptOps::ScriptOp paramOp) : protoOp(kProtoOpScript), op(paramOp), arg(0) { } ProtoInstruction::ProtoInstruction(ScriptOps::ScriptOp paramOp, int32 paramArg) : protoOp(kProtoOpScript), op(paramOp), arg(paramArg) { } ProtoInstruction::ProtoInstruction(ProtoOp paramProtoOp, ScriptOps::ScriptOp paramOp, int32 paramArg) : protoOp(paramProtoOp), op(paramOp), arg(paramArg) { } struct ProtoScript { void reset(); Common::Array instrs; }; void ProtoScript::reset() { instrs.clear(); } struct ScriptNamedInstruction { const char *str; ProtoOp protoOp; ScriptOps::ScriptOp op; }; class ScriptCompiler { public: ScriptCompiler(TextParser &parser, const Common::Path &blamePath, ScriptDialect dialect, uint loadAsRoom, uint fileRoom, IScriptCompilerGlobalState *gs); void compileScriptSet(ScriptSet *ss); private: bool parseNumber(const Common::String &token, uint32 &outNumber, bool mayStartWithNonZeroDigit) const; static bool parseDecNumber(const Common::String &token, uint start, uint32 &outNumber); static bool parseHexNumber(const Common::String &token, uint start, uint32 &outNumber); static bool parseBinNumber(const Common::String &token, uint start, uint32 &outNumber); void expectNumber(uint32 &outNumber, bool mayStartWithNonZeroDigit); void compileRoomScriptSet(RoomScriptSet *rss); void compileReahOrAD2044ScreenScriptSet(ScreenScriptSet *sss); void compileSchizmScreenScriptSet(ScreenScriptSet *sss); void compileFunction(Script *script); bool compileInstructionToken(ProtoScript &script, const Common::String &token); void codeGenScript(ProtoScript &protoScript, Script &script); uint indexString(const Common::String &str); enum NumberParsingMode { kNumberParsingDec, kNumberParsingHex, kNumberParsingBin, }; TextParser &_parser; NumberParsingMode _numberParsingMode; const Common::Path _blamePath; ScriptDialect _dialect; uint _loadAsRoom; const char *_scrToken; const char *_eroomToken; Common::HashMap _stringToIndex; IScriptCompilerGlobalState *_gs; }; class ScriptCompilerGlobalState : public IScriptCompilerGlobalState { public: void define(const Common::String &key, uint roomNumber, int32 value) override; bool getDefine(const Common::String &str, uint &outRoomNumber, int32 &outValue) const override; uint getFunctionIndex(const Common::String &fnName) override; void setFunction(uint fnIndex, const Common::SharedPtr