Initial commit

This commit is contained in:
2026-02-02 04:50:13 +01:00
commit 5b11698731
22592 changed files with 7677434 additions and 0 deletions

2
engines/private/POTFILES Normal file
View File

@@ -0,0 +1,2 @@
engines/private/detection.cpp
engines/private/metaengine.cpp

439
engines/private/code.cpp Normal file
View File

@@ -0,0 +1,439 @@
/* 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 <http://www.gnu.org/licenses/>.
*
*/
// Heavily inspired by hoc
// Copyright (C) AT&T 1995
// All Rights Reserved
//
// Permission to use, copy, modify, and distribute this software and
// its documentation for any purpose and without fee is hereby
// granted, provided that the above copyright notice appear in all
// copies and that both that the copyright notice and this
// permission notice and warranty disclaimer appear in supporting
// documentation, and that the name of AT&T or any of its entities
// not be used in advertising or publicity pertaining to
// distribution of the software without specific, written prior
// permission.
//
// AT&T DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
// INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS.
// IN NO EVENT SHALL AT&T OR ANY OF ITS ENTITIES BE LIABLE FOR ANY
// SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER
// IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
// ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
// THIS SOFTWARE.
#include "common/debug.h"
#include "common/hash-ptr.h"
#include "common/str.h"
#include "private/grammar.h"
#include "private/private.h"
#include "private/tokens.h"
namespace Private {
Gen::VM *Gen::g_vm;
void Gen::VM::run() {
Gen::execute(Gen::g_vm->_prog);
}
namespace Settings {
using namespace Gen;
SettingMaps *g_setts;
SettingMaps::SettingMaps() :
_setting(nullptr) {
}
SettingMaps::~SettingMaps() {
for (uint i = 0; i < _settings.size(); i++) {
free(_settings[i]);
}
}
/* initialize setting for code generation */
void SettingMaps::init() {
_setting = (Setting *)malloc(sizeof(Setting));
_settings.push_back(_setting);
memset((void *)_setting, 0, sizeof(Setting));
g_vm->_prog = (Inst *)&_setting->prog;
g_vm->_stack = (Datum *)&_setting->stack;
g_vm->_progp = Gen::g_vm->_prog;
g_vm->_stackp = Gen::g_vm->_stack;
}
void SettingMaps::save(const char *name) {
_map.setVal(name, _setting);
}
void SettingMaps::load(const Common::String &name) {
assert(_map.contains(name));
_setting = _map.getVal(name);
debugC(1, kPrivateDebugCode, "loading setting %s", name.c_str());
g_vm->_prog = (Inst *)&_setting->prog;
g_vm->_stack = (Datum *)&_setting->stack;
g_vm->_stackp = g_vm->_stack;
g_vm->_progp = g_vm->_prog;
}
} // end of namespace Settings
namespace Gen {
/* pop and return top elem from stack */
Datum pop() {
assert(!(g_vm->_stackp <= g_vm->_stack));
return *--g_vm->_stackp;
}
/* push d onto stack */
int push(const Datum &d) {
assert(!(g_vm->_stackp >= &g_vm->_stack[NSTACK]));
*g_vm->_stackp++ = d;
return 0;
}
/* push constant onto stack */
int constpush() {
Datum d;
Symbol *s = (Symbol *)*g_vm->_pc++;
d.type = NUM;
d.u.val = s->u.val;
debugC(1, kPrivateDebugCode, "pushing const %d with name %s", d.u.val, s->name->c_str());
push(d);
return 0;
}
int strpush() { /* push constant onto stack */
Datum d;
d.type = STRING;
Symbol *s = (Symbol *)*g_vm->_pc++;
d.u.str = s->u.str;
debugC(1, kPrivateDebugCode, "pushing const %s with name %s", d.u.str, s->name->c_str());
push(d);
return 0;
}
int varpush() { /* push variable onto stack */
Datum d;
d.type = NAME;
d.u.sym = (Symbol *)(*g_vm->_pc++);
debugC(1, kPrivateDebugCode, "var pushing %s", d.u.sym->name->c_str());
push(d);
return 0;
}
int funcpush() {
Datum s = pop();
Datum n = pop();
ArgArray args;
debugC(1, kPrivateDebugCode, "executing %s with %d params", s.u.str, n.u.val);
for (int i = 0; i < n.u.val; i++) {
Datum arg = pop();
args.insert(args.begin(), arg);
}
call(s.u.str, args);
return 0;
}
/* evaluate variable on stack */
int eval() {
Datum d = pop();
if (d.u.sym->type == NUM) {
d.type = NUM;
d.u.val = d.u.sym->u.val;
debugC(1, kPrivateDebugCode, "eval NUM returned %d", d.u.val);
} else if (d.u.sym->type == STRING) {
d.type = STRING;
d.u.str = d.u.sym->u.str;
debugC(1, kPrivateDebugCode, "eval STR returned %s", d.u.str);
} else if (d.u.sym->type == RECT) {
d.type = RECT;
d.u.rect = d.u.sym->u.rect;
debugC(1, kPrivateDebugCode, "eval RECT");
} else if (d.u.sym->type == NAME) {
debugC(1, kPrivateDebugCode, "eval NAME is noop");
// No evaluation until is absolutely needed
} else
assert(0);
push(d);
return 0;
}
/* add top two elems on stack */
int add() {
Datum d2 = pop();
Datum d1 = pop();
if (d1.type == NAME) {
d1.u.sym = g_private->maps.lookupVariable(d1.u.sym->name);
d1.u.val = d1.u.sym->u.val;
d1.type = NUM;
}
if (d2.type == NAME) {
d2.u.sym = g_private->maps.lookupVariable(d2.u.sym->name);
d2.u.val = d2.u.sym->u.val;
d2.type = NUM;
}
assert(d1.type == NUM);
assert(d2.type == NUM);
debugC(1, kPrivateDebugCode, "adding %d %d\n", d1.u.val, d2.u.val);
d1.u.val += d2.u.val;
push(d1);
return 0;
}
int negate() {
Datum d = pop();
int v = 0;
if (d.type == NAME) {
d.u.sym = g_private->maps.lookupVariable(d.u.sym->name);
v = d.u.sym->u.val;
d.type = NUM;
} else if (d.type == NUM) {
v = d.u.val;
} else
assert(0);
debugC(1, kPrivateDebugCode, "negating %d\n", d.u.val);
d.u.val = !v;
push(d);
return 0;
}
int gt() {
Datum d2 = pop();
Datum d1 = pop();
if (d1.type == NAME) {
//char *name = d1.u.sym->name->c_str();
//debug("eval %s to %d",
d1.u.sym = g_private->maps.lookupVariable(d1.u.sym->name);
d1.u.val = d1.u.sym->u.val;
d1.type = NUM;
}
if (d2.type == NAME) {
//char *name = d1.u.sym->name->c_str();
//debug("eval %s to %d",
d2.u.sym = g_private->maps.lookupVariable(d2.u.sym->name);
d2.u.val = d2.u.sym->u.val;
d2.type = NUM;
}
d1.u.val = (int)(d1.u.val > d2.u.val);
push(d1);
return 0;
}
int lt() {
Datum d2 = pop();
Datum d1 = pop();
if (d1.type == NAME) {
//char *name = d1.u.sym->name->c_str();
//debug("eval %s to %d",
d1.u.sym = g_private->maps.lookupVariable(d1.u.sym->name);
d1.u.val = d1.u.sym->u.val;
d1.type = NUM;
}
if (d2.type == NAME) {
//char *name = d1.u.sym->name->c_str();
//debug("eval %s to %d",
d2.u.sym = g_private->maps.lookupVariable(d2.u.sym->name);
d2.u.val = d2.u.sym->u.val;
d2.type = NUM;
}
d1.u.val = (int)(d1.u.val < d2.u.val);
push(d1);
return 0;
}
int ge() {
Datum d2 = pop();
Datum d1 = pop();
if (d1.type == NAME) {
//char *name = d1.u.sym->name->c_str();
//debug("eval %s to %d",
d1.u.sym = g_private->maps.lookupVariable(d1.u.sym->name);
d1.u.val = d1.u.sym->u.val;
d1.type = NUM;
}
if (d2.type == NAME) {
//char *name = d1.u.sym->name->c_str();
//debug("eval %s to %d",
d2.u.sym = g_private->maps.lookupVariable(d2.u.sym->name);
d2.u.val = d2.u.sym->u.val;
d2.type = NUM;
}
d1.u.val = (int)(d1.u.val >= d2.u.val);
push(d1);
return 0;
}
int le() {
Datum d2 = pop();
Datum d1 = pop();
if (d1.type == NAME) {
//char *name = d1.u.sym->name->c_str();
//debug("eval %s to %d",
d1.u.sym = g_private->maps.lookupVariable(d1.u.sym->name);
d1.u.val = d1.u.sym->u.val;
d1.type = NUM;
}
if (d2.type == NAME) {
//char *name = d1.u.sym->name->c_str();
//debug("eval %s to %d",
d2.u.sym = g_private->maps.lookupVariable(d2.u.sym->name);
d2.u.val = d2.u.sym->u.val;
d2.type = NUM;
}
d1.u.val = (int)(d1.u.val <= d2.u.val);
push(d1);
return 0;
}
int eq() {
Datum d2 = pop();
Datum d1 = pop();
if (d1.type == NAME) {
//char *name = d1.u.sym->name->c_str();
//debug("eval %s to %d",
d1.u.sym = g_private->maps.lookupVariable(d1.u.sym->name);
d1.u.val = d1.u.sym->u.val;
d1.type = NUM;
}
if (d2.type == NAME) {
//char *name = d1.u.sym->name->c_str();
//debug("eval %s to %d",
d2.u.sym = g_private->maps.lookupVariable(d2.u.sym->name);
d2.u.val = d2.u.sym->u.val;
d2.type = NUM;
}
d1.u.val = (int)(d1.u.val == d2.u.val);
push(d1);
return 0;
}
int ne() {
Datum d2 = pop();
Datum d1 = pop();
if (d1.type == NAME) {
d1.u.sym = g_private->maps.lookupVariable(d1.u.sym->name);
d1.u.val = d1.u.sym->u.val;
d1.type = NUM;
}
if (d2.type == NAME) {
d2.u.sym = g_private->maps.lookupVariable(d2.u.sym->name);
d2.u.val = d2.u.sym->u.val;
d2.type = NUM;
}
d1.u.val = (int)(d1.u.val != d2.u.val);
push(d1);
return 0;
}
/* install one instruction or operand */
Inst *code(const Inst &f) {
//debugC(1, kPrivateDebugCode, "pushing code at %x", progp);
Inst *oprogp = g_vm->_progp;
assert(!(g_vm->_progp >= &g_vm->_prog[NPROG]));
*g_vm->_progp++ = f;
return oprogp;
}
int ifcode() {
Inst *savepc = g_vm->_pc; /* then part */
debugC(1, kPrivateDebugCode, "ifcode: evaluating condition");
execute(savepc + 3); /* condition */
Datum d = pop();
debugC(1, kPrivateDebugCode, "ifcode: selecting branch");
if (d.type == NAME) {
debugC(1, kPrivateDebugCode, "name %s", d.u.sym->name->c_str());
d.u.sym = g_private->maps.lookupVariable(d.u.sym->name);
d.u.val = d.u.sym->u.val;
}
if (d.u.val) {
debugC(1, kPrivateDebugCode, "ifcode: true branch");
execute(*((Inst **)(savepc)));
} else if (*((Inst **)(savepc + 1))) { /* else part? */
debugC(1, kPrivateDebugCode, "ifcode: false branch");
execute(*((Inst **)(savepc + 1)));
}
debugC(1, kPrivateDebugCode, "ifcode finished");
g_vm->_pc = *((Inst **)(savepc + 2)); /* next stmt */
return 0;
}
int randbool() {
Datum d = pop();
int v = g_private->getRandomBool(d.u.val);
d.u.val = v;
push(d);
return 0;
}
int fail() {
assert(0);
return 0;
}
/* run the machine */
void execute(Inst *p) {
for (g_vm->_pc = p; *(g_vm->_pc) != STOP;) {
(*(*(g_vm->_pc++)))();
}
}
} // End of namespace Gen
} // End of namespace Private

View File

@@ -0,0 +1,3 @@
# This file is included from the main "configure" script
# add_engine [name] [desc] [build-by-default] [subengines] [base games] [deps] [components]
add_engine private "Private Eye" yes "" "" "highres"

View File

@@ -0,0 +1,3 @@
begin_section("Private");
add_person("Gustavo Grieco", "neuromancer", "");
end_section();

165
engines/private/cursors.cpp Normal file
View File

