REminiscence/protection.cpp

258 lines
7.0 KiB
C++

/*
* 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) {
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];
}
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;
static const char *kSecurityCodeText = "SECURITY CODE";
_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;
}