2019-10-27 17:00:00 +01:00
|
|
|
|
|
|
|
/*
|
|
|
|
* REminiscence - Flashback interpreter
|
|
|
|
* Copyright (C) 2005-2019 Gregory Montoir (cyx@users.sourceforge.net)
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "game.h"
|
|
|
|
#include "screenshot.h"
|
|
|
|
#include "systemstub.h"
|
|
|
|
|
|
|
|
static uint8_t reverseBits(uint8_t ch) {
|
2019-12-28 17:00:00 +01:00
|
|
|
static const uint8_t lut[] = {
|
|
|
|
0x0, 0x8, 0x4, 0xC, 0x2, 0xA, 0x6, 0xE,
|
|
|
|
0x1, 0x9, 0x5, 0xD, 0x3, 0xB, 0x7, 0xF
|
|
|
|
};
|
|
|
|
return (lut[ch & 15] << 4) | lut[ch >> 4];
|
2019-10-27 17:00:00 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
static uint8_t decryptChar(uint8_t ch) {
|
|
|
|
return reverseBits(ch ^ 0x55);
|
|
|
|
}
|
|
|
|
|
|
|
|
static uint8_t encryptChar(uint8_t ch) {
|
|
|
|
return reverseBits(ch) ^ 0x55;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool Game::handleProtectionScreenShape() {
|
|
|
|
bool valid = false;
|
|
|
|
_cut.prepare();
|
|
|
|
const int palOffset = _res.isAmiga() ? 32 : 0;
|
|
|
|
_cut.copyPalette(_protectionPal + palOffset, 0);
|
|
|
|
|
|
|
|
_cut.updatePalette();
|
|
|
|
_cut._gfx.setClippingRect(64, 48, 128, 128);
|
|
|
|
|
|
|
|
_menu._charVar1 = 0xE0;
|
|
|
|
_menu._charVar2 = 0xEF;
|
|
|
|
_menu._charVar4 = 0xE5;
|
|
|
|
_menu._charVar5 = 0xE2;
|
|
|
|
|
|
|
|
// 5 codes per shape (a code is 6 characters long)
|
|
|
|
if (0) {
|
|
|
|
for (int shape = 0; shape < 30; ++shape) {
|
|
|
|
fprintf(stdout, "Shape #%2d\n", shape);
|
|
|
|
for (int code = 0; code < 5; ++code) {
|
|
|
|
const int offset = (shape * 5 + code) * 6;
|
|
|
|
if (_res.isAmiga()) {
|
|
|
|
fprintf(stdout, "\t ");
|
|
|
|
for (int i = 0; i < 6; ++i) {
|
|
|
|
const char chr = _protectionNumberDataAmiga[(shape * 5 + code) * 6 + i] ^ 0xD7;
|
|
|
|
fprintf(stdout, "%c", chr);
|
|
|
|
}
|
|
|
|
fprintf(stdout, " : ");
|
|
|
|
for (int i = 0; i < 6; ++i) {
|
|
|
|
fprintf(stdout, "%c", _protectionCodeDataAmiga[offset + i] ^ 0xD7);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
fprintf(stdout, "\t code %d : ", code + 1);
|
|
|
|
for (int i = 0; i < 6; ++i) {
|
|
|
|
fprintf(stdout, "%c", decryptChar(_protectionCodeData[offset + i]));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
fprintf(stdout, "\n");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (0) {
|
|
|
|
uint8_t palette[256 * 3];
|
|
|
|
_stub->getPalette(palette, 256);
|
|
|
|
for (int shape = 0; shape < 30; ++shape) {
|
|
|
|
_cut.drawProtectionShape(shape, 0);
|
|
|
|
char fname[32];
|
|
|
|
snprintf(fname, sizeof(fname), "shape_%02d.bmp", shape);
|
|
|
|
saveBMP(fname, _vid._tempLayer, palette, _vid._w, _vid._h);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
const int shapeNum = getRandomNumber() % 30;
|
|
|
|
const int codeNum = getRandomNumber() % 5;
|
|
|
|
for (int16_t zoom = 2000; zoom >= 0; zoom -= 100) {
|
|
|
|
_cut.drawProtectionShape(shapeNum, zoom);
|
|
|
|
_stub->copyRect(0, 0, _vid._w, _vid._h, _vid._tempLayer, _vid._w);
|
|
|
|
_stub->updateScreen(0);
|
|
|
|
_stub->sleep(30);
|
|
|
|
}
|
|
|
|
_vid.setTextPalette();
|
|
|
|
|
|
|
|
char codeNumber[8];
|
|
|
|
if (_res.isAmiga()) {
|
|
|
|
static const uint8_t kNumberLen = 6;
|
|
|
|
for (int i = 0; i < kNumberLen; ++i) {
|
|
|
|
codeNumber[i] = _protectionNumberDataAmiga[(shapeNum * 5 + codeNum) * kNumberLen + i] ^ 0xD7;
|
|
|
|
}
|
|
|
|
codeNumber[kNumberLen] = 0;
|
|
|
|
} else {
|
|
|
|
snprintf(codeNumber, sizeof(codeNumber), "CODE %d", codeNum + 1);
|
|
|
|
}
|
|
|
|
|
|
|
|
static const int kCodeLen = 6;
|
|
|
|
char codeText[kCodeLen + 1];
|
|
|
|
int len = 0;
|
|
|
|
do {
|
|
|
|
codeText[len] = '\0';
|
|
|
|
memcpy(_vid._frontLayer, _vid._tempLayer, _vid._layerSize);
|
|
|
|
_vid.drawString("PROTECTION", 11 * Video::CHAR_W, 2 * Video::CHAR_H, _menu._charVar2);
|
|
|
|
char buf[32];
|
|
|
|
snprintf(buf, sizeof(buf), "%s : %s", codeNumber, codeText);
|
|
|
|
_vid.drawString(buf, 8 * Video::CHAR_W, 23 * Video::CHAR_H, _menu._charVar2);
|
|
|
|
_vid.updateScreen();
|
|
|
|
_stub->sleep(50);
|
|
|
|
_stub->processEvents();
|
|
|
|
char c = _stub->_pi.lastChar;
|
|
|
|
if (c != 0) {
|
|
|
|
_stub->_pi.lastChar = 0;
|
|
|
|
if (len < kCodeLen) {
|
|
|
|
if ((c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9')) {
|
|
|
|
codeText[len] = c;
|
|
|
|
++len;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (_stub->_pi.backspace) {
|
|
|
|
_stub->_pi.backspace = false;
|
|
|
|
if (len > 0) {
|
|
|
|
--len;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (_stub->_pi.enter) {
|
|
|
|
_stub->_pi.enter = false;
|
|
|
|
if (len > 0) {
|
|
|
|
int charsCount = 0;
|
|
|
|
if (_res.isAmiga()) {
|
|
|
|
const uint8_t *p = _protectionCodeDataAmiga + (shapeNum * 5 + codeNum) * kCodeLen;
|
|
|
|
for (int i = 0; i < len && (codeText[i] ^ 0xD7) == p[i]; ++i) {
|
|
|
|
++charsCount;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
const uint8_t *p = _protectionCodeData + (shapeNum * 5 + codeNum) * kCodeLen;
|
|
|
|
for (int i = 0; i < len && encryptChar(codeText[i]) == p[i]; ++i) {
|
|
|
|
++charsCount;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
valid = (charsCount == kCodeLen);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} while (!_stub->_pi.quit);
|
|
|
|
_vid.fadeOut();
|
|
|
|
return valid;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool Game::handleProtectionScreenWords() {
|
|
|
|
bool valid = false;
|
|
|
|
static const int kWordsCount = 40;
|
|
|
|
if (0) {
|
|
|
|
for (int i = 0; i < kWordsCount * 18; i += 18) {
|
|
|
|
const uint8_t *data = _protectionWordData + i;
|
|
|
|
fprintf(stdout, "page %d column %d line %2d word %d : ", data[0], data[1], data[2], data[3]);
|
|
|
|
for (int j = 4; j < 18; ++j) {
|
|
|
|
const uint8_t ch = decryptChar(data[j]);
|
|
|
|
if (!(ch >= 'A' && ch <= 'Z')) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
fprintf(stdout, "%c", ch);
|
|
|
|
}
|
|
|
|
fprintf(stdout, "\n");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
_vid.setTextPalette();
|
|
|
|
_vid.setPalette0xF();
|
|
|
|
|
|
|
|
memset(_vid._frontLayer, 0, _vid._layerSize);
|
|
|
|
|
|
|
|
static const char *kText[] = {
|
|
|
|
"Enter the word found in the",
|
|
|
|
"following location in your",
|
|
|
|
"rulebook. (Do not count the",
|
|
|
|
"title header that appears on",
|
|
|
|
"all pages. Ignore captions",
|
|
|
|
"and header).",
|
|
|
|
0
|
|
|
|
};
|
|
|
|
for (int i = 0; kText[i]; ++i) {
|
|
|
|
_vid.drawString(kText[i], 24, 16 + i * Video::CHAR_H, 0xE5);
|
|
|
|
}
|
|
|
|
static const int icon_spr_w = 16;
|
|
|
|
static const int icon_spr_h = 16;
|
|
|
|
int icon_num = 31;
|
|
|
|
for (int y = 140; y < 140 + 5 * icon_spr_h; y += icon_spr_h) {
|
|
|
|
for (int x = 56; x < 56 + 9 * icon_spr_w; x += icon_spr_w) {
|
|
|
|
drawIcon(icon_num, x, y, 0xF);
|
|
|
|
++icon_num;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
const uint8_t code = getRandomNumber() % kWordsCount;
|
|
|
|
const uint8_t *protectionData = _protectionWordData + code * 18;
|
|
|
|
|
2019-12-28 17:00:00 +01:00
|
|
|
static const char *kSecurityCodeText = "SECURITY CODE";
|
2019-10-27 17:00:00 +01:00
|
|
|
_vid.drawString(kSecurityCodeText, 72 + (114 - strlen(kSecurityCodeText) * 8) / 2, 158, 0xE4);
|
|
|
|
char buf[16];
|
|
|
|
snprintf(buf, sizeof(buf), "PAGE %d", protectionData[0]);
|
|
|
|
_vid.drawString(buf, 69, 189, 0xE5);
|
|
|
|
snprintf(buf, sizeof(buf), "COLUMN %d", protectionData[1]);
|
|
|
|
_vid.drawString(buf, 69, 197, 0xE5);
|
|
|
|
snprintf(buf, sizeof(buf), "LINE %d", protectionData[2]);
|
|
|
|
_vid.drawString(buf, (protectionData[2] < 10) ? 141 : 133, 189, 0xE5);
|
|
|
|
snprintf(buf, sizeof(buf), "WORD %d", protectionData[3]);
|
|
|
|
_vid.drawString(buf, 141, 197, 0xE5);
|
|
|
|
|
|
|
|
memcpy(_vid._tempLayer, _vid._frontLayer, _vid._layerSize);
|
|
|
|
|
|
|
|
static const int kCodeLen = 14;
|
|
|
|
char codeText[kCodeLen + 1];
|
|
|
|
int len = 0;
|
|
|
|
do {
|
|
|
|
memcpy(_vid._frontLayer, _vid._tempLayer, _vid._layerSize);
|
|
|
|
codeText[len] = '\0';
|
|
|
|
_vid.drawString(codeText, 72 + (114 - strlen(codeText) * 8) / 2, 166, 0xE3);
|
|
|
|
_vid.updateScreen();
|
|
|
|
_stub->sleep(50);
|
|
|
|
_stub->processEvents();
|
|
|
|
char c = _stub->_pi.lastChar;
|
|
|
|
if (c != 0) {
|
|
|
|
_stub->_pi.lastChar = 0;
|
|
|
|
if (len < kCodeLen) {
|
|
|
|
if (c >= 'A' && c <= 'Z') {
|
|
|
|
codeText[len] = c;
|
|
|
|
++len;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (_stub->_pi.backspace) {
|
|
|
|
_stub->_pi.backspace = false;
|
|
|
|
if (len > 0) {
|
|
|
|
--len;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (_stub->_pi.enter) {
|
|
|
|
_stub->_pi.enter = false;
|
|
|
|
if (len > 0) {
|
|
|
|
int charsCount = 0;
|
|
|
|
for (int i = 0; i < len; ++i) {
|
|
|
|
if (encryptChar(codeText[i]) != protectionData[4 + i]) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
++charsCount;
|
|
|
|
}
|
|
|
|
// words are padded with spaces
|
|
|
|
valid = decryptChar(protectionData[4 + charsCount]) == 0x20;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} while (!_stub->_pi.quit);
|
|
|
|
_vid.fadeOut();
|
|
|
|
return valid;
|
|
|
|
}
|
|
|
|
|