@@ -0,0 +1,165 @@
/* 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 <http://www.gnu.org/licenses/>.
*
*/
#include "common/rect.h"
#include "graphics/cursorman.h"
#include "common/compression/installshieldv3_archive.h"
#include "common/formats/winexe_ne.h"
#include "common/formats/winexe_pe.h"
#include "common/macresman.h"
#include "graphics/maccursor.h"
#include "graphics/wincursor.h"
#include "private/private.h"
#include "engines/advancedDetector.h"
namespace Private {
struct CursorEntry {
const char *name;
const char *aname;
uint windowsId;
uint japaneseWindowsId;
uint macId;
};
void PrivateEngine::loadCursors() {
const CursorEntry cursorIDReference[] = {
{ "kTurnLeft", "k1", 23, 17, 133 },
{ "kTurnRight", "k2", 9, 3, 132 },
{ "kZoomIn", "k3", 17, 11, 138 },
{ "kZoomOut", "k4", 11, 5, 135 },
{ "kExit", "k5", 7, 1, 130 },
{ "kPhone", "k6", 25, 19, 141 },
{ "kInventory", "k7", 19, 13, 139 }
};
_defaultCursor = Graphics::makeDefaultWinCursor();
if (_platform == Common::kPlatformWindows) {
Common::WinResources *exe = nullptr;
Common::SeekableReadStream *exeStream = nullptr;
Common::ArchiveMemberList members;
Common::InstallShieldV3 installerArchive;
if (installerArchive.open("SUPPORT/PVTEYE.Z")) {
const char *exeNames[] = {
"PVTEYE.EXE",
"PvteyeJ.EXE", // Japan
"PVTDEMO.EXE"
};
for (uint i = 0; i < ARRAYSIZE(exeNames) && exeStream == nullptr; i++) {
exeStream = installerArchive.createReadStreamForMember(exeNames[i]);
}
if (exeStream == nullptr) {
error("Executable not found in PVTEYE.Z");
}
} else {
Common::File *file = new Common::File();
if (!file->open("SUPPORT/PVTEYE.EX_")) {
error("PVTEYE.EX_ not found");
}
exeStream = file;
}
exe = Common::WinResources::createFromEXE(exeStream);
if (exe == nullptr) {
error("Executable not found");
}
const Common::Array<Common::WinResourceID> cursorIDs = exe->getIDList(Common::kWinGroupCursor);
_cursors.resize(cursorIDs.size());
assert(cursorIDs.size() > 0);
for (uint i = 0; i < cursorIDs.size(); i++) {
_cursors[i].winCursorGroup = Graphics::WinCursorGroup::createCursorGroup(exe, cursorIDs[i]);
_cursors[i].cursor = _cursors[i].winCursorGroup->cursors[0].cursor;
for (uint j = 0; j < ARRAYSIZE(cursorIDReference); j++) {
const CursorEntry &entry = cursorIDReference[j];
uint entryId = (_language == Common::JA_JPN) ? entry.japaneseWindowsId : entry.windowsId;
if (entryId == _cursors[i].winCursorGroup->cursors[0].id.getID()) {
_cursors[i].name = entry.name;
_cursors[i].aname = entry.aname;
break;
}
}
}
delete exe;
delete exeStream;
} else {
Common::MacResManager resMan;
const char *executableFilePath = isDemo() ? "SUPPORT/Private Eye Demo" : "SUPPORT/Private Eye";
const char *executableInstallerPath = isDemo() ? "Private Eye Demo" : "Private Eye";
Common::ScopedPtr<Common::Archive> macInstaller(loadMacInstaller());
if (resMan.open(executableFilePath) || (macInstaller && resMan.open(executableInstallerPath, *macInstaller))) {
const Common::MacResIDArray cursorResIDs = resMan.getResIDArray(MKTAG('C', 'U', 'R', 'S'));
_cursors.resize(cursorResIDs.size());
for (uint i = 0; i < cursorResIDs.size(); i++) {
Common::SharedPtr<Common::SeekableReadStream> resData(resMan.getResource(MKTAG('C', 'U', 'R', 'S'), cursorResIDs[i]));
Graphics::MacCursor *cursor = new Graphics::MacCursor();
cursor->readFromStream(*resData);
_cursors[i].cursor = cursor;
_cursors[i].winCursorGroup = nullptr;
for (uint j = 0; j < ARRAYSIZE(cursorIDReference); j++) {
const CursorEntry &entry = cursorIDReference[j];
if (entry.macId == cursorResIDs[i]) {
_cursors[i].name = entry.name;
_cursors[i].aname = entry.aname;
break;
}
}
}
}
}
}
void PrivateEngine::changeCursor(const Common::String &cursor) {
if (_currentCursor == cursor) {
return;
}
if (cursor == "default") {
CursorMan.replaceCursor(_defaultCursor);
} else {
for (uint i = 0; i < _cursors.size(); i++) {
if (_cursors[i].name == cursor || _cursors[i].aname == cursor) {
CursorMan.replaceCursor(_cursors[i].cursor);
break;
}
}
}
_currentCursor = cursor;
CursorMan.showMouse(true);
}
} // End of namespace Private

View File

@@ -0,0 +1,179 @@
/* 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 <http://www.gnu.org/licenses/>.
*
*/
// Heavily based on code by jdieguez
#include "private/decompiler.h"
namespace Private {
const char *kHeader = "Precompiled Game Matrix";
const uint kHeaderSize = 23;
const byte kCodeString = 0x01;
const byte kCodeShortLiteral = 0x02;
const byte kCodeBraceClose = 0x04;
const byte kCodeRect = 0x2e;
const byte kCodeRects = 0x4f;
const byte kCodeShortId = 0x50;
const static char *kCodeTable[] = {"", //
"", // 0x01 (string)
"", // 0x02 (short literal)
" {\n", // 0x03
"}\n", // 0x04
"(", // 0x05
")", // 0x06
"", //
"", //
"", //
",", // 0x0a
"", //
"%", // 0x0c
"", //
";\n", // 0x0e
"!", // 0x0f
"-", // 0x10
"+", // 0x11
"=", // 0x12
">", // 0x13
"<", // 0x14
"if ", // 0x15
"else ", // 0x16
"Exit", // 0x17
"goto ", // 0x18
"Mask", // 0x19
"MaskDrawn", // 0x1a
"Movie", // 0x1b
"Transition", // 0x1c
"ThumbnailMovie", // 0x1d
"BustMovie", // 0x1e
"ViewScreen", // 0x1f
"VSPicture", // 0x20
"Bitmap", // 0x21
"Timer", // 0x22
"SoundArea", // 0x23
"Sound", // 0x24
"SoundEffect", // 0x25
"SyncSound", // 0x26
"LoopedSound", // 0x27
"NoStopSounds", // 0x28
"Resume", // 0x29
"Inventory", // 0x2a
"SetFlag", // 0x2b
"ChgMode", // 0x2c
"PoliceBust", // 0x2d
"CRect", // 0x2e overridden with "RECT" if in "define rects" block
"", //
"Random", // 0x30
"SafeDigit", // 0x31
"LoseInventory", // 0x32
"", //
"PaperShuffleSound", // 0x34
"Quit", // 0x35
"DossierAdd", // 0x36
"DossierBitmap", // 0x37
"DossierPrevSuspect", // 0x38
"DossierNextSuspect", // 0x39
"DossierChgSheet", // 0x3a
"DiaryLocList", // 0x3b
"DiaryPage", // 0x3c
"DiaryInvList", // 0x3d
"DiaryPageTurn", // 0x3e
"DiaryGoLoc", // 0x3f
"SaveGame", // 0x40
"LoadGame", // 0x41
"RestartGame", // 0x42
"AskSave", // 0x43
"SetModifiedFlag", // 0x44
"PhoneClip", // 0x45
"PoliceClip", // 0x46
"AMRadioClip", // 0x47
"\nsetting ", // 0x48
"debug ", // 0x49
"\ndefine ", // 0x4a
"", //
"variables", // 0x4c
"", //
"", //
"rects", // 0x4f
""}; // 0x50 (short id)
Decompiler::Decompiler(char *buf, uint32 fileSize, bool mac) {
Common::Array<byte> array;
uint32 i = 0;
while (i < fileSize) {
array.push_back(buf[i]);
i++;
}
Common::String firstBytes((const char *)array.begin(), (const char *)array.begin() + kHeaderSize);
if (firstBytes != kHeader) {
_result = Common::String(buf);
return;
}
decompile(array, mac);
}
void Decompiler::decompile(Common::Array<byte> &buffer, bool mac) {
Common::Array<byte>::iterator it = buffer.begin();
Common::String ss;
bool inDefineRects = false;
for (it += kHeaderSize; it != buffer.end();) {
byte b = *it++;
if (b == kCodeString) {
byte len = *it++;
Common::String s((const char *)it, (const char *)it + len);
it += len;
ss += Common::String::format("\"%s\"", s.c_str());
} else if (b == kCodeShortLiteral || b == kCodeShortId) {
byte b1 = *it++;
byte b2 = *it++;
uint number = mac ? b2 + (b1 << 8) : b1 + (b2 << 8);
if (b == kCodeShortId)
ss += "k";
ss += Common::String::format("%d", number);
} else if (b == kCodeRect && inDefineRects) {
ss += "RECT"; // override CRect
} else if (b <= kCodeShortId && strlen(kCodeTable[b]) > 0) {
ss += kCodeTable[b];
} else {
error("decompile(): Unknown byte code (%d %c)", b, b);
}
if (b == kCodeRects) {
inDefineRects = true;
} else if (b == kCodeBraceClose && inDefineRects) {
inDefineRects = false;
}
}
ss += "\ndefine locations { k0, k1, k2, k3, k4, k5, k6, k7, k8, k9, k10, k11, k12, k13, k14 }";
_result = ss;
}
Common::String Decompiler::getResult() const {
return _result;
}
} // namespace Private

View File

@@ -0,0 +1,44 @@
/* 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 <http://www.gnu.org/licenses/>.
*
*/
// Heavily based on code by jdieguez
#ifndef PRIVATE_DECOMPILER_H
#define PRIVATE_DECOMPILER_H
#include "common/array.h"
#include "common/debug.h"
#include "common/str.h"
namespace Private {
class Decompiler {
public:
Decompiler(char *buf, uint32 fileSize, bool mac = false);
Common::String getResult() const;
private:
void decompile(Common::Array<byte> &buffer, bool mac);
Common::String _result;
};
} // namespace Private
#endif

View File

