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