1
2 /*
3 * REminiscence - Flashback interpreter
4 * Copyright (C) 2005-2019 Gregory Montoir (cyx@users.sourceforge.net)
5 */
6
7 #include "game.h"
8 #include "screenshot.h"
9 #include "systemstub.h"
10
reverseBits(uint8_t ch)11 static uint8_t reverseBits(uint8_t ch) {
12 static const uint8_t lut[] = {
13 0x0, 0x8, 0x4, 0xC, 0x2, 0xA, 0x6, 0xE,
14 0x1, 0x9, 0x5, 0xD, 0x3, 0xB, 0x7, 0xF
15 };
16 return (lut[ch & 15] << 4) | lut[ch >> 4];
17 }
18
decryptChar(uint8_t ch)19 static uint8_t decryptChar(uint8_t ch) {
20 return reverseBits(ch ^ 0x55);
21 }
22
encryptChar(uint8_t ch)23 static uint8_t encryptChar(uint8_t ch) {
24 return reverseBits(ch) ^ 0x55;
25 }
26
handleProtectionScreenShape()27 bool Game::handleProtectionScreenShape() {
28 bool valid = false;
29 _cut.prepare();
30 const int palOffset = _res.isAmiga() ? 32 : 0;
31 _cut.copyPalette(_protectionPal + palOffset, 0);
32
33 _cut.updatePalette();
34 _cut._gfx.setClippingRect(64, 48, 128, 128);
35
36 _menu._charVar1 = 0xE0;
37 _menu._charVar2 = 0xEF;
38 _menu._charVar4 = 0xE5;
39 _menu._charVar5 = 0xE2;
40
41 // 5 codes per shape (a code is 6 characters long)
42 if (0) {
43 for (int shape = 0; shape < 30; ++shape) {
44 fprintf(stdout, "Shape #%2d\n", shape);
45 for (int code = 0; code < 5; ++code) {
46 const int offset = (shape * 5 + code) * 6;
47 if (_res.isAmiga()) {
48 fprintf(stdout, "\t ");
49 for (int i = 0; i < 6; ++i) {
50 const char chr = _protectionNumberDataAmiga[(shape * 5 + code) * 6 + i] ^ 0xD7;
51 fprintf(stdout, "%c", chr);
52 }
53 fprintf(stdout, " : ");
54 for (int i = 0; i < 6; ++i) {
55 fprintf(stdout, "%c", _protectionCodeDataAmiga[offset + i] ^ 0xD7);
56 }
57 } else {
58 fprintf(stdout, "\t code %d : ", code + 1);
59 for (int i = 0; i < 6; ++i) {
60 fprintf(stdout, "%c", decryptChar(_protectionCodeData[offset + i]));
61 }
62 }
63 fprintf(stdout, "\n");
64 }
65 }
66 }
67 if (0) {
68 uint8_t palette[256 * 3];
69 _stub->getPalette(palette, 256);
70 for (int shape = 0; shape < 30; ++shape) {
71 _cut.drawProtectionShape(shape, 0);
72 char fname[32];
73 snprintf(fname, sizeof(fname), "shape_%02d.bmp", shape);
74 saveBMP(fname, _vid._tempLayer, palette, _vid._w, _vid._h);
75 }
76 }
77 const int shapeNum = getRandomNumber() % 30;
78 const int codeNum = getRandomNumber() % 5;
79 for (int16_t zoom = 2000; zoom >= 0; zoom -= 100) {
80 _cut.drawProtectionShape(shapeNum, zoom);
81 _stub->copyRect(0, 0, _vid._w, _vid._h, _vid._tempLayer, _vid._w);
82 _stub->updateScreen(0);
83 _stub->sleep(30);
84 }
85 _vid.setTextPalette();
86
87 char codeNumber[8];
88 if (_res.isAmiga()) {
89 static const uint8_t kNumberLen = 6;
90 for (int i = 0; i < kNumberLen; ++i) {
91 codeNumber[i] = _protectionNumberDataAmiga[(shapeNum * 5 + codeNum) * kNumberLen + i] ^ 0xD7;
92 }
93 codeNumber[kNumberLen] = 0;
94 } else {
95 snprintf(codeNumber, sizeof(codeNumber), "CODE %d", codeNum + 1);
96 }
97
98 static const int kCodeLen = 6;
99 char codeText[kCodeLen + 1];
100 int len = 0;
101 do {
102 codeText[len] = '\0';
103 memcpy(_vid._frontLayer, _vid._tempLayer, _vid._layerSize);
104 _vid.drawString("PROTECTION", 11 * Video::CHAR_W, 2 * Video::CHAR_H, _menu._charVar2);
105 char buf[32];
106 snprintf(buf, sizeof(buf), "%s : %s", codeNumber, codeText);
107 _vid.drawString(buf, 8 * Video::CHAR_W, 23 * Video::CHAR_H, _menu._charVar2);
108 _vid.updateScreen();
109 _stub->sleep(50);
110 _stub->processEvents();
111 char c = _stub->_pi.lastChar;
112 if (c != 0) {
113 _stub->_pi.lastChar = 0;
114 if (len < kCodeLen) {
115 if ((c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9')) {
116 codeText[len] = c;
117 ++len;
118 }
119 }
120 }
121 if (_stub->_pi.backspace) {
122 _stub->_pi.backspace = false;
123 if (len > 0) {
124 --len;
125 }
126 }
127 if (_stub->_pi.enter) {
128 _stub->_pi.enter = false;
129 if (len > 0) {
130 int charsCount = 0;
131 if (_res.isAmiga()) {
132 const uint8_t *p = _protectionCodeDataAmiga + (shapeNum * 5 + codeNum) * kCodeLen;
133 for (int i = 0; i < len && (codeText[i] ^ 0xD7) == p[i]; ++i) {
134 ++charsCount;
135 }
136 } else {
137 const uint8_t *p = _protectionCodeData + (shapeNum * 5 + codeNum) * kCodeLen;
138 for (int i = 0; i < len && encryptChar(codeText[i]) == p[i]; ++i) {
139 ++charsCount;
140 }
141 }
142 valid = (charsCount == kCodeLen);
143 break;
144 }
145 }
146 } while (!_stub->_pi.quit);
147 _vid.fadeOut();
148 return valid;
149 }
150
handleProtectionScreenWords()151 bool Game::handleProtectionScreenWords() {
152 bool valid = false;
153 static const int kWordsCount = 40;
154 if (0) {
155 for (int i = 0; i < kWordsCount * 18; i += 18) {
156 const uint8_t *data = _protectionWordData + i;
157 fprintf(stdout, "page %d column %d line %2d word %d : ", data[0], data[1], data[2], data[3]);
158 for (int j = 4; j < 18; ++j) {
159 const uint8_t ch = decryptChar(data[j]);
160 if (!(ch >= 'A' && ch <= 'Z')) {
161 break;
162 }
163 fprintf(stdout, "%c", ch);
164 }
165 fprintf(stdout, "\n");
166 }
167 }
168
169 _vid.setTextPalette();
170 _vid.setPalette0xF();
171
172 memset(_vid._frontLayer, 0, _vid._layerSize);
173
174 static const char *kText[] = {
175 "Enter the word found in the",
176 "following location in your",
177 "rulebook. (Do not count the",
178 "title header that appears on",
179 "all pages. Ignore captions",
180 "and header).",
181 0
182 };
183 for (int i = 0; kText[i]; ++i) {
184 _vid.drawString(kText[i], 24, 16 + i * Video::CHAR_H, 0xE5);
185 }
186 static const int icon_spr_w = 16;
187 static const int icon_spr_h = 16;
188 int icon_num = 31;
189 for (int y = 140; y < 140 + 5 * icon_spr_h; y += icon_spr_h) {
190 for (int x = 56; x < 56 + 9 * icon_spr_w; x += icon_spr_w) {
191 drawIcon(icon_num, x, y, 0xF);
192 ++icon_num;
193 }
194 }
195
196 const uint8_t code = getRandomNumber() % kWordsCount;
197 const uint8_t *protectionData = _protectionWordData + code * 18;
198
199 static const char *kSecurityCodeText = "SECURITY CODE";
200 _vid.drawString(kSecurityCodeText, 72 + (114 - strlen(kSecurityCodeText) * 8) / 2, 158, 0xE4);
201 char buf[16];
202 snprintf(buf, sizeof(buf), "PAGE %d", protectionData[0]);
203 _vid.drawString(buf, 69, 189, 0xE5);
204 snprintf(buf, sizeof(buf), "COLUMN %d", protectionData[1]);
205 _vid.drawString(buf, 69, 197, 0xE5);
206 snprintf(buf, sizeof(buf), "LINE %d", protectionData[2]);
207 _vid.drawString(buf, (protectionData[2] < 10) ? 141 : 133, 189, 0xE5);
208 snprintf(buf, sizeof(buf), "WORD %d", protectionData[3]);
209 _vid.drawString(buf, 141, 197, 0xE5);
210
211 memcpy(_vid._tempLayer, _vid._frontLayer, _vid._layerSize);
212
213 static const int kCodeLen = 14;
214 char codeText[kCodeLen + 1];
215 int len = 0;
216 do {
217 memcpy(_vid._frontLayer, _vid._tempLayer, _vid._layerSize);
218 codeText[len] = '\0';
219 _vid.drawString(codeText, 72 + (114 - strlen(codeText) * 8) / 2, 166, 0xE3);
220 _vid.updateScreen();
221 _stub->sleep(50);
222 _stub->processEvents();
223 char c = _stub->_pi.lastChar;
224 if (c != 0) {
225 _stub->_pi.lastChar = 0;
226 if (len < kCodeLen) {
227 if (c >= 'A' && c <= 'Z') {
228 codeText[len] = c;
229 ++len;
230 }
231 }
232 }
233 if (_stub->_pi.backspace) {
234 _stub->_pi.backspace = false;
235 if (len > 0) {
236 --len;
237 }
238 }
239 if (_stub->_pi.enter) {
240 _stub->_pi.enter = false;
241 if (len > 0) {
242 int charsCount = 0;
243 for (int i = 0; i < len; ++i) {
244 if (encryptChar(codeText[i]) != protectionData[4 + i]) {
245 break;
246 }
247 ++charsCount;
248 }
249 // words are padded with spaces
250 valid = decryptChar(protectionData[4 + charsCount]) == 0x20;
251 }
252 }
253 } while (!_stub->_pi.quit);
254 _vid.fadeOut();
255 return valid;
256 }
257
258