@@ -0,0 +1,284 @@
/* 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 <http://www.gnu.org/licenses/>.
*
*/
#include "base/plugins.h"
#include "common/translation.h"
#include "engines/advancedDetector.h"
#include "private/private.h"
#include "private/detection.h"
static const DebugChannelDef debugFlagList[] = {
{Private::kPrivateDebugFunction, "function", "Function execution debug channel"},
{Private::kPrivateDebugCode, "code", "Code execution debug channel"},
{Private::kPrivateDebugScript, "script", "Script execution debug channel"},
DEBUG_CHANNEL_END
};
namespace Private {
static const PlainGameDescriptor privateGames[] = {
{ "private-eye", "Private Eye" },
{ nullptr, nullptr }
};
static const ADGameDescription gameDescriptions[] = {
{
"private-eye", // US release
nullptr,
AD_ENTRY2s("pvteye.z", "b682118cda6a42fa89833cae2b8824bd", 271895,
"intro.smk", "270a1d0a29df122fc3d1d38e655161a7", 7310984),
Common::EN_USA,
Common::kPlatformWindows,
ADGF_NO_FLAGS,
GUIO3(GUIO_NOMIDI, GAMEOPTION_SFX_SUBTITLES, GAMEOPTION_HIGHLIGHT_MASKS)
},
{
"private-eye", // Demo from the US release v1.0.0.23
"Demo",
AD_ENTRY2s("pvteye.z", "af383c813157810e89d8d6d595895ff7", 263893,
"bklynlgo.bmp", "1dfb703349a46f8ec183de107992b7f5", 33118),
Common::EN_USA,
Common::kPlatformWindows,
ADGF_DEMO,
GUIO3(GUIO_NOMIDI, GAMEOPTION_SFX_SUBTITLES, GAMEOPTION_HIGHLIGHT_MASKS)
},
{
"private-eye", // EU release (UK)
nullptr,
AD_ENTRY2s("pvteye.z", "d9ce391395701615e8b5d04bc4bf7ec3", 284699,
"bklynlgo.bmp", "1dfb703349a46f8ec183de107992b7f5", 33118),
Common::EN_GRB,
Common::kPlatformWindows,
ADGF_NO_FLAGS,
GUIO3(GUIO_NOMIDI, GAMEOPTION_SFX_SUBTITLES, GAMEOPTION_HIGHLIGHT_MASKS)
},
{
"private-eye", // Demo from the EU release
"Demo",
AD_ENTRY2s("pvteye.z", "01ca8641970189cb2ca3a96526026091", 284129,
"bklynlgo.bmp", "1dfb703349a46f8ec183de107992b7f5", 33118),
Common::EN_GRB,
Common::kPlatformWindows,
ADGF_DEMO,
GUIO3(GUIO_NOMIDI, GAMEOPTION_SFX_SUBTITLES, GAMEOPTION_HIGHLIGHT_MASKS)
},
{
"private-eye", // Demo from PCGamer Disc 2.6 JULY 1996 v1.0.0.12
"Demo",
AD_ENTRY2s("pvteye.z", "8ef908e212bb9c1e10f5e3c81f56682c", 263893,
"bklynlgo.bmp", "1dfb703349a46f8ec183de107992b7f5", 33118),
Common::EN_USA,
Common::kPlatformWindows,
ADGF_DEMO,
GUIO3(GUIO_NOMIDI, GAMEOPTION_SFX_SUBTITLES, GAMEOPTION_HIGHLIGHT_MASKS)
},
{
"private-eye", // Another demo
"Demo",
AD_ENTRY2s("pvteye.z", "af383c813157810e89d8d6d595895ff7", 271214,
"bklynlgo.bmp", "1dfb703349a46f8ec183de107992b7f5", 33118),
Common::EN_USA,
Common::kPlatformWindows,
ADGF_DEMO,
GUIO3(GUIO_NOMIDI, GAMEOPTION_SFX_SUBTITLES, GAMEOPTION_HIGHLIGHT_MASKS)
},
{
"private-eye", // EU release (ES)
nullptr,
AD_ENTRY2s("pvteye.ex_", "f41770550ab717086b2d0c805fef4b8f", 498176,
"bklynlgo.bmp", "1dfb703349a46f8ec183de107992b7f5", 33118),
Common::ES_ESP,
Common::kPlatformWindows,
ADGF_NO_FLAGS,
GUIO3(GUIO_NOMIDI, GAMEOPTION_SFX_SUBTITLES, GAMEOPTION_HIGHLIGHT_MASKS)
},
{
"private-eye", // Demo from the EU release (ES)
"Demo",
AD_ENTRY2s("pvtdemo.ex_", "048f751acd7a0f1a87b20d6dc5229210", 497152,
"bklynlgo.bmp", "1dfb703349a46f8ec183de107992b7f5", 33118),
Common::ES_ESP,
Common::kPlatformWindows,
ADGF_DEMO,
GUIO3(GUIO_NOMIDI, GAMEOPTION_SFX_SUBTITLES, GAMEOPTION_HIGHLIGHT_MASKS)
},
{
"private-eye", // EU release (FR)
nullptr,
AD_ENTRY2s("pvteye.ex_", "ae0dec43b2f54d45c8a1c93e97092141", 600576,
"bklynlgo.bmp", "1dfb703349a46f8ec183de107992b7f5", 33118),
Common::FR_FRA,
Common::kPlatformWindows,
ADGF_NO_FLAGS,
GUIO2(GUIO_NOMIDI, GAMEOPTION_HIGHLIGHT_MASKS)
},
{
"private-eye", // EU release (DE)
nullptr,
AD_ENTRY2s("pvteye.ex_", "5ca171c4e8d804c7277887277d049f03", 600576,
"bklynlgo.bmp", "1dfb703349a46f8ec183de107992b7f5", 33118),
Common::DE_DEU,
Common::kPlatformWindows,
ADGF_NO_FLAGS,
GUIO2(GUIO_NOMIDI, GAMEOPTION_HIGHLIGHT_MASKS)
},
{
"private-eye", // Promotional demo disc
"Demo",
AD_ENTRY2s("pvteye.z", "adb2ceca453da546d5e86baad0c73cd1", 262537,
"bklynlgo.bmp", "1dfb703349a46f8ec183de107992b7f5", 33118),
Common::EN_USA,
Common::kPlatformWindows,
ADGF_DEMO,
GUIO2(GUIO_NOMIDI, GAMEOPTION_HIGHLIGHT_MASKS)
},
{
"private-eye", // Demo from the EU release (DE)
"Demo",
AD_ENTRY2s("pvtdemo.ex_", "17156cbac7d14b08f4e351ac0e16a889", 599040,
"bklynlgo.bmp", "1dfb703349a46f8ec183de107992b7f5", 33118),
Common::DE_DEU,
Common::kPlatformWindows,
ADGF_DEMO,
GUIO2(GUIO_NOMIDI, GAMEOPTION_HIGHLIGHT_MASKS)
},
{
"private-eye", // Demo from the EU release (FR)
"Demo",
AD_ENTRY2s("pvtdemo.ex_", "ad2156a762b3e376fda1b791a9491ea8", 599040,
"bklynlgo.bmp", "1dfb703349a46f8ec183de107992b7f5", 33118),
Common::FR_FRA,
Common::kPlatformWindows,
ADGF_DEMO,
GUIO2(GUIO_NOMIDI, GAMEOPTION_HIGHLIGHT_MASKS)
},
{
"private-eye", // RU release
nullptr,
AD_ENTRY2s("pvteye.z", "b682118cda6a42fa89833cae2b8824bd", 271895,
"intro.smk", "61cc13c9e4e2affd574087209df5c4a4", 7241368),
Common::RU_RUS,
Common::kPlatformWindows,
ADGF_NO_FLAGS,
GUIO2(GUIO_NOMIDI, GAMEOPTION_HIGHLIGHT_MASKS)
},
{
"private-eye", // KO release
nullptr,
AD_ENTRY2s("pvteye.z", "b682118cda6a42fa89833cae2b8824bd", 271895,
"intro.smk", "288f5f61311eb10fc6861707c340c15e", 7195200),
Common::KO_KOR,
Common::kPlatformWindows,
ADGF_NO_FLAGS,
GUIO2(GUIO_NOMIDI, GAMEOPTION_HIGHLIGHT_MASKS)
},
{
"private-eye", // JP release
nullptr,
AD_ENTRY2s("pvteye.z", "f5fb6e89e98294a09ff21e72282b58dc", 239066,
"intro.smk", "b8d15a522f896ccbe4539394b6b8ec8a", 7202836),
Common::JA_JPN,
Common::kPlatformWindows,
ADGF_NO_FLAGS,
GUIO2(GUIO_NOMIDI, GAMEOPTION_HIGHLIGHT_MASKS)
},
{
"private-eye", // MacOS release (US)
nullptr,
AD_ENTRY2s("game.mac", "d:33553cc04813d3f658bbe9d548377878", 81894,
"bklynlgo.bmp", "d:1dfb703349a46f8ec183de107992b7f5", 33118),
Common::EN_USA,
Common::kPlatformMacintosh,
ADGF_NO_FLAGS,
GUIO3(GUIO_NOMIDI, GAMEOPTION_SFX_SUBTITLES, GAMEOPTION_HIGHLIGHT_MASKS)
},
{
"private-eye", // MacOS release (US) uninstalled
0,
AD_ENTRY1s("Private Eye Installer", "d:02533427ebdf26d5dd12cee8e9f4de4d", 1647309),
Common::EN_USA,
Common::kPlatformMacintosh,
ADGF_NO_FLAGS,
GUIO1(GUIO_NOMIDI)
},
{
"private-eye", // MacOS release (JP) uninstalled
0,
AD_ENTRY1s("xn--16jc8na7ay6a0eyg9e5nud0e4525d", "d:113b57e2f6bdaf1146fe83fe0f992891", 1477309),
Common::JA_JPN,
Common::kPlatformMacintosh,
ADGF_NO_FLAGS,
GUIO1(GUIO_NOMIDI)
},
{
"private-eye", // MacOS demo (US)
"Demo",
AD_ENTRY2s("demogame.mac", "d:cfbceaa8b91f0f53c745db61d1bc9749", 6103,
"bklynlgo.bmp", "d:1dfb703349a46f8ec183de107992b7f5", 33118),
Common::EN_USA,
Common::kPlatformMacintosh,
ADGF_DEMO,
GUIO3(GUIO_NOMIDI, GAMEOPTION_SFX_SUBTITLES, GAMEOPTION_HIGHLIGHT_MASKS)
},
{
"private-eye", // MacOS demo (US) uninstalled
0,
AD_ENTRY1s("Private Eye Demo Installer", "d:e7665ddc5e6d932c4a65598ecc4ec7d2", 1626393),
Common::EN_USA,
Common::kPlatformMacintosh,
ADGF_DEMO,
GUIO1(GUIO_NOMIDI)
},
AD_TABLE_END_MARKER
};
} // End of namespace Private
static const char *const directoryGlobs[] = {
"support",
"intro",
nullptr
};
class PrivateMetaEngineDetection : public AdvancedMetaEngineDetection<ADGameDescription> {
public:
PrivateMetaEngineDetection() : AdvancedMetaEngineDetection(Private::gameDescriptions, Private::privateGames) {
_maxScanDepth = 2;
_directoryGlobs = directoryGlobs;
}
const char *getName() const override {
return "private";
}
const char *getEngineName() const override {
return "Private Eye";
}
const char *getOriginalCopyright() const override {
return "Copyright (C) Brooklyn Multimedia";
}
const DebugChannelDef *getDebugChannels() const override {
return debugFlagList;
}
};
REGISTER_PLUGIN_STATIC(PRIVATE_DETECTION, PLUGIN_TYPE_ENGINE_DETECTION, PrivateMetaEngineDetection);

View File

@@ -0,0 +1,7 @@
#ifndef PRIVATE_DETECTION_H
#define PRIVATE_DETECTION_H
#define GAMEOPTION_SFX_SUBTITLES GUIO_GAMEOPTIONS1
#define GAMEOPTION_HIGHLIGHT_MASKS GUIO_GAMEOPTIONS2
#endif // PRIVATE_DETECTION_H

903
engines/private/funcs.cpp Normal file
View File

