scummvm/engines/dreamweb/monitor.cpp
2021-12-26 18:48:43 +01:00

784 lines
16 KiB
C++

/* 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 "dreamweb/sound.h"
#include "dreamweb/dreamweb.h"
namespace DreamWeb {
struct MonitorKeyEntry {
uint8 keyAssigned;
char username[12];
char password[12];
};
// New monitor key list
static MonitorKeyEntry monitorKeyEntries[4] = {
{ 1, "PUBLIC", "PUBLIC" },
{ 0, "RYAN", "BLACKDRAGON" },
{ 0, "LOUIS", "HENDRIX" },
{ 0, "BECKETT", "SEPTIMUS" }
};
void DreamWebEngine::useMon() {
_vars._lastTrigger = 0;
_currentFile[0] = 34;
memset(_currentFile+1, ' ', 12);
_currentFile[13] = 0;
monitorKeyEntries[0].keyAssigned = 1;
monitorKeyEntries[1].keyAssigned = 0;
monitorKeyEntries[2].keyAssigned = 0;
monitorKeyEntries[3].keyAssigned = 0;
createPanel();
showPanel();
showIcon();
drawFloor();
getRidOfAll();
loadGraphicsFile(_monitorGraphics, "G03"); // mon. graphic name
loadPersonal();
loadNews();
loadCart();
loadGraphicsFile(_monitorCharset, "C01"); // character set 2
printOuterMon();
initialMonCols();
printLogo();
workToScreen();
turnOnPower();
fadeUpYellows();
fadeUpMonFirst();
_monAdX = 76;
_monAdY = 141;
monMessage(1);
hangOnCurs(120);
monMessage(2);
randomAccess(60);
monMessage(3);
hangOnCurs(100);
printLogo();
scrollMonitor();
_bufferIn = 0;
_bufferOut = 0;
bool stop = false;
do {
uint16 oldMonadx = _monAdX;
uint16 oldMonady = _monAdY;
input();
_monAdX = oldMonadx;
_monAdY = oldMonady;
stop = execCommand();
if (_quitRequested) //TODO : Check why it crashes when put before the execcommand
break;
} while (!stop);
_monitorGraphics.clear();
_monitorCharset.clear();
_textFile1.clear();
_textFile2.clear();
_textFile3.clear();
_getBack = 1;
_sound->playChannel1(26);
_manIsOffScreen = 0;
restoreAll();
redrawMainScrn();
workToScreenM();
}
int DreamWebEngine::findCommand(const char *const cmdList[]) {
// Loop over all commands in the list and see if we get a match
int cmd = 0;
while (cmdList[cmd] != nullptr) {
const char *cmdStr = cmdList[cmd];
const char *inputStr = _inputLine;
// Compare the command, char by char, to see if we get a match.
// We only care about the prefix matching, though.
char inputChar, cmdChar;
do {
inputChar = *inputStr; inputStr += 2;
cmdChar = *cmdStr++;
if (cmdChar == 0)
return cmd;
} while (inputChar == cmdChar);
++cmd;
}
return -1;
}
bool DreamWebEngine::execCommand() {
static const char *const comlist[] = {
"EXIT",
"HELP",
"LIST",
"READ",
"LOGON",
"KEYS",
nullptr
};
static const char *const comlistFR[] = {
"SORTIR",
"AIDE",
"LISTE",
"LIRE",
"CONNEXION",
"TOUCHES", // should be CLES but it is translated as TOUCHES in the game...
nullptr
};
static const char *const comlistDE[] = {
"ENDE",
"HILF",
"LISTE",
"LIES",
"ZUGRIFF",
"DATEN",
nullptr
};
static const char *const comlistIT[] = {
"ESCI",
"AIUTO",
"ELENCA",
"LEGGI",
"ACCEDI",
"CHIAVI",
nullptr
};
static const char *const comlistES[] = {
"SALIR",
"AYUDA",
"LISTA",
"LEER",
"ACCESO",
"CLAVES",
nullptr
};
if (_inputLine[0] == 0) {
// No input
scrollMonitor();
return false;
}
int cmd = findCommand(comlist);
if (cmd == -1) {
// This did not match an english command. Try to find a localized one.
switch (getLanguage()) {
case Common::FR_FRA:
cmd = findCommand(comlistFR);
break;
case Common::DE_DEU:
cmd = findCommand(comlistDE);
break;
case Common::IT_ITA:
cmd = findCommand(comlistIT);
break;
case Common::ES_ESP:
cmd = findCommand(comlistES);
break;
default:
break;
}
}
// Execute the selected command
switch (cmd) {
case 0:
return true;
case 1:
monMessage(6);
// An extra addition in ScummVM: available commands.
// Since the reference to the game manual is a form of copy protection,
// this extra text is wrapped around the common copy protection check,
// to keep it faithful to the original, if requested.
if (!_copyProtection) {
switch (getLanguage()) {
case Common::FR_FRA:
monPrint("LES COMMANDES VALIDES SONT SORTIR, AIDE, LISTE, LIRE, CONNEXION, TOUCHES");
break;
case Common::DE_DEU:
monPrint("G\232LTIGE BEFEHLE SIND ENDE, HILFE, LISTE, LIES, ZUGRIFF, DATEN");
break;
case Common::IT_ITA:
monPrint("I COMANDI VALIDI SONO ESCI, AIUTO, ELENCA, LEGGI, ACCEDI, CHIAVI");
break;
case Common::ES_ESP:
default:
monPrint("VALID COMMANDS ARE EXIT, HELP, LIST, READ, LOGON, KEYS");
break;
}
}
break;
case 2:
dirCom();
break;
case 3:
read();
break;
case 4:
signOn();
break;
case 5:
showKeys();
break;
default:
netError();
break;
}
return false;
}
void DreamWebEngine::monitorLogo() {
if (_logoNum != _oldLogoNum) {
_oldLogoNum = _logoNum;
//fadeDownMon(); // FIXME: Commented out in ASM
printLogo();
printUnderMonitor();
workToScreen();
printLogo();
//fadeUpMon(); // FIXME: Commented out in ASM
printLogo();
_sound->playChannel1(26);
randomAccess(20);
} else {
printLogo();
}
}
void DreamWebEngine::printLogo() {
showFrame(_monitorGraphics, 56, 32, 0, 0);
showCurrentFile();
}
void DreamWebEngine::input() {
memset(_inputLine, 0, sizeof(_inputLine));
_curPos = 0;
printChar(_monitorCharset, _monAdX, _monAdY, '>', 0, nullptr, nullptr);
multiDump(_monAdX, _monAdY, 6, 8);
_monAdX += 6;
_cursLocX = _monAdX;
_cursLocY = _monAdY;
while (true) {
printCurs();
waitForVSync();
delCurs();
readKey();
if (_quitRequested)
return;
uint8 currentKey = _currentKey;
if (currentKey == 0)
continue;
if (currentKey == 13)
return;
if (currentKey == 8) {
if (_curPos > 0)
delChar();
continue;
}
if (_curPos == 28)
continue;
if ((currentKey == 32) && (_curPos == 0))
continue;
currentKey = makeCaps(currentKey);
_inputLine[_curPos * 2 + 0] = currentKey;
if (currentKey > 'Z')
continue;
multiGet(_mapStore + _curPos * 256, _monAdX, _monAdY, 8, 8);
uint8 charWidth;
printChar(_monitorCharset, _monAdX, _monAdY, currentKey, 0, &charWidth, nullptr);
_inputLine[_curPos * 2 + 1] = charWidth;
_monAdX += charWidth;
++_curPos;
_cursLocX += charWidth;
}
}
byte DreamWebEngine::makeCaps(byte c) {
// TODO: Replace calls to this by toupper() ?
if (c >= 'a')
c -= 'a' - 'A'; // = 32
return c;
}
void DreamWebEngine::delChar() {
--_curPos;
_inputLine[_curPos * 2] = 0;
uint8 width = _inputLine[_curPos * 2 + 1];
_monAdX -= width;
_cursLocX -= width;
uint16 offset = _curPos;
offset = ((offset & 0x00ff) << 8) | ((offset & 0xff00) >> 8);
multiPut(_mapStore + offset, _monAdX, _monAdY, 8, 8);
multiDump(_monAdX, _monAdY, 8, 8);
}
void DreamWebEngine::printCurs() {
uint16 x = _cursLocX;
uint16 y = _cursLocY;
uint16 height;
if (_foreignRelease) {
y -= 3;
height = 11;
} else
height = 8;
multiGet(_textUnder, x, y, 6, height);
++_mainTimer;
if ((_mainTimer & 16) == 0)
showFrame(_monitorCharset, x, y, '/' - 32, 0);
multiDump(x - (getLanguage() == Common::RU_RUS ? 7 : 6), y, 12, height);
}
void DreamWebEngine::delCurs() {
uint16 x = _cursLocX;
uint16 y = _cursLocY;
uint16 width = 6;
uint16 height;
if (_foreignRelease) {
y -= 3;
height = 11;
} else
height = 8;
multiPut(_textUnder, x, y, width, height);
multiDump(x, y, width, height);
}
void DreamWebEngine::scrollMonitor() {
printLogo();
printUnderMonitor();
workToScreen();
_sound->playChannel1(25);
}
void DreamWebEngine::showCurrentFile() {
uint16 x;
// Monitor Frame position differs between Floppy and CD version
if (isCD())
x = 178;
else
x = 199;
const char *currentFile = _currentFile + 1;
while (*currentFile) {
char c = *currentFile++;
c = modifyChar(c);
printChar(_monitorCharset, &x, 37, c, 0, nullptr, nullptr);
}
}
void DreamWebEngine::accessLightOn() {
showFrame(_monitorGraphics, 74, 182, 8, 0);
multiDump(74, 182, 12, 8);
}
void DreamWebEngine::accessLightOff() {
showFrame(_monitorGraphics, 74, 182, 7, 0);
multiDump(74, 182, 12, 8);
}
void DreamWebEngine::randomAccess(uint16 count) {
for (uint16 i = 0; i < count; ++i) {
waitForVSync();
waitForVSync();
uint16 v = _rnd.getRandomNumber(15);
if (v < 10)
accessLightOff();
else
accessLightOn();
}
accessLightOff();
}
void DreamWebEngine::monMessage(uint8 index) {
assert(index > 0);
const char *string = _textFile1._text;
for (uint8 i = 0; i < index; ++i) {
while (*string++ != '+') {
}
}
monPrint(string);
}
void DreamWebEngine::netError() {
monMessage(5);
scrollMonitor();
}
void DreamWebEngine::powerLightOn() {
showFrame(_monitorGraphics, 257+4, 182, 6, 0);
multiDump(257+4, 182, 12, 8);
}
void DreamWebEngine::powerLightOff() {
showFrame(_monitorGraphics, 257+4, 182, 5, 0);
multiDump(257+4, 182, 12, 8);
}
void DreamWebEngine::lockLightOn() {
showFrame(_monitorGraphics, 56, 182, 10, 0);
multiDump(58, 182, 12, 8);
}
void DreamWebEngine::lockLightOff() {
showFrame(_monitorGraphics, 56, 182, 9, 0);
multiDump(58, 182, 12, 8);
}
void DreamWebEngine::turnOnPower() {
for (uint i = 0; i < 3; ++i) {
powerLightOn();
hangOn(30);
powerLightOff();
hangOn(30);
}
powerLightOn();
}
void DreamWebEngine::printOuterMon() {
showFrame(_monitorGraphics, 40, 32, 1, 0);
showFrame(_monitorGraphics, 264, 32, 2, 0);
showFrame(_monitorGraphics, 40, 12, 3, 0);
showFrame(_monitorGraphics, 40, 164, 4, 0);
}
void DreamWebEngine::loadPersonal() {
if (_vars._location == 0 || _vars._location == 42)
loadTextFile(_textFile1, "T01"); // monitor file 1
else
loadTextFile(_textFile1, "T02"); // monitor file 2
}
void DreamWebEngine::loadNews() {
// textfile2 holds information accessible by anyone
if (_vars._newsItem == 0)
loadTextFile(_textFile2, "T10"); // monitor file 10
else if (_vars._newsItem == 1)
loadTextFile(_textFile2, "T11"); // monitor file 11
else if (_vars._newsItem == 2)
loadTextFile(_textFile2, "T12"); // monitor file 12
else
loadTextFile(_textFile2, "T13"); // monitor file 13
}
void DreamWebEngine::loadCart() {
byte cartridgeId = 0;
uint16 objectIndex = findSetObject("INTF");
uint16 cartridgeIndex = checkInside(objectIndex, 1);
if (cartridgeIndex != kNumexobjects)
cartridgeId = getExAd(cartridgeIndex)->objId[3] + 1;
if (cartridgeId == 0)
loadTextFile(_textFile3, "T20"); // monitor file 20
else if (cartridgeId == 1)
loadTextFile(_textFile3, "T21"); // monitor file 21
else if (cartridgeId == 2)
loadTextFile(_textFile3, "T22"); // monitor file 22
else if (cartridgeId == 3)
loadTextFile(_textFile3, "T23"); // monitor file 23
else
loadTextFile(_textFile3, "T24"); // monitor file 24
}
void DreamWebEngine::showKeys() {
randomAccess(10);
scrollMonitor();
monMessage(18);
for (int i = 0; i < 4; i++) {
if (monitorKeyEntries[i].keyAssigned)
monPrint(monitorKeyEntries[i].username);
}
scrollMonitor();
}
const char *DreamWebEngine::getKeyAndLogo(const char *foundString) {
byte newLogo = foundString[1] - 48;
byte keyNum = foundString[3] - 48;
if (monitorKeyEntries[keyNum].keyAssigned == 1) {
// Key OK
_logoNum = newLogo;
return foundString + 4;
} else {
monMessage(12); // "Access denied, key required -"
monPrint(monitorKeyEntries[keyNum].username);
scrollMonitor();
return nullptr;
}
}
const char *DreamWebEngine::searchForString(const char *topic, const char *text) {
char delim = *topic;
while (true) {
const char *s = topic;
int delimCount = 0;
char c;
do {
c = makeCaps(*text++);
if (c == '*' || (delim == '=' && c == 34))
return nullptr;
if (c == delim) {
delimCount++;
if (delimCount == 2)
return text;
}
} while (c == *s++);
}
}
void DreamWebEngine::dirCom() {
randomAccess(30);
const char *dirname = parser();
if (dirname[1]) {
dirFile(dirname);
return;
}
_logoNum = 0;
memcpy(_currentFile+1, "ROOT ", 12);
monitorLogo();
scrollMonitor();
monMessage(9);
searchForFiles(_textFile1._text);
searchForFiles(_textFile2._text);
searchForFiles(_textFile3._text);
scrollMonitor();
}
void DreamWebEngine::dirFile(const char *dirName) {
char topic[14];
memcpy(topic, dirName, 14);
topic[0] = 34;
const char *text = _textFile1._text;
const char *found = searchForString(topic, text);
if (!found) {
text = _textFile2._text;
found = searchForString(topic, text);
if (!found) {
text = _textFile3._text;
found = searchForString(topic, text);
}
}
if (found) {
found = getKeyAndLogo(found);
if (!found)
return; // not logged in
} else {
monMessage(7);
return;
}
// "keyok2"
memcpy(_currentFile+1, dirName+1, 12);
monitorLogo();
scrollMonitor();
monMessage(10);
while (true) {
byte curChar = *found++;
if (curChar == 34 || curChar == '*') {
// "endofdir2"
scrollMonitor();
return;
}
if (curChar == '=')
found = monPrint(found);
}
}
void DreamWebEngine::read() {
randomAccess(40);
const char *name = parser();
if (name[1] == 0) {
netError();
return;
}
const char *topic = _currentFile;
const char *text = _textFile1._text;
const char *found = searchForString(topic, text);
if (!found) {
text = _textFile2._text;
found = searchForString(topic, text);
if (!found) {
text = _textFile3._text;
found = searchForString(topic, text);
}
}
if (found) {
if (!getKeyAndLogo(found))
return;
} else {
monMessage(7);
return;
}
// "keyok1"
found = searchForString(name, found);
if (!found) {
_logoNum = _oldLogoNum;
monMessage(11);
return;
}
// "findtopictext"
monitorLogo();
scrollMonitor();
found++;
while (true) {
found = monPrint(found);
if (found[0] == 34 || found[0] == '=' || found[0] == '*') {
// "endoftopic"
scrollMonitor();
return;
}
processTrigger();
randomAccess(24);
}
}
void DreamWebEngine::signOn() {
const char *name = parser();
int8 foundIndex = -1;
Common::String inputLine = name + 1;
inputLine.trim();
for (byte i = 0; i < 4; i++) {
if (inputLine.equalsIgnoreCase(monitorKeyEntries[i].username)) {
// Check if the key has already been assigned
if (monitorKeyEntries[i].keyAssigned) {
monMessage(17);
return;
} else {
foundIndex = i;
break;
}
}
}
if (foundIndex == -1) {
monMessage(13);
return;
}
monMessage(15);
uint16 prevX = _monAdX;
uint16 prevY = _monAdY;
input(); // password input
_monAdX = prevX;
_monAdY = prevY;
// The entered line has zeroes in-between each character
uint32 len = strlen(monitorKeyEntries[foundIndex].password);
bool found = true;
for (uint32 i = 0; i < len; i++) {
if (monitorKeyEntries[foundIndex].password[i] != _inputLine[i * 2]) {
found = false;
break;
}
}
if (!found) {
scrollMonitor();
monMessage(16);
} else {
monMessage(14);
monPrint(monitorKeyEntries[foundIndex].username);
scrollMonitor();
monitorKeyEntries[foundIndex].keyAssigned = 1;
}
}
void DreamWebEngine::searchForFiles(const char *filesString) {
byte curChar;
while (true) {
curChar = filesString[0];
filesString++;
if (curChar == '*')
return; // "endofdir"
if (curChar == 34)
filesString = monPrint(filesString);
}
}
const char *DreamWebEngine::parser() {
char *output = _operand1;
memset(output, 0, sizeof(_operand1));
*output++ = '=';
const char *in = _inputLine;
uint8 c;
// skip command
do {
c = *in++;
in++;
if (!c)
return output;
} while (c != 32);
// skip spaces between command and operand
do {
c = *in++;
in++;
} while (c == 32);
// copy first operand
do {
*output++ = c;
c = *in++;
in++;
if (!c)
return _operand1;
} while (c != 32);
return _operand1;
}
} // End of namespace DreamWeb