@@ -0,0 +1,903 @@
/* 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 <http://www.gnu.org/licenses/>.
*
*/
#include "common/str.h"
#include "common/system.h"
#include "common/timer.h"
#include "private/grammar.h"
#include "private/private.h"
#include "private/tokens.h"
namespace Private {
static void fChgMode(ArgArray args) {
// assert types
assert(args.size() == 2 || args.size() == 3);
assert(args[0].type == NUM);
if (args.size() == 2)
debugC(1, kPrivateDebugScript, "ChgMode(%d, %s)", args[0].u.val, args[1].u.sym->name->c_str());
else if (args.size() == 3)
debugC(1, kPrivateDebugScript, "ChgMode(%d, %s, %s)", args[0].u.val, args[1].u.sym->name->c_str(), args[2].u.sym->name->c_str());
else
assert(0);
g_private->_mode = args[0].u.val;
g_private->_nextSetting = args[1].u.sym->name->c_str();
if (g_private->_mode == 0) {
g_private->_origin = Common::Point(kOriginZero[0], kOriginZero[1]);
} else if (g_private->_mode == 1) {
g_private->_origin = Common::Point(kOriginOne[0], kOriginOne[1]);
} else
assert(0);
if (args.size() == 3) {
Symbol *location = g_private->maps.lookupLocation(args[2].u.sym->name);
g_private->setLocationAsVisited(location);
// set a game flag when visiting the police station.
if (!g_private->isDemo()) {
if (*(args[2].u.sym->name) == g_private->getPoliceStationLocation()) {
Common::String beenDowntownName = g_private->getBeenDowntownVariable();
Symbol *beenDowntown = g_private->maps.lookupVariable(&beenDowntownName);
setSymbol(beenDowntown, 1);
}
}
}
if (g_private->_mode == 0) {
// This is the only place where this should be used
if (g_private->_noStopSounds) {
g_private->_noStopSounds = false;
} else {
g_private->stopSounds();
}
}
}
static void fVSPicture(ArgArray args) {
assert(args[0].type == STRING);
debugC(1, kPrivateDebugScript, "VSPicture(%s)", args[0].u.str);
g_private->_nextVS = args[0].u.str;
}
static void fDiaryLocList(ArgArray args) {
int x1, y1, x2, y2;
assert(args[0].type == NUM);
assert(args[1].type == NUM);
assert(args[2].type == NUM);
assert(args[3].type == NUM);
g_private->_currentDiaryPage = -1;
debugC(1, kPrivateDebugScript, "DiaryLocList(%d, %d, %d, %d)", args[0].u.val, args[1].u.val, args[2].u.val, args[3].u.val);
x2 = args[0].u.val;
y2 = args[1].u.val;
x1 = args[2].u.val;
y1 = args[3].u.val;
Common::Rect rect(x1, y1, x2, y2);
g_private->loadLocations(rect);
}
static void fDiaryGoLoc(ArgArray args) {
debugC(1, kPrivateDebugScript, "DiaryGoLoc(%d, ..)", args[0].u.val);
ExitInfo e;
e.rect = *args[1].u.rect;
e.nextSetting = g_private->getDiaryMiddleSetting();
if (args[0].u.val) {
e.cursor = "kTurnRight";
g_private->_diaryNextPageExit = e;
} else {
e.cursor = "kTurnLeft";
g_private->_diaryPrevPageExit = e;
}
g_private->_exits.push_front(e);
}
static void fDiaryPageTurn(ArgArray args) {
debugC(1, kPrivateDebugScript, "DiaryPageTurn(%d, ..)", args[0].u.val);
ExitInfo e;
e.nextSetting = g_private->getDiaryMiddleSetting();
e.rect = *args[1].u.rect;
if (args[0].u.val == 1) {
e.cursor = "kTurnRight";
if (g_private->_currentDiaryPage >= (int)g_private->_diaryPages.size() - 1) {
e.nextSetting = g_private->getDiaryLastPageSetting();
}
g_private->_diaryNextPageExit = e;
} else {
e.cursor = "kTurnLeft";
if (g_private->_currentDiaryPage <= 0) {
e.nextSetting = g_private->getDiaryTOCSetting();
}
g_private->_diaryPrevPageExit = e;
}
g_private->_exits.push_front(e);
}
static void fDiaryPage(ArgArray args) {
debugC(1, kPrivateDebugScript, "DiaryPage(%d, %d, %d, %d, ..)", args[0].u.rect->left, args[0].u.rect->top, args[0].u.rect->right, args[0].u.rect->bottom);
g_private->loadMemories(*args[0].u.rect, args[1].u.val, args[2].u.val);
}
static void fDiaryInvList(ArgArray args) {
debugC(1, kPrivateDebugScript, "DiaryInvList(%d, ..)", args[0].u.val);
g_private->_currentDiaryPage = g_private->_diaryPages.size();
const Common::Rect *r1 = args[1].u.rect;
const Common::Rect *r2 = args[2].u.rect;
g_private->loadInventory(args[0].u.val, *r1, *r2);
}
static void fgoto(ArgArray args) {
// assert types
debugC(1, kPrivateDebugScript, "goto(%s)", args[0].u.str);
g_private->_nextSetting = args[0].u.str;
}
static void fSyncSound(ArgArray args) {
assert(args[0].type == STRING);
assert(args[1].type == NAME);
debugC(1, kPrivateDebugScript, "SyncSound(%s, %s)", args[0].u.str, args[1].u.sym->name->c_str());
g_private->_nextSetting = args[1].u.sym->name->c_str();
Common::String s = args[0].u.str;
if (s != "\"\"") {
g_private->drawScreen();
g_private->stopSounds();
g_private->playForegroundSound(s);
g_private->waitForSoundsToStop();
}
}
static void fQuit(ArgArray args) {
debugC(1, kPrivateDebugScript, "Quit()");
g_private->quitGame();
}
static void fLoadGame(ArgArray args) {
assert(args[0].type == STRING);
assert(args[2].type == NAME);
debugC(1, kPrivateDebugScript, "LoadGame(%s, %s)", args[0].u.str, args[2].u.sym->name->c_str());
MaskInfo m;
if (strcmp(args[0].u.str, "\"\"") == 0) // Not sure why the game tries to load an empty mask
return;
m.surf = g_private->loadMask(args[0].u.str, 0, 0, true);
m.cursor = *args[2].u.sym->name;
m.nextSetting = "";
m.flag1 = nullptr;
m.flag2 = nullptr;
g_private->_loadGameMask = m;
g_private->_masks.push_front(m);
}
static void fSaveGame(ArgArray args) {
// assert types
debugC(1, kPrivateDebugScript, "SaveGame(%s, %s)", args[0].u.str, args[1].u.sym->name->c_str());
MaskInfo m;
m.surf = g_private->loadMask(args[0].u.str, 0, 0, true);
m.cursor = *args[1].u.sym->name;
m.nextSetting = "";
m.flag1 = nullptr;
m.flag2 = nullptr;
g_private->_saveGameMask = m;
g_private->_masks.push_front(m);
}
static void fRestartGame(ArgArray args) {
assert(args.size() == 0);
g_private->restartGame();
}
static void fPoliceBust(ArgArray args) {
// assert types
assert(args.size() == 1 || args.size() == 2);
int mode = (args.size() == 2) ? args[1].u.val : 0;
debugC(1, kPrivateDebugScript, "PoliceBust(%d, %d)", args[0].u.val, mode);
if (mode == 3) {
g_private->completePoliceBust();
return;
}
if (mode == 2) {
g_private->wallSafeAlarm();
return;
}
if (mode == 1) {
// Not implemented: a special mode for police busts
// in Marlowe's office that was removed from the game.
return;
}
if (args[0].u.val) {
g_private->startPoliceBust();
} else {
g_private->stopPoliceBust();
}
}
static void fBustMovie(ArgArray args) {
// assert types
assert(args.size() == 1);
debugC(1, kPrivateDebugScript, "BustMovie(%s)", args[0].u.sym->name->c_str());
g_private->_nextMovie = g_private->_policeBustMovie;
g_private->_nextSetting = args[0].u.sym->name->c_str();
Common::String memoryPath = g_private->_policeBustMovie;
memoryPath.replace('/', '\\');
g_private->addMemory(memoryPath);
}
static void fDossierAdd(ArgArray args) {
assert(args.size() == 2);
Common::String s1 = args[0].u.str;
Common::String s2 = args[1].u.str;
if (s2 == "\"\"") {
s2 = "";
}
g_private->addDossier(s1, s2);
}
static void fDossierBitmap(ArgArray args) {
assert(args.size() == 2);
int x = args[0].u.val;
int y = args[1].u.val;
assert(x == 40 && y == 30);
g_private->loadDossier();
}
static void fDossierChgSheet(ArgArray args) {
assert(args.size() == 4);
debugC(1, kPrivateDebugScript, "DossierChgSheet(%s,%d,%d,%d)", args[0].u.str, args[1].u.val, args[2].u.val, args[3].u.val);
Common::String s(args[0].u.str);
MaskInfo m;
// do nothing if suspect only has one sheet
if (g_private->_dossiers[g_private->_dossierSuspect].page2.empty()) {
return;
}
int p = args[1].u.val;
int x = args[2].u.val;
int y = args[3].u.val;
m.surf = g_private->loadMask(s, x, y, true);
m.cursor = g_private->getExitCursor();
m.nextSetting = "";
m.flag1 = nullptr;
m.flag2 = nullptr;
if (p == 0)
g_private->_dossierPrevSheetMask = m;
else if (p == 1)
g_private->_dossierNextSheetMask = m;
else
error("Invalid sheet number in DossierChgSheet %d", p);
g_private->_masks.push_front(m);
}
static void fDossierPrevSuspect(ArgArray args) {
assert(args.size() == 3);
Common::String s(args[0].u.str);
MaskInfo m;
if (g_private->_dossierSuspect == 0) {
return;
}
int x = args[1].u.val;
int y = args[2].u.val;
m.surf = g_private->loadMask(s, x, y, true);
m.cursor = g_private->getExitCursor();
m.nextSetting = "";
m.flag1 = nullptr;
m.flag2 = nullptr;
g_private->_dossierPrevSuspectMask = m;
g_private->_masks.push_front(m);
}
static void fDossierNextSuspect(ArgArray args) {
assert(args.size() == 3);
Common::String s(args[0].u.str);
MaskInfo m;
if ((g_private->_dossierSuspect + 1) >= g_private->_dossiers.size()) {
return;
}
int x = args[1].u.val;
int y = args[2].u.val;
m.surf = g_private->loadMask(s, x, y, true);
m.cursor = g_private->getExitCursor();
m.nextSetting = "";
m.flag1 = nullptr;
m.flag2 = nullptr;
g_private->_dossierNextSuspectMask = m;
g_private->_masks.push_front(m);
}
static void fNoStopSounds(ArgArray args) {
assert(args.size() == 0);
debugC(1, kPrivateDebugScript, "NoStopSounds()");
g_private->_noStopSounds = true;
}
static void fLoseInventory(ArgArray args) {
assert(args.size() == 0);
debugC(1, kPrivateDebugScript, "LoseInventory()");
g_private->removeRandomInventory();
}
static void fInventory(ArgArray args) {
// assert types
Datum b1 = args[0];
Datum v1 = args[1];
Datum v2 = args[2];
Datum e = args[3];
Datum i = args[4];
Datum c = args[5];
Datum snd;
if (args.size() >= 9)
snd = args[8];
else {
snd.type = STRING;
snd.u.str = "\"\"";
}
assert(v1.type == STRING || v1.type == NAME);
assert(b1.type == STRING);
assert(e.type == NAME || e.type == NUM);
assert(snd.type == STRING);
assert(i.type == STRING);
Common::String bmp(i.u.str);
assert(g_private->isDemo() || bmp != "\"\"");
if (v1.type == STRING)
assert(strcmp(v1.u.str, "\"\"") == 0);
debugC(1, kPrivateDebugScript, "Inventory(...)");
Common::String mask(b1.u.str);
if (mask != "\"\"") {
if (bmp != "\"\"" && g_private->inInventory(bmp)) {
return;
}
MaskInfo m;
m.surf = g_private->loadMask(mask, 0, 0, true);
if (e.type == NUM) {
assert(e.u.val == 0);
m.nextSetting = "";
} else
m.nextSetting = e.u.sym->name->c_str();
m.cursor = g_private->getInventoryCursor();
m.point = Common::Point(0, 0);
if (v1.type == NAME) {
m.flag1 = g_private->maps.lookupVariable(v1.u.sym->name);
} else
m.flag1 = nullptr;
if (v2.type == NAME) {
m.flag2 = g_private->maps.lookupVariable(v2.u.sym->name);
} else
m.flag2 = nullptr;
m.inventoryItem = bmp;
g_private->_masks.push_front(m);
g_private->_toTake = true;
Common::String sound(snd.u.str);
if (sound == "\"\"") {
sound = g_private->getTakeLeaveSound();
}
g_private->playForegroundSound(g_private->_takeLeaveSound, sound);
} else {
Common::String flag;
if (v1.type == NAME) {
if (strcmp(c.u.str, "\"REMOVE\"") == 0) {
g_private->removeInventory(bmp);
} else {
flag = *(v1.u.sym->name);
g_private->addInventory(bmp, flag);
}
} else {
g_private->addInventory(bmp, flag);
}
if (v2.type == NAME) {
v2.u.sym = g_private->maps.lookupVariable(v2.u.sym->name);
v2.u.sym->u.val = 1;
}
}
}
static void fSetFlag(ArgArray args) {
assert(args.size() == 2);
assert(args[0].type == NAME && args[1].type == NUM);
debugC(1, kPrivateDebugScript, "SetFlag(%s, %d)", args[0].u.sym->name->c_str(), args[1].u.val);
args[0].u.sym = g_private->maps.lookupVariable(args[0].u.sym->name);
args[0].u.sym->u.val = args[1].u.val;
}
static void fExit(ArgArray args) {
// assert types
assert(args[2].type == RECT || args[2].type == NAME);
debugC(1, kPrivateDebugScript, "Exit(%d %d %d)", args[0].type, args[1].type, args[2].type); //, args[0].u.str, args[1].u.sym->name->c_str(), "RECT");
ExitInfo e;
if (args[0].type == NUM && args[0].u.val == 0)
e.nextSetting = "";
else
e.nextSetting = args[0].u.sym->name->c_str();
if (args[1].type == NUM && args[1].u.val == 0)
e.cursor = "";
else
e.cursor = *args[1].u.sym->name;
if (args[2].type == NAME) {
Symbol *rect = g_private->maps.lookupRect(args[2].u.sym->name);
assert(rect->type == RECT);
args[2].u.rect = rect->u.rect;
}
e.rect = *args[2].u.rect;
g_private->_exits.push_front(e);
}
static void fSetModifiedFlag(ArgArray args) {
// assert types
debugC(1, kPrivateDebugScript, "SetModifiedFlag(%d)", args[0].u.val);
g_private->_modified = args[0].u.val != 0;
}
static void fPaperShuffleSound(ArgArray args) {
assert(args.size() == 0);
debugC(1, kPrivateDebugScript, "PaperShuffleSound()");
g_private->playForegroundSound(g_private->getPaperShuffleSound());
}
static void fSoundEffect(ArgArray args) {
// assert types
debugC(1, kPrivateDebugScript, "SoundEffect(%s)", args[0].u.str);
Common::String s(args[0].u.str);
if (s != "\"\"") {
g_private->playForegroundSound(s);
} else {
g_private->stopSounds();
}
}
static void fSound(ArgArray args) {
// assert types
debugC(1, kPrivateDebugScript, "Sound(%s)", args[0].u.str);
if (args.size() == 4) {
bool b1 = args[1].u.val != 0;
bool b2 = args[2].u.val != 0;
int c = args[3].u.val;
if (!b1 && !b2 && c == 1) {
g_private->stopSounds();
} else if (!b1 && !b2 && c == 2) {
g_private->stopForegroundSounds();
} else
assert(0);
}
Common::String s(args[0].u.str);
if (s != "\"\"") {
g_private->playForegroundSound(s);
} else {
g_private->stopSounds();
}
}
static void fLoopedSound(ArgArray args) {
// assert types
assert(args.size() == 1);
debugC(1, kPrivateDebugScript, "LoopedSound(%s)", args[0].u.str);
Common::String s(args[0].u.str);
if (s != "\"\"") {
g_private->playBackgroundSound(s);
} else {
g_private->stopSounds();
}
}
static void fViewScreen(ArgArray args) {
// assert types
debugC(1, kPrivateDebugScript, "WARNING: ViewScreen not implemented!");
}
static void fTransition(ArgArray args) {
assert(args[0].type == STRING);
assert(args[1].type == NAME);
debugC(1, kPrivateDebugScript, "Transition(%s, %s)", args[0].u.str, args[1].u.sym->name->c_str());
g_private->_nextMovie = args[0].u.str;
g_private->_nextSetting = args[1].u.sym->name->c_str();
}
static void fResume(ArgArray args) {
assert(args[0].type == NUM);
debugC(1, kPrivateDebugScript, "Resume(%d)", args[0].u.val); // this value is always 1
g_private->resumeGame();
}
static void fMovie(ArgArray args) {
// assert types
assert(args[0].type == STRING);
assert(args[1].type == NAME);
debugC(1, kPrivateDebugScript, "Movie(%s, %s)", args[0].u.str, args[1].u.sym->name->c_str());
Common::String movie = args[0].u.str;
Common::String nextSetting = *args[1].u.sym->name;
if (!g_private->_playedMovies.contains(movie) && movie != "\"\"") {
g_private->addMemory(movie);
g_private->_nextMovie = movie;
g_private->_playedMovies.setVal(movie, true);
g_private->_nextSetting = nextSetting;
} else if (movie == "\"\"") {
g_private->_repeatedMovieExit = nextSetting;
debugC(1, kPrivateDebugScript, "repeated movie exit is %s", nextSetting.c_str());
} else {
debugC(1, kPrivateDebugScript, "movie %s already played", movie.c_str());
g_private->_nextSetting = g_private->_repeatedMovieExit;
}
}
static void fCRect(ArgArray args) {
// assert types
debugC(1, kPrivateDebugScript, "CRect(%d, %d, %d, %d)", args[0].u.val, args[1].u.val, args[2].u.val, args[3].u.val);
int x1, y1, x2, y2;
x1 = args[0].u.val;
y1 = args[1].u.val;
x2 = args[2].u.val;
y2 = args[3].u.val;
Datum d = Datum();
Common::Rect *rect = new Common::Rect(x1, y1, x2, y2);
d.type = RECT;
d.u.rect = rect;
Gen::push(d);
g_private->_rects.push_back(rect);
}
static void fBitmap(ArgArray args) {
assert(args.size() == 1 || args.size() == 3);
int x = 0;
int y = 0;
const char *f = args[0].u.str;
if (args.size() == 3) {
x = args[1].u.val;
y = args[2].u.val;
}
debugC(1, kPrivateDebugScript, "Bitmap(%s, %d, %d)", f, x, y);
Common::String s(args[0].u.str);
g_private->loadImage(s, x, y);
}
static void _fMask(ArgArray args, bool drawn) {
assert(args.size() == 3 || args.size() == 5);
int x = 0;
int y = 0;
const char *f = args[0].u.str;
const char *e = args[1].u.sym->name->c_str();
Common::String *c = args[2].u.sym->name;
if (args.size() == 5) {
x = args[3].u.val;
y = args[4].u.val;
}
debugC(1, kPrivateDebugScript, "Mask(%s, %s, %s, %d, %d)", f, e, c->c_str(), x, y);
const Common::String s(f);
MaskInfo m;
m.surf = g_private->loadMask(s, x, y, drawn);
m.nextSetting = e;
m.cursor = *c;
m.flag1 = nullptr;
m.flag2 = nullptr;
m.point = Common::Point(x, y);
g_private->_masks.push_front(m);
}
static void fMask(ArgArray args) {
_fMask(args, false);
}
static void fMaskDrawn(ArgArray args) {
_fMask(args, true);
}
static void fAMRadioClip(ArgArray args) {
assert(args.size() <= 4);
debugC(1, kPrivateDebugScript, "AMRadioClip(%s,%d,...)", args[0].u.str, args[1].u.val);
const char *name = args[0].u.str;
if (strcmp(name, "\"\"") == 0) {
int clipCount = args[1].u.val;
g_private->initializeAMRadioChannels(clipCount);
return;
}
int priority = args[1].u.val;
// The third and fourth parameters are numbers followed by an optional '+' character.
// Each number is a priority and the '+' indicates that it is to be treated as a range
// instead of the default behavior of requiring an exact match.
int disabledPriority1 = (args.size() >= 3) ? args[2].u.val : 0;
bool exactPriorityMatch1 = (args.size() >= 3) ? (args[2].type != NUM_PLUS) : true;
int disabledPriority2 = (args.size() >= 4) ? args[3].u.val : 0;
bool exactPriorityMatch2 = (args.size() >= 4) ? (args[3].type != NUM_PLUS) : true;
Common::String flagName = (args.size() >= 6) ? *(args[4].u.sym->name) : "";
int flagValue = (args.size() >= 6) ? args[5].u.val : 0;
g_private->addRadioClip(g_private->_AMRadio, name, priority,
disabledPriority1, exactPriorityMatch1,
disabledPriority2, exactPriorityMatch2,
flagName, flagValue);
}
static void fPoliceClip(ArgArray args) {
assert(args.size() <= 4 || args.size() == 6);
debugC(1, kPrivateDebugScript, "PoliceClip(%s,%d,...)", args[0].u.str, args[1].u.val);
const char *name = args[0].u.str;
if (strcmp(name, "\"\"") == 0) {
g_private->initializePoliceRadioChannels();
return;
}
int priority = args[1].u.val;
if (strcmp(name, "\"DISABLE_ONLY\"") == 0) {
g_private->disableRadioClips(g_private->_policeRadio, priority);
return;
}
// The third and fourth parameters are numbers followed by an optional '+' character.
// Each number is a priority and the '+' indicates that it is to be treated as a range
// instead of the default behavior of requiring an exact match.
int disabledPriority1 = (args.size() >= 3) ? args[2].u.val : 0;
bool exactPriorityMatch1 = (args.size() >= 3) ? (args[2].type != NUM_PLUS) : true;
int disabledPriority2 = (args.size() >= 4) ? args[3].u.val : 0;
bool exactPriorityMatch2 = (args.size() >= 4) ? (args[3].type != NUM_PLUS) : true;
g_private->addRadioClip(g_private->_policeRadio, name, priority,
disabledPriority1, exactPriorityMatch1,
disabledPriority2, exactPriorityMatch2,
"", 0);
}
static void fPhoneClip(ArgArray args) {
if (args.size() == 2) {
debugC(1, kPrivateDebugScript, "Unimplemented PhoneClip special case");
return;
}
assert(args.size() == 6);
debugC(1, kPrivateDebugScript, "PhoneClip(%s,%d,%d,%d,%s,%d)",
args[0].u.str, args[1].u.val, args[2].u.val, args[3].u.val, args[4].u.sym->name->c_str(), args[5].u.val);
const char *name = args[0].u.str;
bool once = (args[1].u.val != 0);
int startIndex = args[2].u.val;
int endIndex = args[3].u.val;
Common::String *flagName = args[4].u.sym->name;
int flagValue = args[5].u.val;
assert(startIndex <= endIndex);
g_private->addPhone(name, once, startIndex, endIndex, *flagName, flagValue);
}
static void fSoundArea(ArgArray args) {
// assert types
//char *n;
Common::String n;
if (args[1].type == NAME)
n = *(args[1].u.sym->name);
else if (args[1].type == STRING) {
n = Common::String(args[1].u.str);
Common::replace(n, "\"", "");
Common::replace(n, "\"", "");
} else
error("Invalid input for SoundArea");
debugC(1, kPrivateDebugScript, "SoundArea(%s, %s, ..)", args[0].u.str, n.c_str());
Common::String s = args[0].u.str;
MaskInfo m;
if (n == "kAMRadio") {
m.surf = g_private->loadMask(s, 0, 0, true);
m.cursor = *args[2].u.sym->name;
m.nextSetting = "";
m.flag1 = nullptr;
m.flag2 = nullptr;
g_private->_AMRadioArea = m;
g_private->_masks.push_front(m);
} else if (n == "kPoliceRadio") {
m.surf = g_private->loadMask(s, 0, 0, true);
m.cursor = *args[2].u.sym->name;
m.nextSetting = "";
m.flag1 = nullptr;
m.flag2 = nullptr;
g_private->_policeRadioArea = m;
g_private->_masks.push_front(m);
} else if (n == "kPhone") {
m.surf = g_private->loadMask(s, 0, 0, true);
m.cursor = *args[2].u.sym->name;
m.nextSetting = "";
m.flag1 = nullptr;
m.flag2 = nullptr;
g_private->_phoneArea = m;
g_private->initializePhoneOnDesktop();
} else
error("Invalid type for SoundArea");
}
static void fSafeDigit(ArgArray args) {
assert(args[0].type == NUM);
assert(args[1].type == RECT);
debugC(1, kPrivateDebugScript, "SafeDigit(%d, ..)", args[0].u.val);
g_private->addSafeDigit(args[0].u.val, args[1].u.rect);
}
static void fAskSave(ArgArray args) {
// This is not needed, since scummvm will take care of this
debugC(1, kPrivateDebugScript, "WARNING: AskSave is partially implemented");
g_private->_nextSetting = *args[0].u.sym->name;
}
static void fTimer(ArgArray args) {
assert(args.size() == 2 || args.size() == 3);
if (args.size() == 3)
debugC(1, kPrivateDebugScript, "Timer(%d, %s, %s)", args[0].u.val, args[1].u.sym->name->c_str(), args[2].u.sym->name->c_str());
else
debugC(1, kPrivateDebugScript, "Timer(%d, %s)", args[0].u.val, args[1].u.str);
int32 delay = args[0].u.val * 1000; // seconds => milliseconds
if (delay > 0) {
Common::String skipSetting;
if (args.size() == 3) {
skipSetting = *(args[2].u.sym->name);
}
g_private->setTimer(delay, *(args[1].u.sym->name), skipSetting);
} else if (delay == 0) {
g_private->_nextSetting = *(args[1].u.sym->name);
} else {
assert(0);
}
}
const FuncTable funcTable[] = {
// Control flow
{fChgMode, "ChgMode"},
{fResume, "Resume"},
{fgoto, "goto"},
{fTimer, "Timer"},
// Variables
{fSetFlag, "SetFlag"},
{fSetModifiedFlag, "SetModifiedFlag"},
// Sounds
{fSound, "Sound"},
{fSoundEffect, "SoundEffect"},
{fLoopedSound, "LoopedSound"},
{fNoStopSounds, "NoStopSounds"},
{fSyncSound, "SyncSound"},
{fAMRadioClip, "AMRadioClip"},
{fPoliceClip, "PoliceClip"},
{fPhoneClip, "PhoneClip"},
{fSoundArea, "SoundArea"},
{fPaperShuffleSound, "PaperShuffleSound"},
// Images
{fBitmap, "Bitmap"},
{fMask, "Mask"},
{fMaskDrawn, "MaskDrawn"},
{fVSPicture, "VSPicture"},
{fViewScreen, "ViewScreen"},
{fExit, "Exit"},
// Video
{fTransition, "Transition"},
{fMovie, "Movie"},
// Diary
{fDiaryLocList, "DiaryLocList"},
{fDiaryPageTurn, "DiaryPageTurn"},
{fDiaryPage, "DiaryPage"},
{fDiaryInvList, "DiaryInvList"},
{fDiaryGoLoc, "DiaryGoLoc"},
// Main menu
{fQuit, "Quit"},
{fLoadGame, "LoadGame"},
{fSaveGame, "SaveGame"},
{fAskSave, "AskSave"},
{fRestartGame, "RestartGame"},
// Dossiers
{fDossierAdd, "DossierAdd"},
{fDossierChgSheet, "DossierChgSheet"},
{fDossierBitmap, "DossierBitmap"},
{fDossierPrevSuspect, "DossierPrevSuspect"},
{fDossierNextSuspect, "DossierNextSuspect"},
// Inventory
{fLoseInventory, "LoseInventory"},
{fInventory, "Inventory"},
// PoliceBust
{fPoliceBust, "PoliceBust"},
{fBustMovie, "BustMovie"},
// Others
{fSafeDigit, "SafeDigit"},
{fCRect, "CRect"},
{nullptr, nullptr}};
void call(const char *name, const ArgArray &args) {
Common::String n(name);
if (!g_private->_functions.contains(n)) {
error("I don't know how to execute %s", name);
}
void (*func)(ArgArray) = (void (*)(ArgArray))g_private->_functions.getVal(n);
func(args);
}
} // End of namespace Private

1726
engines/private/grammar.cpp Normal file

File diff suppressed because it is too large Load Diff

147
engines/private/grammar.h Normal file
View File

@@ -0,0 +1,147 @@
/* 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 <http://www.gnu.org/licenses/>.
*
*/
#ifndef PRIVATE_GRAMMAR_H
#define PRIVATE_GRAMMAR_H
#include "common/str.h"
#include "common/hash-str.h"
#include "common/hash-ptr.h"
#include "common/queue.h"
#include "common/list.h"
#include "common/array.h"
#include "common/rect.h"
#include "private/symbol.h"
#define NSTACK 256
#define NPROG 10000
namespace Private {
typedef struct Datum { /* interpreter stack type */
short type;
union {
int val;
const char *str;
Symbol *sym;
Common::Rect *rect;
} u;
} Datum;
typedef struct Arg {
int n;
int (** inst)();
} Arg;
typedef int (* Inst)(); /* machine instruction */
#define STOP (Inst) 0
typedef Common::HashMap<void *, Common::String *> PtrToName;
void initInsts();
void initFuncs();
namespace Settings {
typedef struct Setting {
Datum stack[NSTACK]; /* the stack */
Inst prog[NPROG]; /* the machine */
} Setting;
typedef Common::HashMap<Common::String, Setting *> SettingMap;
class SettingMaps {
public:
SettingMaps();
~SettingMaps();
Setting *_setting;
SettingMap _map;
void init();
void save(const char *);
void load(const Common::String &);
private:
Common::Array<Setting *> _settings;
};
extern SettingMaps *g_setts;
}
// Funtions
typedef Common::Array<Datum> ArgArray;
void call(const char *, const ArgArray &);
// Code Generation and Execution
namespace Gen {
class VM {
public:
Datum *_stack; /* the stack */
Datum *_stackp; /* next free spot on stack */
Inst *_progp; /* next free spot for code generation */
Inst *_prog; /* the machine */
Inst *_pc; /* program counter during execution */
void run(); /* run the virtual machine */
};
extern VM *g_vm;
Datum pop();
int push(const Datum &);
Inst *code(const Inst &);
int eval();
int add();
int negate();
int power();
int assign();
int bltin();
int varpush();
int constpush();
int strpush();
int funcpush();
int print();
int ifcode();
int fail();
int lt();
int gt();
int le();
int ge();
int eq();
int ne();
int randbool();
// Code Execution
void execute(Inst *);
}
} // End of namespace Private
#endif

224
engines/private/grammar.y Normal file
View File

@@ -0,0 +1,224 @@
/* 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 <http://www.gnu.org/licenses/>.
*
*/
// Heavily inspired by hoc
// Copyright (C) AT&T 1995
// All Rights Reserved
//
// Permission to use, copy, modify, and distribute this software and
// its documentation for any purpose and without fee is hereby
// granted, provided that the above copyright notice appear in all
// copies and that both that the copyright notice and this
// permission notice and warranty disclaimer appear in supporting
// documentation, and that the name of AT&T or any of its entities
// not be used in advertising or publicity pertaining to
// distribution of the software without specific, written prior
// permission.
//
// AT&T DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
// INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS.
// IN NO EVENT SHALL AT&T OR ANY OF ITS ENTITIES BE LIABLE FOR ANY
// SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER
// IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
// ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
// THIS SOFTWARE.
%require "3.6"
%defines "engines/private/tokens.h"
%output "engines/private/grammar.cpp"
%define api.prefix {PRIVATE_}
%{
#include "private/private.h"
#include "private/grammar.h"
#undef yyerror
#define yyerror PRIVATE_xerror
#define code1(c1) code(c1);
#define code2(c1,c2) code(c1); code(c2)
#define code3(c1,c2,c3) code(c1); code(c2); code(c3)
using namespace Private;
using namespace Gen;
using namespace Settings;
extern int PRIVATE_lex();
//extern int PRIVATE_parse();
int markplus();
void PRIVATE_xerror(const char *str) {
}
int PRIVATE_wrap() {
return 1;
}
%}
%union {
Private::Symbol *sym; /* symbol table pointer */
int (**inst)(); /* machine instruction */
char *s; /* string value */
int *i; /* integer value */
int narg; /* auxiliary value to count function arguments */
}
%token<s> NAME
%token<sym> STRING NUM
%token NUM_PLUS
%type <inst> body if startp cond end expr statements statement fcall value
%token LTE GTE NEQ EQ FALSETOK TRUETOK NULLTOK IFTOK ELSETOK RECT GOTOTOK DEBUGTOK EMITCODEONTOK EMITCODEOFFTOK RESETIDTOK DEFINETOK SETTINGTOK RANDOMTOK
%type<narg> params
%%
lines: line lines
| line
;
line: DEBUGTOK '{' debug '}' { /* Not used in the game */ }
| EMITCODEONTOK { /* Unclear what this is */ }
| EMITCODEOFFTOK { /* Unclear what this is */ }
| RESETIDTOK { /* Unclear what this is */ }
| DEFINETOK NAME '{' define '}' { g_private->maps.installAll($NAME); }
| SETTINGTOK NAME '{' statements '}' { g_setts->save($NAME);
g_setts->init(); }
;
debug: /* nothing */
| NAME ',' debug
;
statements: /* nothing */ { $$ = g_vm->_progp; }
| statement statements
statement: GOTOTOK NAME ';' {
$$ = g_vm->_progp;
code2(strpush, (Inst) g_private->maps.constant(STRING, 0, $NAME));
code2(constpush, (Inst) g_private->maps.constant(NUM, 1, NULL));
code2(strpush, (Inst) g_private->maps.constant(STRING, 0, "goto"));
code1(funcpush);
}
| fcall ';' { $$ = $1; }
| if cond body end {
/* else-less if */
($1)[1] = (Inst)$3; /* thenpart */
($1)[3] = (Inst)$4;
} /* end, if cond fails */
| if cond body end ELSETOK body end {
/* if with else */
($1)[1] = (Inst)$3; /* thenpart */
($1)[2] = (Inst)$6; /* elsepart */
($1)[3] = (Inst)$7;
} /* end, if cond fails */
;
body: statement { $$ = $1; }
| '{' statements '}' { $$ = $2; }
;
end: /* nothing */ { code1(STOP); $$ = g_vm->_progp; }
;
if: IFTOK { $$ = code1(ifcode); code3(STOP, STOP, STOP); }
;
cond: '(' expr ')' { code1(STOP); $$ = $2; }
;
define: /* nothing */
| NAME ',' RECT '(' NUM ',' NUM ',' NUM ',' NUM ')' ',' define {
Common::Rect *r = new Common::Rect($5->u.val, $7->u.val, $9->u.val, $11->u.val);
assert(r->isValidRect());
g_private->maps.defineSymbol($NAME, r);
}
| NAME ',' RECT '(' NUM ',' NUM ',' NUM ',' NUM ')' {
Common::Rect *r = new Common::Rect($5->u.val, $7->u.val, $9->u.val, $11->u.val);
g_private->maps.defineSymbol($NAME, r);
}
| NAME ',' define { g_private->maps.defineSymbol($NAME, NULL); }
| NAME { g_private->maps.defineSymbol($NAME, NULL); }
;
fcall: GOTOTOK '(' NAME ')' {
$$ = g_vm->_progp;
code2(strpush, (Inst) g_private->maps.constant(STRING, 0, $NAME));
code2(constpush, (Inst) g_private->maps.constant(NUM, 1, NULL));
code2(strpush, (Inst) g_private->maps.constant(STRING, 0, "goto"));
code1(funcpush);
}
| RECT '(' NUM ',' NUM ',' NUM ',' NUM ')' { $$ = g_vm->_progp; }
| NAME '(' startp params ')' {
$$ = $startp;
code2(constpush, (Inst) g_private->maps.constant(NUM, $params, NULL));
code2(strpush, (Inst) g_private->maps.constant(STRING, 0, $NAME));
code1(funcpush);
}
;
startp: /*nothing*/ { $$ = g_vm->_progp; }
;
params: /* nothing */ { $$ = 0; }
| fcall ',' params { $$ = $3 + 1; }
| expr ',' params { $$ = $3 + 1; }
| expr { $$ = 1; }
| fcall { $$ = 1; }
;
value: NULLTOK { code2(constpush, (Inst) g_private->maps.constant(NUM, 0, NULL)); }
| FALSETOK { code2(constpush, (Inst) g_private->maps.constant(NUM, 0, NULL)); }
| TRUETOK { code2(constpush, (Inst) g_private->maps.constant(NUM, 1, NULL)); }
| NUM { code2(constpush, (Inst)$NUM); }
| STRING { code2(strpush, (Inst)$STRING); }
| NAME { code1(varpush); code1((Inst) g_private->maps.lookupName($NAME)); code1(eval); }
;
expr: value { $$ = $1; }
| '!' value { code1(negate); $$ = $2; }
| value EQ value { code1(eq); }
| value NEQ value { code1(ne); }
| value '+' value { code1(add); }
| value '<' value { code1(lt); }
| value '>' value { code1(gt); }
| value LTE value { code1(le); }
| value GTE value { code1(ge); }
| value '+' { code1(markplus); $$ = $1; }
| RANDOMTOK '(' NUM '%' ')' { code3(constpush, (Inst)$NUM, randbool); }
;
%%
int markplus() {
Datum d = pop();
if (d.type == NUM) {
d.type = NUM_PLUS;
}
push(d);
return 0;
}

2205
engines/private/lexer.cpp Normal file

File diff suppressed because it is too large Load Diff

94
engines/private/lexer.l Normal file
View File

@@ -0,0 +1,94 @@
%top{
/* 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 <http://www.gnu.org/licenses/>.
*
*/
#define YY_NO_UNISTD_H
#define FORBIDDEN_SYMBOL_EXCEPTION_FILE
#define FORBIDDEN_SYMBOL_EXCEPTION_fprintf
#define FORBIDDEN_SYMBOL_EXCEPTION_fwrite
#define FORBIDDEN_SYMBOL_EXCEPTION_fread
#define FORBIDDEN_SYMBOL_EXCEPTION_stdin
#define FORBIDDEN_SYMBOL_EXCEPTION_stdout
#define FORBIDDEN_SYMBOL_EXCEPTION_stderr
#define FORBIDDEN_SYMBOL_EXCEPTION_exit
#define FORBIDDEN_SYMBOL_EXCEPTION_getc
#include "private/private.h"
#include "private/grammar.h"
#include "private/tokens.h"
using namespace Private;
using namespace Gen;
using namespace Settings;
}
%option noyywrap
%option noinput
%option nounput
%option never-interactive
%option outfile="engines/private/lexer.cpp"
%option prefix="PRIVATE_"
%%
\/\/.* /* ignoring the comment */
\<= return LTE;
\>= return GTE;
!= return NEQ;
== return EQ;
debug return DEBUGTOK;
define return DEFINETOK;
setting return SETTINGTOK;
EmitCodeOff return EMITCODEOFFTOK;
EmitCodeOn return EMITCODEONTOK;
ResetIDCounter return RESETIDTOK;
if return IFTOK;
else return ELSETOK;
goto return GOTOTOK;
RECT return RECT;
FALSE return FALSETOK;
TRUE return TRUETOK;
NULL return NULLTOK;
Random return RANDOMTOK;
[A-Za-z_][A-Za-z_0-9]* PRIVATE_lval.s = g_private->maps.string(PRIVATE_text); return NAME;
[\-]?[0-9]+ PRIVATE_lval.sym = g_private->maps.constant(NUM, atoi(PRIVATE_text), NULL); return NUM;
\"[^\"\r\n]*\" PRIVATE_lval.sym = g_private->maps.constant(STRING, 0, PRIVATE_text); return STRING;
[\r|\n]+ /* ignore return */;
[ \t]+ /* ignore whitespace */;
. return *yytext;
%%
namespace Private {
int parse(const char *code) {
g_setts->init();
YY_BUFFER_STATE bp;
yy_delete_buffer(YY_CURRENT_BUFFER);
bp = yy_scan_string(code);
yy_switch_to_buffer(bp);
PRIVATE_parse();
yy_delete_buffer(bp);
yylex_destroy();
return 0;
}
} // End of namespace Private

View File

@@ -0,0 +1,157 @@
/* 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 <http://www.gnu.org/licenses/>.
*
*/
#include "engines/advancedDetector.h"
#include "graphics/scaler.h"
#include "common/savefile.h"
#include "common/translation.h"
#include "private/private.h"
#include "private/detection.h"
#include "private/savegame.h"
#include "backends/keymapper/action.h"
#include "backends/keymapper/keymapper.h"
#include "backends/keymapper/standard-actions.h"
static const ADExtraGuiOptionsMap optionsList[] = {
{
GAMEOPTION_SFX_SUBTITLES,
{
_s("Display SFX subtitles"),
_s("Use SFX subtitles (if subtitles are enabled)."),
"sfxSubtitles",
false,
0,
0
}
},
{
GAMEOPTION_HIGHLIGHT_MASKS,
{
_s("Highlight decision areas"),
_s("Highlight clickable areas in decision scenes."),
"highlightMasks",
false,
0,
0
}
},
AD_EXTRA_GUI_OPTIONS_TERMINATOR
};
class PrivateMetaEngine : public AdvancedMetaEngine<ADGameDescription> {
public:
const char *getName() const override {
return "private";
}
const ADExtraGuiOptionsMap *getAdvancedExtraGuiOptions() const override {
return optionsList;
}
Common::Error createInstance(OSystem *syst, Engine **engine, const ADGameDescription *desc) const override;
void getSavegameThumbnail(Graphics::Surface &thumb) override;
SaveStateDescriptor querySaveMetaInfos(const char *target, int slot) const override;
Common::KeymapArray initKeymaps(const char *target) const override;
};
Common::Error PrivateMetaEngine::createInstance(OSystem *syst, Engine **engine, const ADGameDescription *gd) const {
*engine = new Private::PrivateEngine(syst, gd);
return Common::kNoError;
}
void PrivateMetaEngine::getSavegameThumbnail(Graphics::Surface &thumb) {
byte *palette;
bool isNewPalette;
Graphics::Surface *vs = Private::g_private->decodeImage(Private::g_private->_nextVS, &palette, &isNewPalette);
::createThumbnail(&thumb, (const uint8 *)vs->getPixels(), vs->w, vs->h, palette);
vs->free();
delete vs;
if (isNewPalette) {
free(palette);
}
}
/**
* querySaveMetaInfos override that filters out saves with incompatible formats.
*
* The Private Eye save format was significantly changed to add more engine state.
* Older saves are incompatible, and we might have to change the format again.
* Save files now contain a version number in their header so that we can detect
* that a save is compatible, and not present incompatible saves to users.
*/
SaveStateDescriptor PrivateMetaEngine::querySaveMetaInfos(const char *target, int slot) const {
using namespace Private;
SaveStateDescriptor desc = MetaEngine::querySaveMetaInfos(target, slot);
if (desc.getSaveSlot() == -1) {
return desc;
}
// Only saves with compatible metadata headers are allowed.
Common::ScopedPtr<Common::InSaveFile> f(g_system->getSavefileManager()->openForLoading(
getSavegameFile(slot, target)));
if (f) {
SavegameMetadata meta;
if (!readSavegameMetadata(f.get(), meta)) {
return SaveStateDescriptor();
}
}
return desc;
}
Common::KeymapArray PrivateMetaEngine::initKeymaps(const char *target) const {
using namespace Common;
using namespace Private;
Keymap *engineKeymap = new Keymap(Keymap::kKeymapTypeGame, "private-default", _("Default keymappings"));
Action *act;
act = new Action(kStandardActionLeftClick, _("Select / Interact"));
act->setLeftClickEvent();
act->addDefaultInputMapping("MOUSE_LEFT");
act->addDefaultInputMapping("JOY_A");
engineKeymap->addAction(act);
act = new Action("SKIP", _("Skip video"));
act->setCustomEngineActionEvent(kActionSkip);
act->addDefaultInputMapping("ESCAPE");
act->addDefaultInputMapping("JOY_X");
engineKeymap->addAction(act);
return Keymap::arrayOf(engineKeymap);
}
namespace Private {
bool PrivateEngine::isDemo() const {
return (bool)(_gameDescription->flags & ADGF_DEMO);
}
} // End of namespace Private
#if PLUGIN_ENABLED_DYNAMIC(PRIVATE)
REGISTER_PLUGIN_DYNAMIC(PRIVATE, PLUGIN_TYPE_ENGINE, PrivateMetaEngine);
#else
REGISTER_PLUGIN_STATIC(PRIVATE, PLUGIN_TYPE_ENGINE, PrivateMetaEngine);
#endif

34
engines/private/module.mk Normal file
View File

@@ -0,0 +1,34 @@
MODULE := engines/private
MODULE_OBJS := \
code.o \
cursors.o \
decompiler.o \
funcs.o \
grammar.o \
lexer.o \
metaengine.o \
private.o \
savegame.o \
symbol.o
MODULE_DIRS += \
engines/private
# HACK: Skip this when including the file for detection objects.
ifeq "$(LOAD_RULES_MK)" "1"
private-grammar:
flex engines/private/lexer.l
bison engines/private/grammar.y
endif
# This module can be built as a plugin
ifeq ($(ENABLE_PRIVATE), DYNAMIC_PLUGIN)
PLUGIN := 1
endif
# Include common rules
include $(srcdir)/rules.mk
# Detection objects
DETECT_OBJS += $(MODULE)/detection.o

3291
engines/private/private.cpp Normal file

File diff suppressed because it is too large Load Diff

555
engines/private/private.h Normal file
View File

@@ -0,0 +1,555 @@
/* 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 <http://www.gnu.org/licenses/>.
*
*/
#ifndef PRIVATE_H
#define PRIVATE_H
#include "common/random.h"
#include "common/serializer.h"
#include "engines/engine.h"
#include "graphics/managed_surface.h"
#include "graphics/wincursor.h"
#include "video/smk_decoder.h"
#include "video/subtitles.h"
#include "private/grammar.h"
namespace Common {
class Archive;
}
namespace Image {
class ImageDecoder;
}
namespace Graphics {
class ManagedSurface;
}
struct ADGameDescription;
namespace Private {
enum PRIVATEActions {
kActionSkip,
};
// debug channels
enum {
kPrivateDebugFunction = 1,
kPrivateDebugCode,
kPrivateDebugScript,
};
// sounds
const int kPaperShuffleSound[7] = {32, 33, 34, 35, 36, 37, 39};
// police
const int kPoliceBustVideos[6] = {1, 2, 4, 5, 7, 8};
// points
const int kOriginZero[2] = {0, 0};
const int kOriginOne[2] = {64, 48};
// vm
extern Gen::VM *Gen::g_vm;
// structs
typedef struct ExitInfo {
Common::String nextSetting;
Common::Rect rect;
Common::String cursor;
void clear() {
nextSetting.clear();
rect.setEmpty();
cursor.clear();
}
} ExitInfo;
typedef struct MaskInfo {
Graphics::Surface *surf;
Common::String nextSetting;
Common::Point point;
Symbol *flag1;
Symbol *flag2;
Common::String cursor;
Common::String inventoryItem;
bool useBoxCollision;
Common::Rect box;
MaskInfo() {
clear();
}
void clear() {
surf = nullptr;
useBoxCollision = false;
box = Common::Rect();
flag1 = nullptr;
flag2 = nullptr;
nextSetting.clear();
cursor.clear();
point = Common::Point();
inventoryItem.clear();
}
} MaskInfo;
enum PhoneStatus : byte {
kPhoneStatusWaiting,
kPhoneStatusAvailable,
kPhoneStatusCalling,
kPhoneStatusMissed,
kPhoneStatusAnswered
};
typedef struct Sound {
Common::String name;
Audio::SoundHandle handle;
} Sound;
typedef struct PhoneInfo {
Common::String name;
bool once;
int startIndex;
int endIndex;
Common::String flagName;
int flagValue;
PhoneStatus status;
int callCount;
uint32 soundIndex;
Common::Array<Common::String> sounds;
} PhoneInfo;
typedef struct RadioClip {
Common::String name;
bool played;
int priority;
int disabledPriority1; // 0 == none
bool exactPriorityMatch1;
int disabledPriority2; // 0 == none
bool exactPriorityMatch2;
Common::String flagName;
int flagValue;
} RadioClip;
typedef struct Radio {
Common::String path;
Sound *sound;
Common::Array<RadioClip> clips;
int channels[3];
Radio() : sound(nullptr) {
clear();
}
void clear() {
clips.clear();
for (uint i = 0; i < ARRAYSIZE(channels); i++) {
channels[i] = -1;
}
}
} Radio;
typedef struct DossierInfo {
Common::String page1;
Common::String page2;
} DossierInfo;
typedef struct CursorInfo {
Common::String name;
Common::String aname;
Graphics::Cursor *cursor;
Graphics::WinCursorGroup *winCursorGroup;
} CursorInfo;
typedef struct MemoryInfo {
Common::String image;
Common::String movie;
} MemoryInfo;
typedef struct DiaryPage {
Common::String locationName;
Common::Array<MemoryInfo> memories;
int locationID;
} DiaryPage;
typedef struct InventoryItem {
Common::String diaryImage;
Common::String flag;
} InventoryItem;
// funcs
typedef struct FuncTable {
void (*func)(Private::ArgArray);
const char *name;
} FunctTable;
typedef Common::HashMap<Common::String, void *> NameToPtr;
extern const FuncTable funcTable[];
// lists
typedef Common::List<ExitInfo> ExitList;
typedef Common::List<MaskInfo> MaskList;
typedef Common::List<PhoneInfo> PhoneList;
typedef Common::List<InventoryItem> InvList;
typedef Common::List<Common::Rect *> RectList;
// arrays
typedef Common::Array<DossierInfo> DossierArray;
typedef Common::Array<DiaryPage> DiaryPages;
// hash tables
typedef Common::HashMap<Common::String, bool> PlayedMediaTable;
enum SubtitleType {
kSubtitleAudio,
kSubtitleVideo
};
struct SubtitleSlot {
Audio::SoundHandle handle;
Video::Subtitles *subs;
SubtitleSlot() : subs(nullptr) {}
};
class PrivateEngine : public Engine {
private:
Common::RandomSource *_rnd;
Graphics::PixelFormat _pixelFormat;
Image::ImageDecoder *_image;
int _screenW, _screenH;
// helper to generate the correct subtitle path
Common::Path getSubtitlePath(const Common::String &soundName);
bool isSfxSubtitle(const Video::Subtitles *subs);
bool isSlotActive(const SubtitleSlot &slot);
public:
bool _shouldHighlightMasks;
bool _highlightMasks;
PrivateEngine(OSystem *syst, const ADGameDescription *gd);
~PrivateEngine();
const ADGameDescription *_gameDescription;
bool isDemo() const;
Common::Language _language;
Common::Platform _platform;
SymbolMaps maps;
Video::SmackerDecoder *_videoDecoder;
Video::SmackerDecoder *_pausedVideo;
Common::String _pausedMovieName;
Common::Error run() override;
void restartGame();
void clearAreas();
void initializePath(const Common::FSNode &gamePath) override;
void pauseEngineIntern(bool pause) override;
Common::SeekableReadStream *loadAssets();
Common::Archive *loadMacInstaller();
// Functions
NameToPtr _functions;
void initFuncs();
// User input
void selectPauseGame(Common::Point);
bool selectMask(Common::Point);
bool selectExit(Common::Point);
bool selectLoadGame(Common::Point);
bool selectSaveGame(Common::Point);
void resumeGame();
// Cursors
void updateCursor(Common::Point);
bool cursorPauseMovie(Common::Point);
bool cursorExit(Common::Point);
bool cursorSafeDigit(Common::Point);
bool cursorMask(Common::Point);
bool hasFeature(EngineFeature f) const override;
bool canLoadGameStateCurrently(Common::U32String *msg = nullptr) override {
return true;
}
bool canSaveAutosaveCurrently() override {
return false;
}
bool canSaveGameStateCurrently(Common::U32String *msg = nullptr) override {
return true;
}
Common::Error loadGameStream(Common::SeekableReadStream *stream) override;
Common::Error saveGameStream(Common::WriteStream *stream, bool isAutosave = false) override;
static Common::Path convertPath(const Common::String &name);
static Common::String getVideoViewScreen(Common::String video);
void playVideo(const Common::String &);
void skipVideo();
void destroyVideo();
void loadSubtitles(const Common::Path &path, SubtitleType type, Sound *sound = nullptr);
// use to clean up sounds which have finished playing once
void updateSubtitles();
void destroySubtitles();
void adjustSubtitleSize();
Video::Subtitles *_videoSubtitles;
SubtitleSlot _voiceSlot; // high priority (speech)
SubtitleSlot _sfxSlot; // low priority (sfxs)
bool _useSubtitles;
bool _sfxSubtitles;
Graphics::Surface *decodeImage(const Common::String &file, byte **palette, bool *isNewPalette);
//byte *decodePalette(const Common::String &name);
void remapImage(uint16 ncolors, const Graphics::Surface *oldImage, const byte *oldPalette, Graphics::Surface *newImage, const byte *currentPalette);
static uint32 findMaskTransparentColor(const byte *palette, uint32 defaultColor);
static void swapImageColors(Graphics::Surface *image, byte *palette, uint32 a, uint32 b);
void loadImage(const Common::String &file, int x, int y);
void drawScreenFrame(const byte *videoPalette);
// Cursors
Graphics::Cursor *_defaultCursor;
Common::Array<CursorInfo> _cursors;
Common::String _currentCursor;
void changeCursor(const Common::String &);
Common::String getInventoryCursor();
Common::String getExitCursor();
void loadCursors();
// Rendering
Graphics::ManagedSurface *_compositeSurface;
Graphics::Surface *loadMask(const Common::String &, int, int, bool);
void loadMaskAndInfo(MaskInfo *m, const Common::String &name, int x, int y, bool drawn);
void drawMask(Graphics::Surface *);
void fillRect(uint32, Common::Rect);
bool inMask(Graphics::Surface *, Common::Point);
uint32 _transparentColor;
Common::Rect _screenRect;
Common::String _framePath;
Graphics::Surface *_frameImage;
Graphics::Surface *_mframeImage;
byte *_framePalette;
Common::String _nextVS;
Common::String _currentVS;
Common::Point _origin;
void drawScreen();
bool _needToDrawScreenFrame;
// settings
Common::String _nextSetting;
Common::String _pausedSetting;
Common::String _currentSetting;
Common::String getPauseMovieSetting();
Common::String getGoIntroSetting();
Common::String getMainDesktopSetting();
Common::String getDiaryTOCSetting();
Common::String getDiaryMiddleSetting();
Common::String getDiaryLastPageSetting();
Common::String getPOGoBustMovieSetting();
Common::String getPoliceBustFromMOSetting();
Common::String getListenToPhoneSetting();
Common::String getAlternateGameVariable();
Common::String getPoliceIndexVariable();
Common::String getWallSafeValueVariable();
Common::String getPoliceArrivedVariable();
Common::String getBeenDowntownVariable();
Common::String getPoliceStationLocation();
const char *getSymbolName(const char *name, const char *strippedName, const char *demoName = nullptr);
// movies
Common::String _nextMovie;
Common::String _currentMovie;
// Dossiers
DossierArray _dossiers;
uint _dossierSuspect;
uint _dossierPage;
MaskInfo _dossierPageMask;
MaskInfo _dossierNextSuspectMask;
MaskInfo _dossierPrevSuspectMask;
MaskInfo _dossierNextSheetMask;
MaskInfo _dossierPrevSheetMask;
bool selectDossierPage(Common::Point);
bool selectDossierNextSuspect(Common::Point);
bool selectDossierPrevSuspect(Common::Point);
bool selectDossierNextSheet(Common::Point);
bool selectDossierPrevSheet(Common::Point);
void addDossier(Common::String &page1, Common::String &page2);
void loadDossier();
// Police Bust
bool _policeBustEnabled;
bool _policeSirenPlayed;
int _numberOfClicks;
int _numberClicksAfterSiren;
int _policeBustMovieIndex;
Common::String _policeBustMovie;
Common::String _policeBustPreviousSetting;
void resetPoliceBust();
void startPoliceBust();
void stopPoliceBust();
void wallSafeAlarm();
void completePoliceBust();
void checkPoliceBust();
// Diary
InvList inventory;
bool inInventory(const Common::String &bmp) const;
void addInventory(const Common::String &bmp, Common::String &flag);
void removeInventory(const Common::String &bmp);
void removeRandomInventory();
Common::String _diaryLocPrefix;
void loadLocations(const Common::Rect &);
void loadInventory(uint32, const Common::Rect &, const Common::Rect &);
bool _toTake;
bool _haveTakenItem;
DiaryPages _diaryPages;
int _currentDiaryPage;
ExitInfo _diaryNextPageExit;
ExitInfo _diaryPrevPageExit;
bool selectDiaryNextPage(Common::Point mousePos);
bool selectDiaryPrevPage(Common::Point mousePos);
void addMemory(const Common::String &path);
void loadMemories(const Common::Rect &rect, uint rightPageOffset, uint verticalOffset);
bool selectLocation(const Common::Point &mousePos);
Common::Array<MaskInfo> _locationMasks;
Common::Array<MaskInfo> _memoryMasks;
bool selectMemory(const Common::Point &mousePos);
void setLocationAsVisited(Symbol *location);
int getMaxLocationValue();
bool selectSkipMemoryVideo(Common::Point mousePos);
// Save/Load games
MaskInfo _saveGameMask;
MaskInfo _loadGameMask;
int _mode;
bool _modified;
PlayedMediaTable _playedMovies;
Common::String _repeatedMovieExit;
// Masks/Exits
ExitList _exits;
MaskList _masks;
// Sounds
void playBackgroundSound(const Common::String &name);
void playForegroundSound(const Common::String &name);
void playForegroundSound(Sound &sound, const Common::String &name);
void playSound(Sound &sound, const Common::String &name, bool loop);
void stopForegroundSounds();
void stopSounds();
void stopSound(Sound &sound);
bool isSoundPlaying();
bool isSoundPlaying(Sound &sound);
void waitForSoundsToStop();
bool consumeEvents();
Sound _bgSound;
Sound _fgSounds[4];
Sound _phoneCallSound;
Sound _AMRadioSound;
Sound _policeRadioSound;
Sound _takeLeaveSound;
bool _noStopSounds;
Common::String _pausedBackgroundSoundName;
Common::String getPaperShuffleSound();
Common::String _globalAudioPath;
Common::String getTakeSound();
Common::String getTakeLeaveSound();
Common::String getLeaveSound();
Common::String _sirenSound;
// Radios
MaskInfo _AMRadioArea;
MaskInfo _policeRadioArea;
Radio _AMRadio;
Radio _policeRadio;
void addRadioClip(
Radio &radio, const Common::String &name, int priority,
int disabledPriority1, bool exactPriorityMatch1,
int disabledPriority2, bool exactPriorityMatch2,
const Common::String &flagName, int flagValue);
void initializeAMRadioChannels(uint clipCount);
void initializePoliceRadioChannels();
void disableRadioClips(Radio &radio, int priority);
void playRadio(Radio &radio, bool randomlyDisableClips);
bool selectAMRadioArea(Common::Point);
bool selectPoliceRadioArea(Common::Point);
// Phone
MaskInfo _phoneArea;
Common::String _phonePrefix;
PhoneList _phones;
void addPhone(const Common::String &name, bool once, int startIndex, int endIndex, const Common::String &flagName, int flagValue);
void initializePhoneOnDesktop();
void checkPhoneCall();
bool cursorPhoneArea(Common::Point mousePos);
bool selectPhoneArea(Common::Point mousePos);
// Safe
Common::String _safeNumberPath;
MaskInfo _safeDigitArea[3];
Common::Rect _safeDigitRect[3];
void initializeWallSafeValue();
bool selectSafeDigit(Common::Point);
void addSafeDigit(uint32, Common::Rect*);
int getSafeDigit(uint32 d);
void incrementSafeDigit(uint32 d);
// Random values
bool getRandomBool(uint);
// Timer
Common::String _timerSetting;
Common::String _timerSkipSetting;
uint32 _timerStartTime;
uint32 _timerDelay;
void setTimer(uint32 duration, const Common::String &setting, const Common::String &skipSetting);
void clearTimer();
void skipTimer();
void checkTimer();
// VM objects
RectList _rects; // created by fCRect
};
extern PrivateEngine *g_private;
} // End of namespace Private
#endif

View File

@@ -0,0 +1,67 @@
/* 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 <http://www.gnu.org/licenses/>.
*
*/
#include "common/debug.h"
#include "common/stream.h"
#include "engines/private/savegame.h"
namespace Private {
static const uint32 kSavegameHeader = MKTAG('P','E','Y','E');
void writeSavegameMetadata(Common::WriteStream *stream, const SavegameMetadata &meta) {
stream->writeUint32BE(kSavegameHeader);
stream->writeUint16LE(meta.version);
stream->writeSByte(meta.language);
stream->writeSByte(meta.platform);
}
bool readSavegameMetadata(Common::SeekableReadStream *stream, SavegameMetadata &meta) {
byte buffer[8];
stream->read(buffer, 8);
if (stream->eos() || stream->err()) {
return false;
}
uint32 header = READ_BE_UINT32(buffer);
if (header != kSavegameHeader) {
debug(1, "Save does not have metadata header");
return false;
}
meta.version = READ_LE_UINT16(buffer + 4);
if (meta.version < kMinimumSavegameVersion) {
debug(1, "Save version %d lower than minimum %d", meta.version, kMinimumSavegameVersion);
return false;
}
if (meta.version > kCurrentSavegameVersion) {
debug(1, "Save version %d newer than current %d", meta.version, kCurrentSavegameVersion);
return false;
}
meta.language = (Common::Language)buffer[6];
meta.platform = (Common::Platform)buffer[7];
return true;
}
} // End of namespace Private

View File

@@ -0,0 +1,67 @@
/* 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 <http://www.gnu.org/licenses/>.
*
*/
#ifndef PRIVATE_SAVEGAME_H
#define PRIVATE_SAVEGAME_H
#include "common/language.h"
#include "common/platform.h"
namespace Common {
class SeekableReadStream;
class WriteStream;
}
namespace Private {
// Savegame format history:
//
// Version - new/changed feature
// =============================
// 4 - _pausedBackgroundSoundName (December 2025)
// 3 - Radio detailed state (December 2025)
// 2 - Phone clip detailed state (December 2025)
// 1 - Metadata header and more game state (November 2025)
//
// Earlier versions did not have a header and not supported.
const uint16 kCurrentSavegameVersion = 4;
const uint16 kMinimumSavegameVersion = 3;
struct SavegameMetadata {
uint16 version;
Common::Language language;
Common::Platform platform;
};
/**
* Write the header to a savegame.
*/
void writeSavegameMetadata(Common::WriteStream *stream, const SavegameMetadata &meta);
/**
* Read the header from a savegame.
*/
bool readSavegameMetadata(Common::SeekableReadStream *stream, SavegameMetadata &meta);
} // End of namespace Private
#endif

233
engines/private/symbol.cpp Normal file
View File

@@ -0,0 +1,233 @@
/* 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 <http://www.gnu.org/licenses/>.
*
*/
// Heavily inspired by hoc
// Copyright (C) AT&T 1995
// All Rights Reserved
//
// Permission to use, copy, modify, and distribute this software and
// its documentation for any purpose and without fee is hereby
// granted, provided that the above copyright notice appear in all
// copies and that both that the copyright notice and this
// permission notice and warranty disclaimer appear in supporting
// documentation, and that the name of AT&T or any of its entities
// not be used in advertising or publicity pertaining to
// distribution of the software without specific, written prior
// permission.
//
// AT&T DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
// INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS.
// IN NO EVENT SHALL AT&T OR ANY OF ITS ENTITIES BE LIABLE FOR ANY
// SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER
// IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
// ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
// THIS SOFTWARE.
#include "common/str.h"
#include "private/grammar.h"
#include "private/private.h"
#include "private/tokens.h"
namespace Private {
SymbolMaps::~SymbolMaps() {
freeSymbolMap(settings);
freeSymbolMap(variables);
freeSymbolMap(cursors);
freeSymbolMap(locations);
freeSymbolMap(rects);
freeSymbolList(constants);
freeSymbolList(names);
freeStringMap(strings);
}
void SymbolMaps::defineSymbol(const char *n, Common::Rect *r) {
Common::String s(n);
stringToDefine.push(s);
rectToDefine.push(r);
}
/*
static void showSymbol(const Symbol *s) {
if (s->type == NUM)
debugC(1, kPrivateDebugCode, "%s %d",s->name->c_str(), s->u.val);
else if (s->type == STRING)
debugC(1, kPrivateDebugCode, "%s %s", s->name->c_str(), s->u.str);
else if (s->type == NAME)
debugC(1, kPrivateDebugCode, "%s %d",s->name->c_str(), s->type);
else
debugC(1, kPrivateDebugCode, "%s %d", s->name->c_str(), s->type);
}
*/
void setSymbol(Symbol *s, int v) {
s->u.val = v;
}
/* find s in symbol table symlist */
static Symbol *lookup(const Common::String &s, const SymbolMap &symlist) {
Symbol *r = symlist.getVal(s);
return r;
}
/* install some symbol s in a symbol table */
static Symbol *install(const Common::String &n, int t, int d, const char *s, Common::Rect *r, SymbolMap *symlist) {
Symbol *sp;
sp = (Symbol *)malloc(sizeof(Symbol));
sp->name = new Common::String(n);
sp->type = t;
if (t == NUM) {
sp->u.val = d;
//debug("install NUM: %s %d", name->c_str(), d);
} else if (t == NAME) {
sp->u.val = d;
//debug("installing NAME: %s %d", name->c_str(), d);
} else if (t == STRING)
sp->u.str = scumm_strdup(s);
else if (t == RECT)
sp->u.rect = r;
else
assert(0);
symlist->setVal(n, sp);
assert(symlist->size() > 0);
return sp;
}
void SymbolMaps::freeSymbolMap(SymbolMap &symbols) {
for (SymbolMap::iterator it = symbols.begin(); it != symbols.end(); ++it) {
Symbol *s = it->_value;
delete s->name;
if (s->type == STRING) {
free(s->u.str);
} else if (s->type == RECT) {
delete s->u.rect;
}
free(s);
}
}
/* lookup some name in some symbol table */
Symbol *SymbolMaps::lookupRect(Common::String *n) {
assert(rects.contains(*n));
return lookup(*n, rects);
}
Symbol *SymbolMaps::lookupVariable(Common::String *n) {
assert(variables.contains(*n));
return lookup(*n, variables);
}
Symbol *SymbolMaps::lookupLocation(Common::String *n) {
assert(locations.contains(*n));
return lookup(*n, locations);
}
Symbol *SymbolMaps::lookupName(const char *n) {
Symbol *s = (Symbol *)malloc(sizeof(Symbol));
Common::String *name = new Common::String(n);
s->name = name;
s->type = NAME;
s->u.val = 0;
names.push_back(s);
return s;
}
void SymbolMaps::installAll(const char *n) {
assert(stringToDefine.size() > 0);
while (!stringToDefine.empty()) {
Common::String s = stringToDefine.pop();
Common::Rect *r = rectToDefine.pop();
//debug("name %s", s.c_str());
if (strcmp(n, "settings") == 0) {
//debug("new setting %s", n);
assert(r == nullptr);
install(s, NAME, 0, s.c_str(), r, &settings);
} else if (strcmp(n, "variables") == 0) {
assert(r == nullptr);
install(s, NAME, 0, nullptr, r, &variables);
variableList.push_front(s);
} else if (strcmp(n, "cursors") == 0) {
assert(r == nullptr);
install(s, NAME, 0, nullptr, r, &cursors);
} else if (strcmp(n, "locations") == 0) {
assert(r == nullptr);
install(s, NAME, 0, nullptr, r, &locations);
locationList.push_front(s);
} else if (strcmp(n, "rects") == 0) {
assert(r != nullptr);
install(s, RECT, 0, nullptr, r, &rects);
} else
error("invalid symbol type");
}
}
Symbol *SymbolMaps::constant(int t, int d, const char *s) {
Symbol *sp;
Common::String *n = new Common::String("<constant>");
sp = (Symbol *)malloc(sizeof(Symbol));
sp->name = n;
sp->type = t;
if (t == NUM || t == NAME)
sp->u.val = d;
else if (t == STRING)
sp->u.str = scumm_strdup(s);
else
assert(0);
constants.push_front(sp);
return sp;
}
void SymbolMaps::freeSymbolList(SymbolList &symbols) {
for (SymbolList::iterator it = symbols.begin(); it != symbols.end(); ++it) {
Symbol *s = (*it);
delete s->name;
if (s->type == STRING) {
free(s->u.str);
}
free(s);
}
}
char *SymbolMaps::string(const char *in) {
Common::String str(in);
char *out;
if (!strings.tryGetVal(in, out)) {
out = scumm_strdup(in);
strings[str] = out;
}
return out;
}
void SymbolMaps::freeStringMap(StringMap &strings) {
for (StringMap::iterator it = strings.begin(); it != strings.end(); ++it) {
char *s = it->_value;
free(s);
}
}
} // End of namespace Private

94
engines/private/symbol.h Normal file
View File

@@ -0,0 +1,94 @@
/* 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 <http://www.gnu.org/licenses/>.
*
*/
#ifndef PRIVATE_SYMBOL_H
#define PRIVATE_SYMBOL_H
#include "common/str.h"
#include "common/hash-str.h"
#include "common/hash-ptr.h"
#include "common/queue.h"
#include "common/list.h"
#include "common/array.h"
#include "common/rect.h"
namespace Private {
typedef struct Symbol { /* symbol table entry */
Common::String *name;
short type; /* NAME, NUM, STRING or RECT */
union {
int val; /* NAME or NUM */
char *str; /* STRING */
Common::Rect *rect; /* RECT */
} u;
} Symbol;
// Symbols
void setSymbol(Symbol *, int);
typedef Common::HashMap<Common::String, Symbol *> SymbolMap;
typedef Common::List<Common::String> NameList;
typedef Common::List<Symbol *> SymbolList;
typedef Common::HashMap<Common::String, char *> StringMap;
typedef Common::Queue<Common::String> StringQueue;
typedef Common::Queue<Common::Rect *> RectQueue;
class SymbolMaps {
private:
StringQueue stringToDefine;
RectQueue rectToDefine;
static void freeSymbolMap(SymbolMap &symbols);
static void freeSymbolList(SymbolList &symbols);
static void freeStringMap(StringMap &strings);
public:
SymbolMap settings;
SymbolMap variables;
SymbolMap cursors;
SymbolMap locations;
SymbolMap rects;
SymbolList constants;
SymbolList names;
NameList variableList;
NameList locationList;
StringMap strings;
SymbolMaps() { }
~SymbolMaps();
Symbol *constant(int t, int d, const char *s);
Symbol *lookupVariable(Common::String *n);
Symbol *lookupLocation(Common::String *n);
Symbol *lookupRect(Common::String *n);
Symbol *lookupName(const char *n);
void installAll(const char *n);
void defineSymbol(const char *, Common::Rect *);
char *string(const char *in);
};
} // End of namespace Private
#endif

118
engines/private/tokens.h Normal file
View File

@@ -0,0 +1,118 @@
/* A Bison parser, made by GNU Bison 3.8.2. */
/* Bison interface for Yacc-like parsers in C
Copyright (C) 1984, 1989-1990, 2000-2015, 2018-2021 Free Software Foundation,
Inc.
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 <https://www.gnu.org/licenses/>. */
/* As a special exception, you may create a larger work that contains
part or all of the Bison parser skeleton and distribute that work
under terms of your choice, so long as that work isn't itself a
parser generator using the skeleton or a modified version thereof
as a parser skeleton. Alternatively, if you modify or redistribute
the parser skeleton itself, you may (at your option) remove this
special exception, which will cause the skeleton and the resulting
Bison output files to be licensed under the GNU General Public
License without this special exception.
This special exception was added by the Free Software Foundation in
version 2.2 of Bison. */
/* DO NOT RELY ON FEATURES THAT ARE NOT DOCUMENTED in the manual,
especially those whose name start with YY_ or yy_. They are
private implementation details that can be changed or removed. */
#ifndef YY_PRIVATE_ENGINES_PRIVATE_TOKENS_H_INCLUDED
# define YY_PRIVATE_ENGINES_PRIVATE_TOKENS_H_INCLUDED
/* Debug traces. */
#ifndef PRIVATE_DEBUG
# if defined YYDEBUG
#if YYDEBUG
# define PRIVATE_DEBUG 1
# else
# define PRIVATE_DEBUG 0
# endif
# else /* ! defined YYDEBUG */
# define PRIVATE_DEBUG 0
# endif /* ! defined YYDEBUG */
#endif /* ! defined PRIVATE_DEBUG */
#if PRIVATE_DEBUG
extern int PRIVATE_debug;
#endif
/* Token kinds. */
#ifndef PRIVATE_TOKENTYPE
# define PRIVATE_TOKENTYPE
enum PRIVATE_tokentype
{
PRIVATE_EMPTY = -2,
PRIVATE_EOF = 0, /* "end of file" */
PRIVATE_error = 256, /* error */
PRIVATE_UNDEF = 257, /* "invalid token" */
NAME = 258, /* NAME */
STRING = 259, /* STRING */
NUM = 260, /* NUM */
NUM_PLUS = 261, /* NUM_PLUS */
LTE = 262, /* LTE */
GTE = 263, /* GTE */
NEQ = 264, /* NEQ */
EQ = 265, /* EQ */
FALSETOK = 266, /* FALSETOK */
TRUETOK = 267, /* TRUETOK */
NULLTOK = 268, /* NULLTOK */
IFTOK = 269, /* IFTOK */
ELSETOK = 270, /* ELSETOK */
RECT = 271, /* RECT */
GOTOTOK = 272, /* GOTOTOK */
DEBUGTOK = 273, /* DEBUGTOK */
EMITCODEONTOK = 274, /* EMITCODEONTOK */
EMITCODEOFFTOK = 275, /* EMITCODEOFFTOK */
RESETIDTOK = 276, /* RESETIDTOK */
DEFINETOK = 277, /* DEFINETOK */
SETTINGTOK = 278, /* SETTINGTOK */
RANDOMTOK = 279 /* RANDOMTOK */
};
typedef enum PRIVATE_tokentype PRIVATE_token_kind_t;
#endif
/* Value type. */
#if ! defined PRIVATE_STYPE && ! defined PRIVATE_STYPE_IS_DECLARED
union PRIVATE_STYPE
{
#line 81 "engines/private/grammar.y"
Private::Symbol *sym; /* symbol table pointer */
int (**inst)(); /* machine instruction */
char *s; /* string value */
int *i; /* integer value */
int narg; /* auxiliary value to count function arguments */
#line 104 "engines/private/tokens.h"
};
typedef union PRIVATE_STYPE PRIVATE_STYPE;
# define PRIVATE_STYPE_IS_TRIVIAL 1
# define PRIVATE_STYPE_IS_DECLARED 1
#endif
extern PRIVATE_STYPE PRIVATE_lval;
int PRIVATE_parse (void);
#endif /* !YY_PRIVATE_ENGINES_PRIVATE_TOKENS_H_INCLUDED */