Import 0.4.5

This commit is contained in:
Gregory Montoir 2019-10-28 00:00:00 +08:00
parent 222984d851
commit 86baaa3a9a
51 changed files with 2084 additions and 543 deletions

139
CHANGES.txt Normal file
View File

@ -0,0 +1,139 @@
* release 0.4.5
- added low-pass filtering for in-game music
- added support for 3DO background music (tunes/*.Cpc)
- added protection screen for DOS SSI version
- fixed ASC and CARTE cutscenes enablement
- fixed loudness of sound effects and in-game music
* release 0.4.4
- added auto-save
- fixed graphical glitches with Macintosh data files
* release 0.4.3
- added title and logo screens for Macintosh
- fixed cutscene offsets with Macintosh data files
* release 0.4.2
- added graphic borders (16:9 displays)
- added detection for Macintosh AppleDouble
- added language selection in title screen
- added story texts for Macintosh
* release 0.4.1
- added support for Macintosh sounds playback
- improved graphics scalers performance
- fixed palette glitches with .BNQ DOS data files
* release 0.4.0
- added initial support for Macintosh data files (512x448 resolution)
- added configuration file entries for disabled cutscenes
- fixed screen shaking offset (level 2)
* release 0.3.7
- added 'caillou-f.set' cutscene
- added detection for 'fbdemofr.zip' DOS demo data files
- fixed cutscene text offsets with Japanese language
* release 0.3.6
- added cutscene music looping
- added 'DEMO' to title menu
- fixed potential crash with the 'walk through walls' glitch
* release 0.3.5
- added Japanese in-game texts
- added support for localized .TBN (PC-CD)
* release 0.3.4
- added screenshot for SDL2
- added support for external graphics scalers (xbrz, scale2x)
- fixed keypress detection for fullscreen/windowed
* release 0.3.3
- added configuration for disabling .SEQ playback
- fixed some Amiga palette glitches
- fixed protection screen for Amiga
* release 0.3.2
- added support for DOS demo*.bin files
- added detection for DOS SSI version
- added compilation with SDL2
* release 0.3.1
- added workarounds for cutscene glitches present in the game (espions, voyage)
- fixed DOS credits sequence
- fixed Amiga cutscene captions display
- fixed Amiga 24px sprites
* release 0.3.0
- added configuration file
- added optional playback for disabled cutscenes (asc, metro, serrure)
- added support for DOS demo data file (DEMO_UK.ABA)
- set frame rate to 30Hz
* release 0.2.2
- added support for level background music
- added italian texts
- fixed PC-CD SEQ cutscenes numbering
- fixed several issues with Amiga data files
* release 0.2.1 (2011/03/15)
- added music playback to PC-CD SEQ cutscenes
- fixed some palette and sprite issues with Amiga data
* release 0.2.0 (2011/03/11)
- added support for PC-CD SEQ cutscenes
- added support for Amiga data files (experimental)
- fixed minor sound glitches
* release 0.1.9 (2007/03/16)
- fixed minor glitches in cutscenes
- fixed several Conrad moves
- fixed fast mode
- made game version autodetection defaulting to English
- added support for SegaCD speech files
* release 0.1.8 (2005/08/31)
- fixed crash in MOD player
- fixed minor glitch with in-game save switches
* release 0.1.7 (2005/08/24)
- improved in-game menu
- added autodetection for language of the game
- added support for Amiga music files
- added support for the protection screen (disabled by default)
* release 0.1.6 (2005/06/05)
- added in-game menu
- fixed remaining graphical glitches
* release 0.1.5 (2005/04/05)
- added spanish and german versions support
- added input keys recording
- reduced memory usage
* release 0.2.0 (2005/04/02)
- added screen shaking (level 2)
- added support for Amiga music (experimental)
- fixed screen refresh after teleportation
- fixed bug in savestate restore
* release 0.1.3 (2005/02/15)
- added dirty blocks rendering
- added save/load states
- fixed issues with level 2 cutscenes (subway maps, mission screens)
- fixed glitch in continue/abort screen
* release 0.1.2 (2005/02/07)
- added sound effects playback
- added support for polygonal cutscenes
- added support for final credits sequence
- fixed instructions screen display in english version
* release 0.1.1 (2005/01/29)
- added missing opcodes, game should now be completable
- added scale2x and scale3x filters
- enabled level switching
- fixed several engine bugs
* release 0.1.0 (2005/01/23)
- first public release

View File

@ -9,12 +9,12 @@ ZLIB_LIBS := -lz
CXXFLAGS += -Wall -Wpedantic -MMD $(SDL_CFLAGS) -DUSE_MODPLUG -DUSE_STATIC_SCALER -DUSE_TREMOR -DUSE_ZLIB CXXFLAGS += -Wall -Wpedantic -MMD $(SDL_CFLAGS) -DUSE_MODPLUG -DUSE_STATIC_SCALER -DUSE_TREMOR -DUSE_ZLIB
SRCS = collision.cpp cutscene.cpp decode_mac.cpp dynlib.cpp file.cpp fs.cpp game.cpp graphics.cpp main.cpp \ SRCS = collision.cpp cpc_player.cpp cutscene.cpp decode_mac.cpp dynlib.cpp file.cpp fs.cpp game.cpp graphics.cpp main.cpp \
menu.cpp mixer.cpp mod_player.cpp ogg_player.cpp piege.cpp resource.cpp resource_aba.cpp \ menu.cpp mixer.cpp mod_player.cpp ogg_player.cpp piege.cpp protection.cpp resource.cpp resource_aba.cpp \
resource_mac.cpp scaler.cpp screenshot.cpp seq_player.cpp \ resource_mac.cpp scaler.cpp screenshot.cpp seq_player.cpp \
sfx_player.cpp staticres.cpp systemstub_sdl.cpp unpack.cpp util.cpp video.cpp sfx_player.cpp staticres.cpp systemstub_sdl.cpp unpack.cpp util.cpp video.cpp
SCALERS := scalers/scaler_nearest.cpp scalers/scaler_tv2x.cpp scalers/scaler_xbrz.cpp scalers/xbrz/xbrz.cpp SCALERS := scalers/scaler_nearest.cpp scalers/scaler_tv2x.cpp scalers/scaler_xbr.cpp
OBJS = $(SRCS:.cpp=.o) $(SCALERS:.cpp=.o) OBJS = $(SRCS:.cpp=.o) $(SCALERS:.cpp=.o)
DEPS = $(SRCS:.cpp=.d) $(SCALERS:.cpp=.d) DEPS = $(SRCS:.cpp=.d) $(SCALERS:.cpp=.d)
@ -25,6 +25,6 @@ rs: $(OBJS)
$(CXX) $(LDFLAGS) -o $@ $(OBJS) $(LIBS) $(CXX) $(LDFLAGS) -o $@ $(OBJS) $(LIBS)
clean: clean:
rm -f *.o *.d rm -f $(OBJS) $(DEPS)
-include $(DEPS) -include $(DEPS)

View File

@ -1,6 +1,6 @@
REminiscence README REminiscence README
Release version: 0.4.4 Release version: 0.4.5
------------------------------------------------------------------------------- -------------------------------------------------------------------------------
@ -18,7 +18,7 @@ Data Files:
You will need the original files of the PC (DOS or CD), Amiga or Macintosh You will need the original files of the PC (DOS or CD), Amiga or Macintosh
release. Support for Amiga and Macintosh is still experimental. release. Support for Amiga and Macintosh is still experimental.
For the Macintosh release, the resource fork must dumped as a file named For the Macintosh release, the resource fork must be dumped as a file named
'FLASHBACK.BIN' (MacBinary) or 'FLASHBACK.RSRC' (AppleDouble). 'FLASHBACK.BIN' (MacBinary) or 'FLASHBACK.RSRC' (AppleDouble).
To hear music during polygonal cutscenes with the PC version, you need to copy To hear music during polygonal cutscenes with the PC version, you need to copy
@ -43,7 +43,7 @@ These paths can be changed using command line switches :
--savepath=PATH Path to save files (default '.') --savepath=PATH Path to save files (default '.')
--levelnum=NUM Level to start from (default '0') --levelnum=NUM Level to start from (default '0')
--fullscreen Fullscreen display --fullscreen Fullscreen display
--widescreen=MODE 16:9 display --widescreen=MODE 16:9 display (adjacent,mirror,blur,none)
--scaler=NAME@X Graphics scaler (default 'scale@3') --scaler=NAME@X Graphics scaler (default 'scale@3')
--language=LANG Language (fr,en,de,sp,it,jp) --language=LANG Language (fr,en,de,sp,it,jp)
--autosave Save game state automatically --autosave Save game state automatically

View File

@ -1,7 +1,7 @@
/* /*
* REminiscence - Flashback interpreter * REminiscence - Flashback interpreter
* Copyright (C) 2005-2018 Gregory Montoir (cyx@users.sourceforge.net) * Copyright (C) 2005-2019 Gregory Montoir (cyx@users.sourceforge.net)
*/ */
#include "game.h" #include "game.h"

129
cpc_player.cpp Normal file
View File

@ -0,0 +1,129 @@
#include "cpc_player.h"
#include "mixer.h"
#include "util.h"
static const char *_tunes[] = {
"Options.Cpc",
"Jungle.Cpc",
"Subway.Cpc",
"TVshow.Cpc",
"City.Cpc",
"Alien.Cpc"
};
CpcPlayer::CpcPlayer(Mixer *mixer, FileSystem *fs)
: _mix(mixer), _fs(fs) {
}
CpcPlayer::~CpcPlayer() {
}
bool CpcPlayer::playTrack(int num) {
_compression[0] = 0;
const int tuneNum = num - 2;
if (tuneNum >= 0 && tuneNum < ARRAYSIZE(_tunes) && _f.open(_tunes[tuneNum], "rb", _fs)) {
_pos = 0;
_sampleL = _sampleR = 0;
while (nextChunk()) {
if (_compression[0]) {
_restartPos = _nextPos;
_mix->setPremixHook(mixCallback, this);
return true;
}
}
}
return false;
}
void CpcPlayer::stopTrack() {
_f.close();
}
void CpcPlayer::pauseTrack() {
_mix->setPremixHook(0, 0);
}
void CpcPlayer::resumeTrack() {
_mix->setPremixHook(mixCallback, this);
}
bool CpcPlayer::nextChunk() {
bool found = false;
while (!_f.ioErr() && !found) {
const uint32_t pos = _pos;
char tag[4];
_f.read(tag, sizeof(tag));
const uint32_t len = _f.readUint32BE();
_nextPos = pos + len;
if (memcmp(tag, "SNDS", 4) == 0) {
_f.readUint32BE();
_f.readUint32BE();
char type[4];
_f.read(type, sizeof(type));
if (memcmp(type, "SHDR", 4) == 0) {
uint8_t buf[24];
_f.read(buf, sizeof(buf));
const uint32_t rate = _f.readUint32BE();
const uint32_t channels = _f.readUint32BE();
if (channels != 2 || rate != _mix->getSampleRate()) {
warning("Unsupported CPC tune channels %d rate %d", channels, rate);
break;
}
_f.read(_compression, sizeof(_compression) - 1);
_compression[sizeof(_compression) - 1] = 0;
if (strcmp(_compression, "SDX2") != 0) {
warning("Unsupported CPC compression '%s'", _compression);
break;
}
found = true;
} else if (memcmp(type, "SSMP", 4) == 0) {
_samplesLeft = _f.readUint32BE();
found = true;
break;
} else {
warning("Unhandled SNDS chunk '%c%c%c%c'", type[0], type[1], type[2], type[3]);
}
} else if (memcmp(tag, "SHDR", 4) == 0 || memcmp(tag, "FILL", 4) == 0 || memcmp(tag, "CTRL", 4) == 0) {
// ignore
} else {
warning("Unhandled chunk '%c%c%c%c' size %d", tag[0], tag[1], tag[2], tag[3], len);
}
_f.seek(_nextPos);
_pos = _nextPos;
}
return found;
}
static int16_t decodeSDX2(int16_t prev, int8_t data) {
const int sqr = data * ABS(data) * 2;
return (data & 1) != 0 ? prev + sqr : sqr;
}
int8_t CpcPlayer::readSampleData() {
if (_samplesLeft <= 0) {
_f.seek(_nextPos);
_pos = _nextPos;
if (!nextChunk()) {
// rewind
_f.seek(_restartPos);
nextChunk();
}
}
const int8_t data = _f.readByte();
--_samplesLeft;
return data;
}
bool CpcPlayer::mix(int16_t *buf, int len) {
for (int i = 0; i < len; ++i) {
_sampleL = decodeSDX2(_sampleL, readSampleData());
_sampleR = decodeSDX2(_sampleR, readSampleData());
*buf++ = (_sampleL + _sampleR) / 2;
}
return true;
}
bool CpcPlayer::mixCallback(void *param, int16_t *buf, int len) {
return ((CpcPlayer *)param)->mix(buf, len);
}

37
cpc_player.h Normal file
View File

@ -0,0 +1,37 @@
#ifndef CPC_PLAYER_H__
#define CPC_PLAYER_H__
#include "intern.h"
#include "file.h"
struct FileSystem;
struct Mixer;
struct CpcPlayer {
Mixer *_mix;
FileSystem *_fs;
File _f;
uint32_t _pos;
uint32_t _nextPos;
uint32_t _restartPos;
char _compression[5];
int _samplesLeft;
int16_t _sampleL, _sampleR;
CpcPlayer(Mixer *mixer, FileSystem *fs);
~CpcPlayer();
bool playTrack(int num);
void stopTrack();
void pauseTrack();
void resumeTrack();
bool nextChunk();
int8_t readSampleData();
bool mix(int16_t *buf, int len);
static bool mixCallback(void *param, int16_t *buf, int len);
};
#endif

View File

@ -1,7 +1,7 @@
/* /*
* REminiscence - Flashback interpreter * REminiscence - Flashback interpreter
* Copyright (C) 2005-2018 Gregory Montoir (cyx@users.sourceforge.net) * Copyright (C) 2005-2019 Gregory Montoir (cyx@users.sourceforge.net)
*/ */
#include <math.h> #include <math.h>
@ -94,6 +94,15 @@ void Cutscene::setPalette() {
sin(330) table: 221, math:-127 sin(330) table: 221, math:-127
*/ */
/*
a = rotation angle
b = scale/distort vertically (180)
c = scale/distort horizontally (90)
| x | cos_a sin_a | cos_b | cos_c * sin_b |
| y | sin_a -cos_a | sin_c | 1 |
*/
void Cutscene::setRotationTransform(uint16_t a, uint16_t b, uint16_t c) { // identity a:0 b:180 c:90 void Cutscene::setRotationTransform(uint16_t a, uint16_t b, uint16_t c) { // identity a:0 b:180 c:90
const int16_t sin_a = SIN(a); const int16_t sin_a = SIN(a);
const int16_t cos_a = COS(a); const int16_t cos_a = COS(a);
@ -101,10 +110,10 @@ void Cutscene::setRotationTransform(uint16_t a, uint16_t b, uint16_t c) { // ide
const int16_t cos_c = COS(c); const int16_t cos_c = COS(c);
const int16_t sin_b = SIN(b); const int16_t sin_b = SIN(b);
const int16_t cos_b = COS(b); const int16_t cos_b = COS(b);
_rotMat[0] /* .x1 */ = ((cos_a * cos_b) >> 8) - ((((cos_c * sin_a) >> 8) * sin_b) >> 8); _rotMat[0] = ((cos_a * cos_b) >> 8) - ((((cos_c * sin_a) >> 8) * sin_b) >> 8);
_rotMat[1] /* .y1 */ = ((sin_a * cos_b) >> 8) + ((((cos_c * cos_a) >> 8) * sin_b) >> 8); _rotMat[1] = ((sin_a * cos_b) >> 8) + ((((cos_c * cos_a) >> 8) * sin_b) >> 8);
_rotMat[2] /* .x2 */ = ( sin_c * sin_a) >> 8; _rotMat[2] = ( sin_c * sin_a) >> 8;
_rotMat[3] /* .y2 */ = (-sin_c * cos_a) >> 8; _rotMat[3] = (-sin_c * cos_a) >> 8;
} }
static bool isNewLineChar(uint8_t chr, Resource *res) { static bool isNewLineChar(uint8_t chr, Resource *res) {
@ -188,21 +197,20 @@ void Cutscene::swapLayers() {
void Cutscene::drawCreditsText() { void Cutscene::drawCreditsText() {
if (_creditsSequence) { if (_creditsSequence) {
if (_textUnk2 != 0) { if (_creditsKeepText != 0) {
if (_varText == 0) { if (_creditsSlowText == 0) {
_textUnk2 = 0; _creditsKeepText = 0;
} else { } else {
return; return;
} }
} }
if (_creditsTextCounter <= 0) { if (_creditsTextCounter <= 0) {
uint8_t code = *_textCurPtr; const uint8_t code = *_textCurPtr;
if (code == 0xFF) { if (code == 0xFF) {
_textBuf[0] = 0xA; _textBuf[0] = 0xA;
} else if (code == 0xFE) { } else if (code == 0xFE) {
++_textCurPtr; ++_textCurPtr;
code = *_textCurPtr++; _creditsTextCounter = *_textCurPtr++;
_creditsTextCounter = code;
} else if (code == 1) { } else if (code == 1) {
++_textCurPtr; ++_textCurPtr;
_creditsTextPosX = *_textCurPtr++; _creditsTextPosX = *_textCurPtr++;
@ -211,8 +219,8 @@ void Cutscene::drawCreditsText() {
_textCurBuf = _textBuf; _textCurBuf = _textBuf;
_textBuf[0] = 0xA; _textBuf[0] = 0xA;
++_textCurPtr; ++_textCurPtr;
if (_varText != 0) { if (_creditsSlowText != 0) {
_textUnk2 = 0xFF; _creditsKeepText = 0xFF;
} }
} else { } else {
*_textCurBuf++ = code; *_textCurBuf++ = code;
@ -235,7 +243,6 @@ void Cutscene::drawProtectionShape(uint8_t shapeNum, int16_t zoom) {
int16_t x = 0; int16_t x = 0;
int16_t y = 0; int16_t y = 0;
zoom += 512; zoom += 512;
setRotationTransform(0, 180, 90);
const uint8_t *shapeOffsetTable = _protectionShapeData + READ_BE_UINT16(_protectionShapeData + 0x02); const uint8_t *shapeOffsetTable = _protectionShapeData + READ_BE_UINT16(_protectionShapeData + 0x02);
const uint8_t *shapeDataTable = _protectionShapeData + READ_BE_UINT16(_protectionShapeData + 0x0E); const uint8_t *shapeDataTable = _protectionShapeData + READ_BE_UINT16(_protectionShapeData + 0x0E);
@ -257,7 +264,7 @@ void Cutscene::drawProtectionShape(uint8_t shapeNum, int16_t zoom) {
} }
_hasAlphaColor = (verticesOffset & 0x4000) != 0; _hasAlphaColor = (verticesOffset & 0x4000) != 0;
_primitiveColor = 0xC0 + *shapeData++; _primitiveColor = 0xC0 + *shapeData++;
drawShapeScaleRotate(p, zoom, dx, dy, x, y, 0, 0); drawShapeScale(p, zoom, dx, dy, x, y, 0, 0);
++_shape_count; ++_shape_count;
} }
} }
@ -269,7 +276,7 @@ void Cutscene::op_markCurPos() {
_frameDelay = 5; _frameDelay = 5;
setPalette(); setPalette();
swapLayers(); swapLayers();
_varText = 0; _creditsSlowText = 0;
} }
void Cutscene::op_refreshScreen() { void Cutscene::op_refreshScreen() {
@ -277,7 +284,7 @@ void Cutscene::op_refreshScreen() {
_clearScreen = fetchNextCmdByte(); _clearScreen = fetchNextCmdByte();
if (_clearScreen != 0) { if (_clearScreen != 0) {
swapLayers(); swapLayers();
_varText = 0; _creditsSlowText = 0;
} }
} }
@ -286,7 +293,7 @@ void Cutscene::op_waitForSync() {
if (_creditsSequence) { if (_creditsSequence) {
uint16_t n = fetchNextCmdByte() * 2; uint16_t n = fetchNextCmdByte() * 2;
do { do {
_varText = 0xFF; _creditsSlowText = 0xFF;
_frameDelay = 3; _frameDelay = 3;
if (_textBuf == _textCurBuf) { if (_textBuf == _textCurBuf) {
_creditsTextCounter = _res->isAmiga() ? 60 : 20; _creditsTextCounter = _res->isAmiga() ? 60 : 20;
@ -296,7 +303,7 @@ void Cutscene::op_waitForSync() {
setPalette(); setPalette();
} while (--n); } while (--n);
swapLayers(); swapLayers();
_varText = 0; _creditsSlowText = 0;
} else { } else {
_frameDelay = fetchNextCmdByte() * 4; _frameDelay = fetchNextCmdByte() * 4;
sync(); // XXX handle input sync(); // XXX handle input
@ -447,7 +454,7 @@ void Cutscene::op_refreshAll() {
_frameDelay = 5; _frameDelay = 5;
setPalette(); setPalette();
swapLayers(); swapLayers();
_varText = 0xFF; _creditsSlowText = 0xFF;
op_handleKeys(); op_handleKeys();
} }
@ -859,7 +866,7 @@ void Cutscene::op_drawShapeScaleRotate() {
void Cutscene::op_drawCreditsText() { void Cutscene::op_drawCreditsText() {
debug(DBG_CUT, "Cutscene::op_drawCreditsText()"); debug(DBG_CUT, "Cutscene::op_drawCreditsText()");
_varText = 0xFF; _creditsSlowText = 0xFF;
if (_textCurBuf == _textBuf) { if (_textCurBuf == _textBuf) {
++_creditsTextCounter; ++_creditsTextCounter;
} }
@ -1009,11 +1016,13 @@ void Cutscene::mainLoop(uint16_t num) {
bool Cutscene::load(uint16_t cutName) { bool Cutscene::load(uint16_t cutName) {
assert(cutName != 0xFFFF); assert(cutName != 0xFFFF);
const char *name = _namesTable[cutName & 0xFF]; const char *name = _namesTableDOS[cutName & 0xFF];
switch (_res->_type) { switch (_res->_type) {
case kResourceTypeAmiga: case kResourceTypeAmiga:
if (strncmp(name, "INTRO", 5) == 0) { if (cutName == 7) {
name = "INTRO"; name = "INTRO";
} else if (cutName == 10) {
name = "SERRURE";
} }
_res->load(name, Resource::OT_CMP); _res->load(name, Resource::OT_CMP);
if (_id == 0x39 && _res->_lang != LANG_FR) { if (_id == 0x39 && _res->_lang != LANG_FR) {
@ -1091,8 +1100,8 @@ void Cutscene::playCredits() {
_textBuf[0] = 0xA; _textBuf[0] = 0xA;
_textCurBuf = _textBuf; _textCurBuf = _textBuf;
_creditsSequence = true; _creditsSequence = true;
_varText = 0; _creditsSlowText = 0;
_textUnk2 = 0; _creditsKeepText = 0;
_creditsTextCounter = 0; _creditsTextCounter = 0;
_interrupted = false; _interrupted = false;
const uint16_t *cut_seq = _creditsCutSeq; const uint16_t *cut_seq = _creditsCutSeq;
@ -1102,8 +1111,9 @@ void Cutscene::playCredits() {
break; break;
} }
prepare(); prepare();
uint16_t cutName = _offsetsTable[cut_id * 2 + 0]; const uint16_t *offsets = _res->isAmiga() ? _offsetsTableAmiga : _offsetsTableDOS;
uint16_t cutOff = _offsetsTable[cut_id * 2 + 1]; uint16_t cutName = offsets[cut_id * 2 + 0];
uint16_t cutOff = offsets[cut_id * 2 + 1];
if (load(cutName)) { if (load(cutName)) {
mainLoop(cutOff); mainLoop(cutOff);
unload(); unload();
@ -1149,18 +1159,26 @@ void Cutscene::play() {
debug(DBG_CUT, "Cutscene::play() _id=0x%X", _id); debug(DBG_CUT, "Cutscene::play() _id=0x%X", _id);
_creditsSequence = false; _creditsSequence = false;
prepare(); prepare();
uint16_t cutName = _offsetsTable[_id * 2 + 0]; const uint16_t *offsets = _res->isAmiga() ? _offsetsTableAmiga : _offsetsTableDOS;
uint16_t cutOff = _offsetsTable[_id * 2 + 1]; uint16_t cutName = offsets[_id * 2 + 0];
uint16_t cutOff = offsets[_id * 2 + 1];
if (cutName == 0xFFFF) { if (cutName == 0xFFFF) {
switch (_id) { switch (_id) {
case 3: // keys
if (g_options.play_carte_cutscene) {
cutName = 2; // CARTE
}
break;
case 8: // save checkpoints
break;
case 19: case 19:
if (g_options.play_serrure_cutscene) { if (g_options.play_serrure_cutscene) {
cutName = 31; // SERRURE cutName = 31; // SERRURE
} }
break; break;
case 22: case 22: // Level 2 fuse repaired
case 23: case 23: // switches
case 24: case 24: // Level 2 fuse is blown
if (g_options.play_asc_cutscene) { if (g_options.play_asc_cutscene) {
cutName = 12; // ASC cutName = 12; // ASC
} }
@ -1171,6 +1189,11 @@ void Cutscene::play() {
cutName = 14; // METRO cutName = 14; // METRO
} }
break; break;
case 46: // Level 2 terminal card mission
break;
default:
warning("Unknown cutscene %d", _id);
break;
} }
} }
if (_patchedOffsetsTable) { if (_patchedOffsetsTable) {
@ -1284,7 +1307,7 @@ void Cutscene::playSet(const uint8_t *p, int offset) {
offset = 10; offset = 10;
const int frames = READ_BE_UINT16(p + offset); offset += 2; const int frames = READ_BE_UINT16(p + offset); offset += 2;
for (int i = 0; i < frames && !_stub->_pi.quit; ++i) { for (int i = 0; i < frames && !_stub->_pi.quit && !_interrupted; ++i) {
const uint32_t timestamp = _stub->getTimeStamp(); const uint32_t timestamp = _stub->getTimeStamp();
memset(_page1, 0xC0, _vid->_layerSize); memset(_page1, 0xC0, _vid->_layerSize);
@ -1339,5 +1362,9 @@ void Cutscene::playSet(const uint8_t *p, int offset) {
const int diff = 6 * TIMER_SLICE - (_stub->getTimeStamp() - timestamp); const int diff = 6 * TIMER_SLICE - (_stub->getTimeStamp() - timestamp);
_stub->sleep((diff < 16) ? 16 : diff); _stub->sleep((diff < 16) ? 16 : diff);
_stub->processEvents(); _stub->processEvents();
if (_stub->_pi.backspace) {
_stub->_pi.backspace = false;
_interrupted = true;
}
} }
} }

View File

@ -1,7 +1,7 @@
/* /*
* REminiscence - Flashback interpreter * REminiscence - Flashback interpreter
* Copyright (C) 2005-2018 Gregory Montoir (cyx@users.sourceforge.net) * Copyright (C) 2005-2019 Gregory Montoir (cyx@users.sourceforge.net)
*/ */
#ifndef CUTSCENE_H__ #ifndef CUTSCENE_H__
@ -39,8 +39,9 @@ struct Cutscene {
}; };
static const OpcodeStub _opcodeTable[]; static const OpcodeStub _opcodeTable[];
static const char *_namesTable[]; static const char *_namesTableDOS[];
static const uint16_t _offsetsTable[]; static const uint16_t _offsetsTableDOS[];
static const uint16_t _offsetsTableAmiga[];
static const uint8_t _amigaDemoOffsetsTable[]; static const uint8_t _amigaDemoOffsetsTable[];
static const uint8_t _ssiOffsetsTable[]; static const uint8_t _ssiOffsetsTable[];
static const uint16_t _cosTable[]; static const uint16_t _cosTable[];
@ -70,7 +71,7 @@ struct Cutscene {
uint32_t _tstamp; uint32_t _tstamp;
uint8_t _frameDelay; uint8_t _frameDelay;
bool _newPal; bool _newPal;
uint8_t _palBuf[0x20 * 2]; uint8_t _palBuf[16 * sizeof(uint16_t) * 2];
uint16_t _baseOffset; uint16_t _baseOffset;
bool _creditsSequence; bool _creditsSequence;
uint32_t _rotMat[4]; uint32_t _rotMat[4];
@ -78,7 +79,6 @@ struct Cutscene {
uint8_t _clearScreen; uint8_t _clearScreen;
Point _vertices[0x80]; Point _vertices[0x80];
bool _hasAlphaColor; bool _hasAlphaColor;
uint8_t _varText;
uint8_t _varKey; uint8_t _varKey;
int16_t _shape_ix; int16_t _shape_ix;
int16_t _shape_iy; int16_t _shape_iy;
@ -97,7 +97,8 @@ struct Cutscene {
uint8_t _textBuf[500]; uint8_t _textBuf[500];
const uint8_t *_textCurPtr; const uint8_t *_textCurPtr;
uint8_t *_textCurBuf; uint8_t *_textCurBuf;
uint8_t _textUnk2; uint8_t _creditsSlowText;
uint8_t _creditsKeepText;
uint8_t _creditsTextPosX; uint8_t _creditsTextPosX;
uint8_t _creditsTextPosY; uint8_t _creditsTextPosY;
int16_t _creditsTextCounter; int16_t _creditsTextCounter;

View File

@ -30,57 +30,64 @@ uint8_t *decodeLzss(File &f, uint32_t &decodedSize) {
} }
static void setPixel(int x, int y, int w, int h, uint8_t color, DecodeBuffer *buf) { static void setPixel(int x, int y, int w, int h, uint8_t color, DecodeBuffer *buf) {
buf->setPixel(buf, x, y, w, h, color); y += buf->y;
} if (y >= 0 && y < buf->h) {
if (buf->xflip) {
void decodeC103(const uint8_t *a3, int w, int h, DecodeBuffer *buf) { x = w - 1 - x;
uint8_t d0; }
int d3 = 0; x += buf->x;
int d7 = 1; if (x >= 0 && x < buf->w) {
int d6 = 0; buf->setPixel(buf, x, y, color);
int d1 = 0;
static const uint32_t d5 = 0xFFF;
uint8_t a1[0x1000];
for (int y = 0; y < h; ++y) {
for (int x = 0; x < w; ++x) {
assert(d6 >= 0);
if (d6 == 0) {
int carry = d7 & 1;
d7 >>= 1;
if (d7 == 0) {
d7 = *a3++;
const int extended_bit = carry ? 0x80 : 0;
carry = d7 & 1;
d7 = extended_bit | (d7 >> 1);
}
if (!carry) {
d0 = *a3++;
a1[d3] = d0;
++d3;
d3 &= d5;
setPixel(x, y, w, h, d0, buf);
continue;
}
d1 = READ_BE_UINT16(a3); a3 += 2;
d6 = d1;
d1 &= d5;
++d1;
d1 = (d3 - d1) & d5;
d6 >>= 12;
d6 += 3;
}
d0 = a1[d1++];
d1 &= d5;
a1[d3++] = d0;
d3 &= d5;
setPixel(x, y, w, h, d0, buf);
--d6;
} }
} }
} }
void decodeC211(const uint8_t *a3, int w, int h, DecodeBuffer *buf) { void decodeC103(const uint8_t *src, int w, int h, DecodeBuffer *buf) {
static const int kBits = 12;
static const int kMask = (1 << kBits) - 1;
int cursor = 0;
int bits = 1;
int count = 0;
int offset = 0;
uint8_t window[(1 << kBits)];
for (int y = 0; y < h; ++y) {
for (int x = 0; x < w; ++x) {
if (count == 0) {
int carry = bits & 1;
bits >>= 1;
if (bits == 0) {
bits = *src++;
if (carry) {
bits |= 0x100;
}
carry = bits & 1;
bits >>= 1;
}
if (!carry) {
const uint8_t color = *src++;
window[cursor] = color;
++cursor;
cursor &= kMask;
setPixel(x, y, w, h, color, buf);
continue;
}
offset = READ_BE_UINT16(src); src += 2;
count = 3 + (offset >> 12);
offset &= kMask;
offset = (cursor - offset - 1) & kMask;
}
const uint8_t color = window[offset++];
offset &= kMask;
window[cursor++] = color;
cursor &= kMask;
setPixel(x, y, w, h, color, buf);
--count;
}
}
}
void decodeC211(const uint8_t *src, int w, int h, DecodeBuffer *buf) {
struct { struct {
const uint8_t *ptr; const uint8_t *ptr;
int repeatCount; int repeatCount;
@ -90,52 +97,48 @@ void decodeC211(const uint8_t *a3, int w, int h, DecodeBuffer *buf) {
int sp = 0; int sp = 0;
while (1) { while (1) {
uint8_t d0 = *a3++; const uint8_t code = *src++;
if ((d0 & 0x80) != 0) { if ((code & 0x80) != 0) {
++y; ++y;
x = 0; x = 0;
} }
int d1 = d0 & 0x1F; int count = code & 0x1F;
if (d1 == 0) { if (count == 0) {
d1 = READ_BE_UINT16(a3); a3 += 2; count = READ_BE_UINT16(src); src += 2;
} }
const int carry_set = (d0 & 0x40) != 0; if ((code & 0x40) == 0) {
d0 <<= 2; if ((code & 0x20) == 0) {
if (!carry_set) { if (count == 1) {
if ((d0 & 0x80) == 0) {
--d1;
if (d1 == 0) {
assert(sp > 0); assert(sp > 0);
--stack[sp - 1].repeatCount; --stack[sp - 1].repeatCount;
if (stack[sp - 1].repeatCount >= 0) { if (stack[sp - 1].repeatCount >= 0) {
a3 = stack[sp - 1].ptr; src = stack[sp - 1].ptr;
} else { } else {
--sp; --sp;
} }
} else { } else {
assert(sp < ARRAYSIZE(stack)); assert(sp < ARRAYSIZE(stack));
stack[sp].ptr = a3; stack[sp].ptr = src;
stack[sp].repeatCount = d1; stack[sp].repeatCount = count - 1;
++sp; ++sp;
} }
} else { } else {
x += d1; x += count;
} }
} else { } else {
if ((d0 & 0x80) == 0) { if ((code & 0x20) == 0) {
if (d1 == 1) { if (count == 1) {
return; return;
} }
const uint8_t color = *a3++; const uint8_t color = *src++;
for (int i = 0; i < d1; ++i) { for (int i = 0; i < count; ++i) {
setPixel(x++, y, w, h, color, buf); setPixel(x++, y, w, h, color, buf);
} }
} else { } else {
for (int i = 0; i < d1; ++i) { for (int i = 0; i < count; ++i) {
setPixel(x++, y, w, h, *a3++, buf); setPixel(x++, y, w, h, *src++, buf);
} }
} }
} }
} }
} }

View File

@ -13,7 +13,7 @@ struct DecodeBuffer {
int x, y; int x, y;
bool xflip; bool xflip;
void (*setPixel)(DecodeBuffer *buf, int src_x, int src_y, int src_w, int src_h, uint8_t color); void (*setPixel)(DecodeBuffer *buf, int x, int y, uint8_t color);
void *dataPtr; void *dataPtr;
}; };

View File

@ -1,7 +1,7 @@
/* /*
* REminiscence - Flashback interpreter * REminiscence - Flashback interpreter
* Copyright (C) 2005-2018 Gregory Montoir (cyx@users.sourceforge.net) * Copyright (C) 2005-2019 Gregory Montoir (cyx@users.sourceforge.net)
*/ */
#include <sys/param.h> #include <sys/param.h>
@ -324,6 +324,16 @@ void File::writeByte(uint8_t b) {
write(&b, 1); write(&b, 1);
} }
void File::writeUint16LE(uint16_t n) {
writeByte(n & 0xFF);
writeByte(n >> 8);
}
void File::writeUint32LE(uint32_t n) {
writeUint16LE(n & 0xFFFF);
writeUint16LE(n >> 16);
}
void File::writeUint16BE(uint16_t n) { void File::writeUint16BE(uint16_t n) {
writeByte(n >> 8); writeByte(n >> 8);
writeByte(n & 0xFF); writeByte(n & 0xFF);
@ -333,3 +343,16 @@ void File::writeUint32BE(uint32_t n) {
writeUint16BE(n >> 16); writeUint16BE(n >> 16);
writeUint16BE(n & 0xFFFF); writeUint16BE(n & 0xFFFF);
} }
void dumpFile(const char *filename, const uint8_t *p, int size) {
char path[MAXPATHLEN];
snprintf(path, sizeof(path), "DUMP/%s", filename);
FILE *fp = fopen(filename, "wb");
if (fp) {
const int count = fwrite(p, 1, size, fp);
if (count != size) {
warning("Failed to write %d bytes (expected %d)", count, size);
}
fclose(fp);
}
}

6
file.h
View File

@ -1,7 +1,7 @@
/* /*
* REminiscence - Flashback interpreter * REminiscence - Flashback interpreter
* Copyright (C) 2005-2018 Gregory Montoir (cyx@users.sourceforge.net) * Copyright (C) 2005-2019 Gregory Montoir (cyx@users.sourceforge.net)
*/ */
#ifndef FILE_H__ #ifndef FILE_H__
@ -32,8 +32,12 @@ struct File {
uint32_t readUint32BE(); uint32_t readUint32BE();
uint32_t write(const void *ptr, uint32_t size); uint32_t write(const void *ptr, uint32_t size);
void writeByte(uint8_t b); void writeByte(uint8_t b);
void writeUint16LE(uint16_t n);
void writeUint32LE(uint32_t n);
void writeUint16BE(uint16_t n); void writeUint16BE(uint16_t n);
void writeUint32BE(uint32_t n); void writeUint32BE(uint32_t n);
}; };
void dumpFile(const char *filename, const uint8_t *p, int size);
#endif // FILE_H__ #endif // FILE_H__

2
fs.cpp
View File

@ -1,7 +1,7 @@
/* /*
* REminiscence - Flashback interpreter * REminiscence - Flashback interpreter
* Copyright (C) 2005-2018 Gregory Montoir (cyx@users.sourceforge.net) * Copyright (C) 2005-2019 Gregory Montoir (cyx@users.sourceforge.net)
*/ */
#ifdef _WIN32 #ifdef _WIN32

2
fs.h
View File

@ -1,7 +1,7 @@
/* /*
* REminiscence - Flashback interpreter * REminiscence - Flashback interpreter
* Copyright (C) 2005-2018 Gregory Montoir (cyx@users.sourceforge.net) * Copyright (C) 2005-2019 Gregory Montoir (cyx@users.sourceforge.net)
*/ */
#ifndef FS_H__ #ifndef FS_H__

273
game.cpp
View File

@ -1,7 +1,7 @@
/* /*
* REminiscence - Flashback interpreter * REminiscence - Flashback interpreter
* Copyright (C) 2005-2018 Gregory Montoir (cyx@users.sourceforge.net) * Copyright (C) 2005-2019 Gregory Montoir (cyx@users.sourceforge.net)
*/ */
#include <time.h> #include <time.h>
@ -16,7 +16,7 @@
Game::Game(SystemStub *stub, FileSystem *fs, const char *savePath, int level, ResourceType ver, Language lang, WidescreenMode widescreenMode, bool autoSave) Game::Game(SystemStub *stub, FileSystem *fs, const char *savePath, int level, ResourceType ver, Language lang, WidescreenMode widescreenMode, bool autoSave)
: _cut(&_res, stub, &_vid), _menu(&_res, stub, &_vid), : _cut(&_res, stub, &_vid), _menu(&_res, stub, &_vid),
_mix(fs, stub), _res(fs, ver, lang), _seq(stub, &_mix), _vid(&_res, stub), _mix(fs, stub), _res(fs, ver, lang), _seq(stub, &_mix), _vid(&_res, stub, widescreenMode),
_stub(stub), _fs(fs), _savePath(savePath) { _stub(stub), _fs(fs), _savePath(savePath) {
_stateSlot = 1; _stateSlot = 1;
_inp_demPos = 0; _inp_demPos = 0;
@ -55,10 +55,11 @@ void Game::run() {
break; break;
} }
if (!g_options.bypass_protection) { if (!g_options.bypass_protection && !g_options.use_words_protection && !_res.isMac()) {
while (!handleProtectionScreen()); while (!handleProtectionScreenShape()) {
if (_stub->_pi.quit) { if (_stub->_pi.quit) {
return; return;
}
} }
} }
@ -94,6 +95,14 @@ void Game::run() {
break; break;
} }
if (!g_options.bypass_protection && g_options.use_words_protection && _res.isDOS()) {
while (!handleProtectionScreenWords()) {
if (_stub->_pi.quit) {
return;
}
}
}
bool presentMenu = ((_res._type != kResourceTypeDOS) || _res.fileExists("MENU1.MAP")); bool presentMenu = ((_res._type != kResourceTypeDOS) || _res.fileExists("MENU1.MAP"));
while (!_stub->_pi.quit) { while (!_stub->_pi.quit) {
if (presentMenu) { if (presentMenu) {
@ -132,9 +141,12 @@ void Game::run() {
displayTitleScreenMac(Menu::kMacTitleScreen_Flashback); displayTitleScreenMac(Menu::kMacTitleScreen_Flashback);
break; break;
} }
if (_stub->_pi.quit) { }
break; if (_stub->_pi.quit) {
} break;
}
if (_stub->hasWidescreen()) {
_stub->clearWidescreen();
} }
if (_currentLevel == 7) { if (_currentLevel == 7) {
_vid.fadeOut(); _vid.fadeOut();
@ -166,11 +178,6 @@ void Game::run() {
_stub->_pi.enter = false; _stub->_pi.enter = false;
_stub->_pi.space = false; _stub->_pi.space = false;
_stub->_pi.shift = false; _stub->_pi.shift = false;
// clear widescreen borders
if (_stub->hasWidescreen()) {
_stub->copyRectLeftBorder(_vid._w, _vid._h, 0);
_stub->copyRectRightBorder(_vid._w, _vid._h, 0);
}
} }
} }
@ -198,6 +205,7 @@ void Game::displayTitleScreenAmiga() {
Color c = Video::AMIGA_convertColor(kAmigaColors[i]); Color c = Video::AMIGA_convertColor(kAmigaColors[i]);
_stub->setPaletteEntry(i, &c); _stub->setPaletteEntry(i, &c);
} }
_vid.setTextPalette();
_stub->setScreenSize(kW, kH); _stub->setScreenSize(kW, kH);
// fill with black // fill with black
_stub->copyRect(0, 0, kW, kH, buf, kW); _stub->copyRect(0, 0, kW, kH, buf, kW);
@ -210,6 +218,32 @@ void Game::displayTitleScreenAmiga() {
_stub->copyRect(0, y, kW, h * 2, buf, kW); _stub->copyRect(0, y, kW, h * 2, buf, kW);
_stub->updateScreen(0); _stub->updateScreen(0);
h += 2; h += 2;
} else {
static const uint8_t selectedColor = 0xE4;
static const uint8_t defaultColor = 0xE8;
for (int i = 0; i < 7; ++i) {
const char *str = Menu::_levelNames[i];
const uint8_t color = (_currentLevel == i) ? selectedColor : defaultColor;
const int x = 24;
const int y = 24 + i * 16;
for (int j = 0; str[j]; ++j) {
_vid.AMIGA_drawStringChar(buf, kW, x + j * Video::CHAR_W, y, _res._fnt, color, str[j]);
}
}
if (_stub->_pi.dirMask & PlayerInput::DIR_UP) {
_stub->_pi.dirMask &= ~PlayerInput::DIR_UP;
if (_currentLevel > 0) {
--_currentLevel;
}
}
if (_stub->_pi.dirMask & PlayerInput::DIR_DOWN) {
_stub->_pi.dirMask &= ~PlayerInput::DIR_DOWN;
if (_currentLevel < 6) {
++_currentLevel;
}
}
_stub->copyRect(0, 0, kW, kH, buf, kW);
_stub->updateScreen(0);
} }
_stub->processEvents(); _stub->processEvents();
if (_stub->_pi.quit) { if (_stub->_pi.quit) {
@ -249,7 +283,7 @@ void Game::displayTitleScreenMac(int num) {
buf.h = _vid._h; buf.h = _vid._h;
buf.x = (_vid._w - w) / 2; buf.x = (_vid._w - w) / 2;
buf.y = (_vid._h - h) / 2; buf.y = (_vid._h - h) / 2;
buf.setPixel = Video::MAC_drawBuffer; buf.setPixel = Video::MAC_setPixel;
memset(_vid._frontLayer, 0, _vid._layerSize); memset(_vid._frontLayer, 0, _vid._layerSize);
_res.MAC_loadTitleImage(num, &buf); _res.MAC_loadTitleImage(num, &buf);
for (int i = 0; i < 12; ++i) { for (int i = 0; i < 12; ++i) {
@ -444,6 +478,9 @@ void Game::playCutscene(int id) {
_cut._id = id; _cut._id = id;
} }
if (_cut._id != 0xFFFF) { if (_cut._id != 0xFFFF) {
if (_stub->hasWidescreen()) {
_stub->enableWidescreen(false);
}
_mix.stopMusic(); _mix.stopMusic();
if (_res._hasSeqData) { if (_res._hasSeqData) {
int num = 0; int num = 0;
@ -520,6 +557,9 @@ void Game::playCutscene(int id) {
_cut.playCredits(); _cut.playCredits();
} }
_mix.stopMusic(); _mix.stopMusic();
if (_stub->hasWidescreen()) {
_stub->enableWidescreen(true);
}
} }
} }
@ -566,8 +606,7 @@ void Game::drawCurrentInventoryItem() {
void Game::showFinalScore() { void Game::showFinalScore() {
if (_stub->hasWidescreen()) { if (_stub->hasWidescreen()) {
_stub->copyRectLeftBorder(_vid._w, _vid._h, 0); _stub->clearWidescreen();
_stub->copyRectRightBorder(_vid._w, _vid._h, 0);
} }
playCutscene(0x49); playCutscene(0x49);
char buf[50]; char buf[50];
@ -673,7 +712,8 @@ bool Game::handleConfigPanel() {
_menu.drawString(_res.getMenuString(LocaleData::LI_19_ABORT_GAME), y + 4, 9, colors[1]); _menu.drawString(_res.getMenuString(LocaleData::LI_19_ABORT_GAME), y + 4, 9, colors[1]);
_menu.drawString(_res.getMenuString(LocaleData::LI_20_LOAD_GAME), y + 6, 9, colors[2]); _menu.drawString(_res.getMenuString(LocaleData::LI_20_LOAD_GAME), y + 6, 9, colors[2]);
_menu.drawString(_res.getMenuString(LocaleData::LI_21_SAVE_GAME), y + 8, 9, colors[3]); _menu.drawString(_res.getMenuString(LocaleData::LI_21_SAVE_GAME), y + 8, 9, colors[3]);
char buf[30]; _vid.fillRect(Video::CHAR_W * (x + 1), Video::CHAR_H * (y + 10), Video::CHAR_W * (w - 2), Video::CHAR_H, 0xE2);
char buf[32];
snprintf(buf, sizeof(buf), "%s < %02d >", _res.getMenuString(LocaleData::LI_22_SAVE_SLOT), _stateSlot); snprintf(buf, sizeof(buf), "%s < %02d >", _res.getMenuString(LocaleData::LI_22_SAVE_SLOT), _stateSlot);
_menu.drawString(buf, y + 10, 9, 1); _menu.drawString(buf, y + 10, 9, 1);
@ -730,8 +770,7 @@ bool Game::handleConfigPanel() {
bool Game::handleContinueAbort() { bool Game::handleContinueAbort() {
if (_stub->hasWidescreen()) { if (_stub->hasWidescreen()) {
_stub->copyRectLeftBorder(_vid._w, _vid._h, 0); _stub->clearWidescreen();
_stub->copyRectRightBorder(_vid._w, _vid._h, 0);
} }
playCutscene(0x48); playCutscene(0x48);
int timeout = 100; int timeout = 100;
@ -799,87 +838,6 @@ bool Game::handleContinueAbort() {
return false; return false;
} }
bool Game::handleProtectionScreen() {
bool valid = true;
_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;
int shapeNum = getRandomNumber() % 30;
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);
}
int codeNum = getRandomNumber() % 5;
_cut.drawProtectionShape(shapeNum, 1);
_vid.setTextPalette();
char codeText[7];
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[20];
snprintf(buf, sizeof(buf), "CODE %d : %s", codeNum + 1, 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 < 6) {
if (c >= 'a' && c <= 'z') {
c &= ~0x20;
}
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) {
const uint8_t *p = _protectionCodeData + shapeNum * 0x1E + codeNum * 6;
for (int i = 0; i < len; ++i) {
uint8_t r = 0;
uint8_t ch = codeText[i];
for (int b = 0; b < 8; ++b) {
if (ch & (1 << b)) {
r |= (1 << (7 - b));
}
}
r ^= 0x55;
if (r != *p++) {
valid = false;
break;
}
}
break;
}
}
} while (!_stub->_pi.quit);
_vid.fadeOut();
return valid;
}
void Game::printLevelCode() { void Game::printLevelCode() {
if (_printLevelCodeCounter != 0) { if (_printLevelCodeCounter != 0) {
--_printLevelCodeCounter; --_printLevelCodeCounter;
@ -1006,22 +964,23 @@ void Game::drawStoryTexts() {
yPos += 8; yPos += 8;
} }
} }
MixerChunk chunk; uint8_t *voiceSegmentData = 0;
_res.load_VCE(_textToDisplay, textSpeechSegment++, &chunk.data, &chunk.len); uint32_t voiceSegmentLen = 0;
if (chunk.data) { _res.load_VCE(_textToDisplay, textSpeechSegment++, &voiceSegmentData, &voiceSegmentLen);
_mix.play(&chunk, 32000, Mixer::MAX_VOLUME); if (voiceSegmentData) {
_mix.play(voiceSegmentData, voiceSegmentLen, 32000, Mixer::MAX_VOLUME);
} }
_vid.updateScreen(); _vid.updateScreen();
while (!_stub->_pi.backspace && !_stub->_pi.quit) { while (!_stub->_pi.backspace && !_stub->_pi.quit) {
if (chunk.data && !_mix.isPlaying(&chunk)) { if (voiceSegmentData && !_mix.isPlaying(voiceSegmentData)) {
break; break;
} }
inp_update(); inp_update();
_stub->sleep(80); _stub->sleep(80);
} }
if (chunk.data) { if (voiceSegmentData) {
_mix.stopAll(); _mix.stopAll();
free(chunk.data); free(voiceSegmentData);
} }
_stub->_pi.backspace = false; _stub->_pi.backspace = false;
if (_res._type == kResourceTypeMac) { if (_res._type == kResourceTypeMac) {
@ -1220,7 +1179,7 @@ void Game::drawAnimBuffer(uint8_t stateNum, AnimBufferState *state) {
break; break;
case kResourceTypeDOS: case kResourceTypeDOS:
if (!(state->dataPtr[-2] & 0x80)) { if (!(state->dataPtr[-2] & 0x80)) {
decodeCharacterFrame(state->dataPtr, _res._scratchBuffer); _vid.PC_decodeSpm(state->dataPtr, _res._scratchBuffer);
drawCharacter(_res._scratchBuffer, state->x, state->y, state->h, state->w, pge->flags); drawCharacter(_res._scratchBuffer, state->x, state->y, state->h, state->w, pge->flags);
} else { } else {
drawCharacter(state->dataPtr, state->x, state->y, state->h, state->w, pge->flags); drawCharacter(state->dataPtr, state->x, state->y, state->h, state->w, pge->flags);
@ -1398,39 +1357,6 @@ void Game::drawObjectFrame(const uint8_t *bankDataPtr, const uint8_t *dataPtr, i
_vid.markBlockAsDirty(sprite_x, sprite_y, sprite_clipped_w, sprite_clipped_h, _vid._layerScale); _vid.markBlockAsDirty(sprite_x, sprite_y, sprite_clipped_w, sprite_clipped_h, _vid._layerScale);
} }
void Game::decodeCharacterFrame(const uint8_t *dataPtr, uint8_t *dstPtr) {
int n = READ_BE_UINT16(dataPtr); dataPtr += 2;
uint16_t len = n * 2;
uint8_t *dst = dstPtr + 0x400;
while (n--) {
uint8_t c = *dataPtr++;
dst[0] = (c & 0xF0) >> 4;
dst[1] = (c & 0x0F) >> 0;
dst += 2;
}
dst = dstPtr;
const uint8_t *src = dstPtr + 0x400;
do {
uint8_t c1 = *src++;
if (c1 == 0xF) {
uint8_t c2 = *src++;
uint16_t c3 = *src++;
if (c2 == 0xF) {
c1 = *src++;
c2 = *src++;
c3 = (c3 << 4) | c1;
len -= 2;
}
memset(dst, c2, c3 + 4);
dst += c3 + 4;
len -= 3;
} else {
*dst++ = c1;
--len;
}
} while (len != 0);
}
void Game::drawCharacter(const uint8_t *dataPtr, int16_t pos_x, int16_t pos_y, uint8_t a, uint8_t b, uint8_t flags) { void Game::drawCharacter(const uint8_t *dataPtr, int16_t pos_x, int16_t pos_y, uint8_t a, uint8_t b, uint8_t flags) {
debug(DBG_GAME, "Game::drawCharacter(%p, %d, %d, 0x%X, 0x%X, 0x%X)", dataPtr, pos_x, pos_y, a, b, flags); debug(DBG_GAME, "Game::drawCharacter(%p, %d, %d, 0x%X, 0x%X, 0x%X)", dataPtr, pos_x, pos_y, a, b, flags);
bool var16 = false; // sprite_mirror_y bool var16 = false; // sprite_mirror_y
@ -1604,6 +1530,7 @@ bool Game::hasLevelMap(int level, int room) const {
void Game::loadLevelMap() { void Game::loadLevelMap() {
debug(DBG_GAME, "Game::loadLevelMap() room=%d", _currentRoom); debug(DBG_GAME, "Game::loadLevelMap() room=%d", _currentRoom);
bool widescreenUpdated = false;
_currentIcon = 0xFF; _currentIcon = 0xFF;
switch (_res._type) { switch (_res._type) {
case kResourceTypeAmiga: case kResourceTypeAmiga:
@ -1637,17 +1564,18 @@ void Game::loadLevelMap() {
const int leftRoom = _res._ctData[CT_LEFT_ROOM + _currentRoom]; const int leftRoom = _res._ctData[CT_LEFT_ROOM + _currentRoom];
if (leftRoom > 0 && hasLevelMap(_currentLevel, leftRoom)) { if (leftRoom > 0 && hasLevelMap(_currentLevel, leftRoom)) {
_vid.PC_decodeMap(_currentLevel, leftRoom); _vid.PC_decodeMap(_currentLevel, leftRoom);
_stub->copyRectLeftBorder(Video::GAMESCREEN_W, Video::GAMESCREEN_H, _vid._backLayer); _stub->copyWidescreenLeft(Video::GAMESCREEN_W, Video::GAMESCREEN_H, _vid._backLayer);
} else { } else {
_stub->copyRectLeftBorder(Video::GAMESCREEN_W, Video::GAMESCREEN_H, 0); _stub->copyWidescreenLeft(Video::GAMESCREEN_W, Video::GAMESCREEN_H, 0);
} }
const int rightRoom = _res._ctData[CT_RIGHT_ROOM + _currentRoom]; const int rightRoom = _res._ctData[CT_RIGHT_ROOM + _currentRoom];
if (rightRoom > 0 && hasLevelMap(_currentLevel, rightRoom)) { if (rightRoom > 0 && hasLevelMap(_currentLevel, rightRoom)) {
_vid.PC_decodeMap(_currentLevel, rightRoom); _vid.PC_decodeMap(_currentLevel, rightRoom);
_stub->copyRectRightBorder(Video::GAMESCREEN_W, Video::GAMESCREEN_H, _vid._backLayer); _stub->copyWidescreenRight(Video::GAMESCREEN_W, Video::GAMESCREEN_H, _vid._backLayer);
} else { } else {
_stub->copyRectRightBorder(Video::GAMESCREEN_W, Video::GAMESCREEN_H, 0); _stub->copyWidescreenRight(Video::GAMESCREEN_W, Video::GAMESCREEN_H, 0);
} }
widescreenUpdated = true;
} }
_vid.PC_decodeMap(_currentLevel, _currentRoom); _vid.PC_decodeMap(_currentLevel, _currentRoom);
break; break;
@ -1656,23 +1584,24 @@ void Game::loadLevelMap() {
const int leftRoom = _res._ctData[CT_LEFT_ROOM + _currentRoom]; const int leftRoom = _res._ctData[CT_LEFT_ROOM + _currentRoom];
if (leftRoom > 0 && hasLevelMap(_currentLevel, leftRoom)) { if (leftRoom > 0 && hasLevelMap(_currentLevel, leftRoom)) {
_vid.MAC_decodeMap(_currentLevel, leftRoom); _vid.MAC_decodeMap(_currentLevel, leftRoom);
_stub->copyRectLeftBorder(_vid._w, _vid._h, _vid._backLayer); _stub->copyWidescreenLeft(_vid._w, _vid._h, _vid._backLayer);
} else { } else {
_stub->copyRectLeftBorder(_vid._w, _vid._h, 0); _stub->copyWidescreenLeft(_vid._w, _vid._h, 0);
} }
const int rightRoom = _res._ctData[CT_RIGHT_ROOM + _currentRoom]; const int rightRoom = _res._ctData[CT_RIGHT_ROOM + _currentRoom];
if (rightRoom > 0 && hasLevelMap(_currentLevel, rightRoom)) { if (rightRoom > 0 && hasLevelMap(_currentLevel, rightRoom)) {
_vid.MAC_decodeMap(_currentLevel, rightRoom); _vid.MAC_decodeMap(_currentLevel, rightRoom);
_stub->copyRectRightBorder(_vid._w, _vid._h, _vid._backLayer); _stub->copyWidescreenRight(_vid._w, _vid._h, _vid._backLayer);
} else { } else {
_stub->copyRectRightBorder(_vid._w, _vid._h, 0); _stub->copyWidescreenRight(_vid._w, _vid._h, 0);
} }
widescreenUpdated = true;
} }
_vid.MAC_decodeMap(_currentLevel, _currentRoom); _vid.MAC_decodeMap(_currentLevel, _currentRoom);
break; break;
} }
if (_stub->hasWidescreen() && _widescreenMode == kWidescreenMirrorRoom) { if (!widescreenUpdated) {
_stub->copyRectMirrorBorders(_vid._w, _vid._h, _vid._backLayer); _vid.updateWidescreen();
} }
} }
@ -1737,7 +1666,7 @@ void Game::loadLevelData() {
_res.load(lvl->name, Resource::OT_CT); _res.load(lvl->name, Resource::OT_CT);
_res.load(lvl->name, Resource::OT_PAL); _res.load(lvl->name, Resource::OT_PAL);
_res.load(lvl->name, Resource::OT_RP); _res.load(lvl->name, Resource::OT_RP);
if (_res._isDemo || g_options.use_tiledata) { // use .BNQ/.LEV/(.SGD) instead of .MAP (PC demo) if (_res._isDemo || g_options.use_tile_data) { // use .BNQ/.LEV/(.SGD) instead of .MAP (PC demo)
if (_currentLevel == 0) { if (_currentLevel == 0) {
_res.load(lvl->name, Resource::OT_SGD); _res.load(lvl->name, Resource::OT_SGD);
} }
@ -1858,18 +1787,22 @@ void Game::drawIcon(uint8_t iconNum, int16_t x, int16_t y, uint8_t colMask) {
_vid.markBlockAsDirty(x, y, 16, 16, _vid._layerScale); _vid.markBlockAsDirty(x, y, 16, 16, _vid._layerScale);
} }
void Game::playSound(uint8_t sfxId, uint8_t softVol) { void Game::playSound(uint8_t num, uint8_t softVol) {
if (sfxId < _res._numSfx) { if (num < _res._numSfx) {
SoundFx *sfx = &_res._sfxList[sfxId]; SoundFx *sfx = &_res._sfxList[num];
if (sfx->data) { if (sfx->data) {
MixerChunk mc; const int volume = Mixer::MAX_VOLUME >> (2 * softVol);
mc.data = sfx->data; _mix.play(sfx->data, sfx->len, sfx->freq, volume);
mc.len = sfx->len;
_mix.play(&mc, sfx->freq, Mixer::MAX_VOLUME >> softVol);
} }
} else { } else if (num == 66) {
// open/close inventory (DOS)
} else if (num >= 68 && num <= 75) {
// in-game music // in-game music
_mix.playMusic(sfxId); _mix.playMusic(num);
} else if (num == 77) {
// triggered when Conrad reaches a platform
} else {
warning("Unknown sound num %d", num);
} }
} }
@ -1918,19 +1851,13 @@ void Game::handleInventory() {
case kResourceTypeAmiga: case kResourceTypeAmiga:
case kResourceTypeDOS: { case kResourceTypeDOS: {
// draw inventory background // draw inventory background
int icon_h = 5;
int icon_y = 140;
int icon_num = 31; int icon_num = 31;
do { for (int y = 140; y < 140 + 5 * icon_spr_h; y += icon_spr_h) {
int icon_x = 56; for (int x = 56; x < 56 + 9 * icon_spr_w; x += icon_spr_w) {
int icon_w = 9; drawIcon(icon_num, x, y, 0xF);
do {
drawIcon(icon_num, icon_x, icon_y, 0xF);
++icon_num; ++icon_num;
icon_x += icon_spr_w; }
} while (--icon_w); }
icon_y += icon_spr_h;
} while (--icon_h);
} }
if (_res._type == kResourceTypeAmiga) { if (_res._type == kResourceTypeAmiga) {
// draw outline rectangle // draw outline rectangle

12
game.h
View File

@ -1,7 +1,7 @@
/* /*
* REminiscence - Flashback interpreter * REminiscence - Flashback interpreter
* Copyright (C) 2005-2018 Gregory Montoir (cyx@users.sourceforge.net) * Copyright (C) 2005-2019 Gregory Montoir (cyx@users.sourceforge.net)
*/ */
#ifndef GAME_H__ #ifndef GAME_H__
@ -28,7 +28,7 @@ struct Game {
enum { enum {
kIngameSaveSlot = 0, kIngameSaveSlot = 0,
kAutoSaveSlot = 255, kAutoSaveSlot = 255,
kAutoSaveIntervalMs = 30 * 1000 kAutoSaveIntervalMs = 120 * 1000
}; };
enum { enum {
@ -54,6 +54,9 @@ struct Game {
static const pge_OpcodeProc _pge_opcodeTable[]; static const pge_OpcodeProc _pge_opcodeTable[];
static const uint8_t _pge_modKeysTable[]; static const uint8_t _pge_modKeysTable[];
static const uint8_t _protectionCodeData[]; static const uint8_t _protectionCodeData[];
static const uint8_t _protectionWordData[];
static const uint8_t _protectionNumberDataAmiga[];
static const uint8_t _protectionCodeDataAmiga[];
static const uint8_t _protectionPal[]; static const uint8_t _protectionPal[];
Cutscene _cut; Cutscene _cut;
@ -115,7 +118,6 @@ struct Game {
void showFinalScore(); void showFinalScore();
bool handleConfigPanel(); bool handleConfigPanel();
bool handleContinueAbort(); bool handleContinueAbort();
bool handleProtectionScreen();
void printSaveStateCompleted(); void printSaveStateCompleted();
void drawLevelTexts(); void drawLevelTexts();
void drawStoryTexts(); void drawStoryTexts();
@ -127,7 +129,6 @@ struct Game {
void drawPiege(AnimBufferState *state); void drawPiege(AnimBufferState *state);
void drawObject(const uint8_t *dataPtr, int16_t x, int16_t y, uint8_t flags); void drawObject(const uint8_t *dataPtr, int16_t x, int16_t y, uint8_t flags);
void drawObjectFrame(const uint8_t *bankDataPtr, const uint8_t *dataPtr, int16_t x, int16_t y, uint8_t flags); void drawObjectFrame(const uint8_t *bankDataPtr, const uint8_t *dataPtr, int16_t x, int16_t y, uint8_t flags);
void decodeCharacterFrame(const uint8_t *dataPtr, uint8_t *dstPtr);
void drawCharacter(const uint8_t *dataPtr, int16_t x, int16_t y, uint8_t a, uint8_t b, uint8_t flags); void drawCharacter(const uint8_t *dataPtr, int16_t x, int16_t y, uint8_t a, uint8_t b, uint8_t flags);
int loadMonsterSprites(LivePGE *pge); int loadMonsterSprites(LivePGE *pge);
void playSound(uint8_t sfxId, uint8_t softVol); void playSound(uint8_t sfxId, uint8_t softVol);
@ -135,6 +136,9 @@ struct Game {
void changeLevel(); void changeLevel();
void handleInventory(); void handleInventory();
// protection
bool handleProtectionScreenShape();
bool handleProtectionScreenWords();
// pieges // pieges
bool _pge_playAnimSound; bool _pge_playAnimSound;

View File

@ -1,7 +1,7 @@
/* /*
* REminiscence - Flashback interpreter * REminiscence - Flashback interpreter
* Copyright (C) 2005-2018 Gregory Montoir (cyx@users.sourceforge.net) * Copyright (C) 2005-2019 Gregory Montoir (cyx@users.sourceforge.net)
*/ */
#include "graphics.h" #include "graphics.h"

View File

@ -1,7 +1,7 @@
/* /*
* REminiscence - Flashback interpreter * REminiscence - Flashback interpreter
* Copyright (C) 2005-2018 Gregory Montoir (cyx@users.sourceforge.net) * Copyright (C) 2005-2019 Gregory Montoir (cyx@users.sourceforge.net)
*/ */
#ifndef GRAPHICS_H__ #ifndef GRAPHICS_H__

View File

@ -1,7 +1,7 @@
/* /*
* REminiscence - Flashback interpreter * REminiscence - Flashback interpreter
* Copyright (C) 2005-2018 Gregory Montoir (cyx@users.sourceforge.net) * Copyright (C) 2005-2019 Gregory Montoir (cyx@users.sourceforge.net)
*/ */
#ifndef INTERN_H__ #ifndef INTERN_H__
@ -46,6 +46,17 @@ inline int16_t ADDC_S16(int a, int b) {
return a; return a;
} }
inline int16_t S8_to_S16(int a) {
if (a < -128) {
return -32768;
} else if (a > 127) {
return 32767;
} else {
const uint8_t u8 = (a ^ 0x80);
return ((u8 << 8) | u8) - 32768;
}
}
template<typename T> template<typename T>
inline void SWAP(T &a, T &b) { inline void SWAP(T &a, T &b) {
T tmp = a; T tmp = a;
@ -106,6 +117,7 @@ enum WidescreenMode {
kWidescreenNone, kWidescreenNone,
kWidescreenAdjacentRooms, kWidescreenAdjacentRooms,
kWidescreenMirrorRoom, kWidescreenMirrorRoom,
kWidescreenBlur,
}; };
struct Options { struct Options {
@ -113,13 +125,17 @@ struct Options {
bool enable_password_menu; bool enable_password_menu;
bool enable_language_selection; bool enable_language_selection;
bool fade_out_palette; bool fade_out_palette;
bool use_tiledata; bool use_tile_data;
bool use_text_cutscenes; bool use_text_cutscenes;
bool use_seq_cutscenes; bool use_seq_cutscenes;
bool use_words_protection;
bool use_white_tshirt;
bool play_asc_cutscene; bool play_asc_cutscene;
bool play_caillou_cutscene; bool play_caillou_cutscene;
bool play_metro_cutscene; bool play_metro_cutscene;
bool play_serrure_cutscene; bool play_serrure_cutscene;
bool play_carte_cutscene;
bool play_gamesaved_sound;
}; };
struct Color { struct Color {
@ -155,7 +171,7 @@ struct InitPGE {
int16_t pos_y; int16_t pos_y;
uint16_t obj_node_number; uint16_t obj_node_number;
uint16_t life; uint16_t life;
int16_t counter_values[4]; int16_t counter_values[4]; // messages
uint8_t object_type; uint8_t object_type;
uint8_t init_room; uint8_t init_room;
uint8_t room_location; uint8_t room_location;
@ -177,7 +193,7 @@ struct LivePGE {
uint8_t anim_seq; uint8_t anim_seq;
uint8_t room_location; uint8_t room_location;
int16_t life; int16_t life;
int16_t counter_value; int16_t counter_value; // msg
uint8_t collision_slot; uint8_t collision_slot;
uint8_t next_inventory_PGE; uint8_t next_inventory_PGE;
uint8_t current_inventory_PGE; uint8_t current_inventory_PGE;
@ -267,6 +283,7 @@ struct SoundFx {
uint16_t len; uint16_t len;
uint16_t freq; uint16_t freq;
uint8_t *data; uint8_t *data;
int8_t peak;
}; };
extern Options g_options; extern Options g_options;

View File

@ -1,7 +1,7 @@
/* /*
* REminiscence - Flashback interpreter * REminiscence - Flashback interpreter
* Copyright (C) 2005-2018 Gregory Montoir (cyx@users.sourceforge.net) * Copyright (C) 2005-2019 Gregory Montoir (cyx@users.sourceforge.net)
*/ */
#include <SDL.h> #include <SDL.h>
@ -22,7 +22,7 @@ static const char *USAGE =
" --savepath=PATH Path to save files (default '.')\n" " --savepath=PATH Path to save files (default '.')\n"
" --levelnum=NUM Start to level, bypass introduction\n" " --levelnum=NUM Start to level, bypass introduction\n"
" --fullscreen Fullscreen display\n" " --fullscreen Fullscreen display\n"
" --widescreen=MODE 16:9 display\n" " --widescreen=MODE 16:9 display (adjacent,mirror,blur,none)\n"
" --scaler=NAME@X Graphics scaler (default 'scale@3')\n" " --scaler=NAME@X Graphics scaler (default 'scale@3')\n"
" --language=LANG Language (fr,en,de,sp,it,jp)\n" " --language=LANG Language (fr,en,de,sp,it,jp)\n"
" --autosave Save game state automatically\n" " --autosave Save game state automatically\n"
@ -36,6 +36,7 @@ static int detectVersion(FileSystem *fs) {
} table[] = { } table[] = {
{ "DEMO_UK.ABA", kResourceTypeDOS, "DOS (Demo)" }, { "DEMO_UK.ABA", kResourceTypeDOS, "DOS (Demo)" },
{ "INTRO.SEQ", kResourceTypeDOS, "DOS CD" }, { "INTRO.SEQ", kResourceTypeDOS, "DOS CD" },
{ "MENU1SSI.MAP", kResourceTypeDOS, "DOS SSI" },
{ "LEVEL1.MAP", kResourceTypeDOS, "DOS" }, { "LEVEL1.MAP", kResourceTypeDOS, "DOS" },
{ "LEVEL1.BNQ", kResourceTypeDOS, "DOS (Demo)" }, { "LEVEL1.BNQ", kResourceTypeDOS, "DOS (Demo)" },
{ "LEVEL1.LEV", kResourceTypeAmiga, "Amiga" }, { "LEVEL1.LEV", kResourceTypeAmiga, "Amiga" },
@ -90,10 +91,14 @@ static void initOptions() {
g_options.fade_out_palette = true; g_options.fade_out_palette = true;
g_options.use_text_cutscenes = false; g_options.use_text_cutscenes = false;
g_options.use_seq_cutscenes = true; g_options.use_seq_cutscenes = true;
g_options.use_words_protection = false;
g_options.use_white_tshirt = false;
g_options.play_asc_cutscene = false; g_options.play_asc_cutscene = false;
g_options.play_caillou_cutscene = false; g_options.play_caillou_cutscene = false;
g_options.play_metro_cutscene = false; g_options.play_metro_cutscene = false;
g_options.play_serrure_cutscene = false; g_options.play_serrure_cutscene = false;
g_options.play_carte_cutscene = false;
g_options.play_gamesaved_sound = false;
// read configuration file // read configuration file
struct { struct {
const char *name; const char *name;
@ -103,13 +108,17 @@ static void initOptions() {
{ "enable_password_menu", &g_options.enable_password_menu }, { "enable_password_menu", &g_options.enable_password_menu },
{ "enable_language_selection", &g_options.enable_language_selection }, { "enable_language_selection", &g_options.enable_language_selection },
{ "fade_out_palette", &g_options.fade_out_palette }, { "fade_out_palette", &g_options.fade_out_palette },
{ "use_tiledata", &g_options.use_tiledata }, { "use_tile_data", &g_options.use_tile_data },
{ "use_text_cutscenes", &g_options.use_text_cutscenes }, { "use_text_cutscenes", &g_options.use_text_cutscenes },
{ "use_seq_cutscenes", &g_options.use_seq_cutscenes }, { "use_seq_cutscenes", &g_options.use_seq_cutscenes },
{ "use_words_protection", &g_options.use_words_protection },
{ "use_white_tshirt", &g_options.use_white_tshirt },
{ "play_asc_cutscene", &g_options.play_asc_cutscene }, { "play_asc_cutscene", &g_options.play_asc_cutscene },
{ "play_caillou_cutscene", &g_options.play_caillou_cutscene }, { "play_caillou_cutscene", &g_options.play_caillou_cutscene },
{ "play_metro_cutscene", &g_options.play_metro_cutscene }, { "play_metro_cutscene", &g_options.play_metro_cutscene },
{ "play_serrure_cutscene", &g_options.play_serrure_cutscene }, { "play_serrure_cutscene", &g_options.play_serrure_cutscene },
{ "play_carte_cutscene", &g_options.play_carte_cutscene },
{ "play_gamesaved_sound", &g_options.play_gamesaved_sound },
{ 0, 0 } { 0, 0 }
}; };
static const char *filename = "rs.cfg"; static const char *filename = "rs.cfg";
@ -147,7 +156,7 @@ static void initOptions() {
} }
static void parseScaler(char *name, ScalerParameters *scalerParameters) { static void parseScaler(char *name, ScalerParameters *scalerParameters) {
struct { static const struct {
const char *name; const char *name;
int type; int type;
const Scaler *scaler; const Scaler *scaler;
@ -158,7 +167,7 @@ static void parseScaler(char *name, ScalerParameters *scalerParameters) {
#ifdef USE_STATIC_SCALER #ifdef USE_STATIC_SCALER
{ "nearest", kScalerTypeInternal, &scaler_nearest }, { "nearest", kScalerTypeInternal, &scaler_nearest },
{ "tv2x", kScalerTypeInternal, &scaler_tv2x }, { "tv2x", kScalerTypeInternal, &scaler_tv2x },
{ "xbrz", kScalerTypeInternal, &scaler_xbrz }, { "xbr", kScalerTypeInternal, &scaler_xbr },
#endif #endif
{ 0, -1 } { 0, -1 }
}; };
@ -200,6 +209,7 @@ static WidescreenMode parseWidescreen(const char *mode) {
} modes[] = { } modes[] = {
{ "adjacent", kWidescreenAdjacentRooms }, { "adjacent", kWidescreenAdjacentRooms },
{ "mirror", kWidescreenMirrorRoom }, { "mirror", kWidescreenMirrorRoom },
{ "blur", kWidescreenBlur },
{ 0, kWidescreenNone }, { 0, kWidescreenNone },
}; };
for (int i = 0; modes[i].name; ++i) { for (int i = 0; modes[i].name; ++i) {
@ -207,8 +217,8 @@ static WidescreenMode parseWidescreen(const char *mode) {
return modes[i].mode; return modes[i].mode;
} }
} }
warning("Unhandled widecreen mode '%s', defaults to adjacent rooms", mode); warning("Unhandled widecreen mode '%s', defaults to 16:9 blur", mode);
return kWidescreenAdjacentRooms; // default value return kWidescreenBlur;
} }
int main(int argc, char *argv[]) { int main(int argc, char *argv[]) {
@ -303,7 +313,7 @@ int main(int argc, char *argv[]) {
const Language language = (forcedLanguage == -1) ? detectLanguage(&fs) : (Language)forcedLanguage; const Language language = (forcedLanguage == -1) ? detectLanguage(&fs) : (Language)forcedLanguage;
SystemStub *stub = SystemStub_SDL_create(); SystemStub *stub = SystemStub_SDL_create();
Game *g = new Game(stub, &fs, savePath, levelNum, (ResourceType)version, language, widescreen, autoSave); Game *g = new Game(stub, &fs, savePath, levelNum, (ResourceType)version, language, widescreen, autoSave);
stub->init(g_caption, g->_vid._w, g->_vid._h, fullscreen, widescreen != kWidescreenNone, &scalerParameters); stub->init(g_caption, g->_vid._w, g->_vid._h, fullscreen, widescreen, &scalerParameters);
g->run(); g->run();
delete g; delete g;
stub->destroy(); stub->destroy();

View File

@ -1,7 +1,7 @@
/* /*
* REminiscence - Flashback interpreter * REminiscence - Flashback interpreter
* Copyright (C) 2005-2018 Gregory Montoir (cyx@users.sourceforge.net) * Copyright (C) 2005-2019 Gregory Montoir (cyx@users.sourceforge.net)
*/ */
#include "game.h" #include "game.h"
@ -89,17 +89,20 @@ void Menu::drawString2(const char *str, int16_t y, int16_t x) {
void Menu::loadPicture(const char *prefix) { void Menu::loadPicture(const char *prefix) {
debug(DBG_MENU, "Menu::loadPicture('%s')", prefix); debug(DBG_MENU, "Menu::loadPicture('%s')", prefix);
static const int kPictureW = 256;
static const int kPictureH = 224;
_res->load_MAP_menu(prefix, _res->_scratchBuffer); _res->load_MAP_menu(prefix, _res->_scratchBuffer);
for (int i = 0; i < 4; ++i) { for (int i = 0; i < 4; ++i) {
for (int y = 0; y < 224; ++y) { for (int y = 0; y < kPictureH; ++y) {
for (int x = 0; x < 64; ++x) { for (int x = 0; x < kPictureW / 4; ++x) {
_vid->_frontLayer[i + x * 4 + 256 * y] = _res->_scratchBuffer[0x3800 * i + x + 64 * y]; _vid->_frontLayer[i + x * 4 + kPictureW * y] = _res->_scratchBuffer[0x3800 * i + x + 64 * y];
} }
} }
} }
memcpy(_vid->_backLayer, _vid->_frontLayer, _vid->_layerSize); memcpy(_vid->_backLayer, _vid->_frontLayer, _vid->_layerSize);
_res->load_PAL_menu(prefix, _res->_scratchBuffer); _res->load_PAL_menu(prefix, _res->_scratchBuffer);
_stub->setPalette(_res->_scratchBuffer, 256); _stub->setPalette(_res->_scratchBuffer, 256);
_vid->updateWidescreen();
} }
void Menu::handleInfoScreen() { void Menu::handleInfoScreen() {
@ -386,7 +389,7 @@ void Menu::handleTitleScreen() {
} }
} }
while (!quitLoop) { while (!quitLoop && !_stub->_pi.quit) {
int selectedItem = -1; int selectedItem = -1;
int previousLanguage = currentLanguage; int previousLanguage = currentLanguage;
@ -497,9 +500,6 @@ void Menu::handleTitleScreen() {
_vid->updateScreen(); _vid->updateScreen();
_stub->sleep(EVENTS_DELAY); _stub->sleep(EVENTS_DELAY);
_stub->processEvents(); _stub->processEvents();
if (_stub->_pi.quit) {
break;
}
} }
} }

2
menu.h
View File

@ -1,7 +1,7 @@
/* /*
* REminiscence - Flashback interpreter * REminiscence - Flashback interpreter
* Copyright (C) 2005-2018 Gregory Montoir (cyx@users.sourceforge.net) * Copyright (C) 2005-2019 Gregory Montoir (cyx@users.sourceforge.net)
*/ */
#ifndef MENU_H__ #ifndef MENU_H__

View File

@ -1,7 +1,7 @@
/* /*
* REminiscence - Flashback interpreter * REminiscence - Flashback interpreter
* Copyright (C) 2005-2018 Gregory Montoir (cyx@users.sourceforge.net) * Copyright (C) 2005-2019 Gregory Montoir (cyx@users.sourceforge.net)
*/ */
#include "mixer.h" #include "mixer.h"
@ -9,8 +9,9 @@
#include "util.h" #include "util.h"
Mixer::Mixer(FileSystem *fs, SystemStub *stub) Mixer::Mixer(FileSystem *fs, SystemStub *stub)
: _stub(stub), _musicType(MT_NONE), _mod(this, fs), _ogg(this, fs), _sfx(this) { : _stub(stub), _musicType(MT_NONE), _cpc(this, fs), _mod(this, fs), _ogg(this, fs), _sfx(this) {
_musicTrack = -1; _musicTrack = -1;
_backgroundMusicType = MT_NONE;
} }
void Mixer::init() { void Mixer::init() {
@ -32,14 +33,14 @@ void Mixer::setPremixHook(PremixHook premixHook, void *userData) {
_premixHookData = userData; _premixHookData = userData;
} }
void Mixer::play(const MixerChunk *mc, uint16_t freq, uint8_t volume) { void Mixer::play(const uint8_t *data, uint32_t len, uint16_t freq, uint8_t volume) {
debug(DBG_SND, "Mixer::play(%d, %d)", freq, volume); debug(DBG_SND, "Mixer::play(%d, %d)", freq, volume);
LockAudioStack las(_stub); LockAudioStack las(_stub);
MixerChannel *ch = 0; MixerChannel *ch = 0;
for (int i = 0; i < NUM_CHANNELS; ++i) { for (int i = 0; i < NUM_CHANNELS; ++i) {
MixerChannel *cur = &_channels[i]; MixerChannel *cur = &_channels[i];
if (cur->active) { if (cur->active) {
if (cur->chunk.data == mc->data) { if (cur->chunk.data == data) {
cur->chunkPos = 0; cur->chunkPos = 0;
return; return;
} }
@ -51,18 +52,19 @@ void Mixer::play(const MixerChunk *mc, uint16_t freq, uint8_t volume) {
if (ch) { if (ch) {
ch->active = true; ch->active = true;
ch->volume = volume; ch->volume = volume;
ch->chunk = *mc; ch->chunk.data = data;
ch->chunk.len = len;
ch->chunkPos = 0; ch->chunkPos = 0;
ch->chunkInc = (freq << FRAC_BITS) / _stub->getOutputSampleRate(); ch->chunkInc = (freq << FRAC_BITS) / _stub->getOutputSampleRate();
} }
} }
bool Mixer::isPlaying(const MixerChunk *mc) const { bool Mixer::isPlaying(const uint8_t *data) const {
debug(DBG_SND, "Mixer::isPlaying"); debug(DBG_SND, "Mixer::isPlaying");
LockAudioStack las(_stub); LockAudioStack las(_stub);
for (int i = 0; i < NUM_CHANNELS; ++i) { for (int i = 0; i < NUM_CHANNELS; ++i) {
const MixerChannel *ch = &_channels[i]; const MixerChannel *ch = &_channels[i];
if (ch->active && ch->chunk.data == mc->data) { if (ch->active && ch->chunk.data == data) {
return true; return true;
} }
} }
@ -93,15 +95,20 @@ void Mixer::playMusic(int num) {
_musicTrack = num; _musicTrack = num;
return; return;
} }
if (_cpc.playTrack(num - MUSIC_TRACK)) {
_backgroundMusicType = _musicType = MT_CPC;
_musicTrack = num;
return;
}
} }
if (num == 1) { // menu screen if (num == 1) { // menu screen
if (_ogg.playTrack(2)) { if (_cpc.playTrack(2) || _ogg.playTrack(2)) {
_musicType = MT_OGG; _backgroundMusicType = _musicType = MT_OGG;
_musicTrack = 2; _musicTrack = 2;
return; return;
} }
} }
if (_musicType == MT_OGG && isMusicSfx(num)) { // do not play level action music with background music if ((_musicType == MT_OGG || _musicType == MT_CPC) && isMusicSfx(num)) { // do not play level action music with background music
return; return;
} }
if (isMusicSfx(num)) { // level action sequence if (isMusicSfx(num)) { // level action sequence
@ -131,14 +138,29 @@ void Mixer::stopMusic() {
case MT_SFX: case MT_SFX:
_sfx.stop(); _sfx.stop();
break; break;
case MT_CPC:
_cpc.pauseTrack();
break;
} }
_musicType = MT_NONE; _musicType = MT_NONE;
if (_musicTrack != -1) { if (_musicTrack != -1) {
_ogg.resumeTrack(); switch (_backgroundMusicType) {
_musicType = MT_OGG; case MT_OGG:
_ogg.resumeTrack();
_musicType = MT_OGG;
break;
case MT_CPC:
_cpc.resumeTrack();
_musicType = MT_CPC;
break;
default:
break;
}
} }
} }
static const bool kUseNr = false;
static void nr(int16_t *buf, int len) { static void nr(int16_t *buf, int len) {
static int prev = 0; static int prev = 0;
for (int i = 0; i < len; ++i) { for (int i = 0; i < len; ++i) {
@ -163,13 +185,15 @@ void Mixer::mix(int16_t *out, int len) {
ch->active = false; ch->active = false;
break; break;
} }
const int sample = ch->chunk.getPCM(ch->chunkPos >> FRAC_BITS); const int sample = ch->chunk.getPCM(ch->chunkPos >> FRAC_BITS) * ch->volume / Mixer::MAX_VOLUME;
out[pos] = ADDC_S16(out[pos], (sample * ch->volume / Mixer::MAX_VOLUME) << 8); out[pos] = ADDC_S16(out[pos], S8_to_S16(sample));
ch->chunkPos += ch->chunkInc; ch->chunkPos += ch->chunkInc;
} }
} }
} }
nr(out, len); if (kUseNr) {
nr(out, len);
}
} }
void Mixer::mixCallback(void *param, int16_t *buf, int len) { void Mixer::mixCallback(void *param, int16_t *buf, int len) {

12
mixer.h
View File

@ -1,19 +1,20 @@
/* /*
* REminiscence - Flashback interpreter * REminiscence - Flashback interpreter
* Copyright (C) 2005-2018 Gregory Montoir (cyx@users.sourceforge.net) * Copyright (C) 2005-2019 Gregory Montoir (cyx@users.sourceforge.net)
*/ */
#ifndef MIXER_H__ #ifndef MIXER_H__
#define MIXER_H__ #define MIXER_H__
#include "intern.h" #include "intern.h"
#include "cpc_player.h"
#include "mod_player.h" #include "mod_player.h"
#include "ogg_player.h" #include "ogg_player.h"
#include "sfx_player.h" #include "sfx_player.h"
struct MixerChunk { struct MixerChunk {
uint8_t *data; const uint8_t *data;
uint32_t len; uint32_t len;
MixerChunk() MixerChunk()
@ -49,6 +50,7 @@ struct Mixer {
MT_MOD, MT_MOD,
MT_OGG, MT_OGG,
MT_SFX, MT_SFX,
MT_CPC,
}; };
enum { enum {
@ -63,7 +65,9 @@ struct Mixer {
MixerChannel _channels[NUM_CHANNELS]; MixerChannel _channels[NUM_CHANNELS];
PremixHook _premixHook; PremixHook _premixHook;
void *_premixHookData; void *_premixHookData;
MusicType _backgroundMusicType;
MusicType _musicType; MusicType _musicType;
CpcPlayer _cpc;
ModPlayer _mod; ModPlayer _mod;
OggPlayer _ogg; OggPlayer _ogg;
SfxPlayer _sfx; SfxPlayer _sfx;
@ -73,8 +77,8 @@ struct Mixer {
void init(); void init();
void free(); void free();
void setPremixHook(PremixHook premixHook, void *userData); void setPremixHook(PremixHook premixHook, void *userData);
void play(const MixerChunk *mc, uint16_t freq, uint8_t volume); void play(const uint8_t *data, uint32_t len, uint16_t freq, uint8_t volume);
bool isPlaying(const MixerChunk *mc) const; bool isPlaying(const uint8_t *data) const;
uint32_t getSampleRate() const; uint32_t getSampleRate() const;
void stopAll(); void stopAll();
void playMusic(int num); void playMusic(int num);

View File

@ -1,7 +1,7 @@
/* /*
* REminiscence - Flashback interpreter * REminiscence - Flashback interpreter
* Copyright (C) 2005-2018 Gregory Montoir (cyx@users.sourceforge.net) * Copyright (C) 2005-2019 Gregory Montoir (cyx@users.sourceforge.net)
*/ */
#include "file.h" #include "file.h"
@ -203,17 +203,20 @@ bool ModPlayer_impl::load(File *f) {
f->read(_modInfo.patternOrderTable, NUM_PATTERNS); f->read(_modInfo.patternOrderTable, NUM_PATTERNS);
f->readUint32BE(); // 'M.K.', Protracker, 4 channels f->readUint32BE(); // 'M.K.', Protracker, 4 channels
uint16_t n = 0; uint8_t n = 0;
for (int i = 0; i < NUM_PATTERNS; ++i) { for (int i = 0; i < NUM_PATTERNS; ++i) {
if (_modInfo.patternOrderTable[i] != 0) { if (_modInfo.patternOrderTable[i] != 0) {
n = MAX(n, _modInfo.patternOrderTable[i]); n = MAX(n, _modInfo.patternOrderTable[i]);
} }
} }
debug(DBG_MOD, "numPatterns=%d",n + 1); debug(DBG_MOD, "numPatterns=%d",n + 1);
n = (n + 1) * 64 * 4 * 4; // 64 lines of 4 notes per channel const int patternsSize = (n + 1) * 64 * 4 * 4; // 64 lines of 4 notes per channel
_modInfo.patternsTable = (uint8_t *)malloc(n); _modInfo.patternsTable = (uint8_t *)malloc(patternsSize);
assert(_modInfo.patternsTable); if (!_modInfo.patternsTable) {
f->read(_modInfo.patternsTable, n); warning("Unable to allocate %d bytes for .MOD patterns table", patternsSize);
return false;
}
f->read(_modInfo.patternsTable, patternsSize);
for (int s = 0; s < NUM_SAMPLES; ++s) { for (int s = 0; s < NUM_SAMPLES; ++s) {
SampleInfo *si = &_modInfo.samples[s]; SampleInfo *si = &_modInfo.samples[s];
@ -612,8 +615,8 @@ void ModPlayer_impl::mixSamples(int16_t *buf, int samplesLen) {
curLen = 0; curLen = 0;
} }
while (count--) { while (count--) {
const int out = si->getPCM(pos >> FRAC_BITS); const int out = si->getPCM(pos >> FRAC_BITS) * tk->volume / 64;
*mixbuf = ADDC_S16(*mixbuf, (out * tk->volume / 64) << 8); *mixbuf = ADDC_S16(*mixbuf, S8_to_S16(out));
++mixbuf; ++mixbuf;
pos += deltaPos; pos += deltaPos;
} }
@ -676,7 +679,7 @@ void ModPlayer::stop() {
if (_playing) { if (_playing) {
_mix->setPremixHook(0, 0); _mix->setPremixHook(0, 0);
_impl->unload(); _impl->unload();
_playing = true; _playing = false;
} }
} }

View File

@ -1,7 +1,7 @@
/* /*
* REminiscence - Flashback interpreter * REminiscence - Flashback interpreter
* Copyright (C) 2005-2018 Gregory Montoir (cyx@users.sourceforge.net) * Copyright (C) 2005-2019 Gregory Montoir (cyx@users.sourceforge.net)
*/ */
#ifndef MOD_PLAYER_H__ #ifndef MOD_PLAYER_H__

View File

@ -1,7 +1,7 @@
/* /*
* REminiscence - Flashback interpreter * REminiscence - Flashback interpreter
* Copyright (C) 2005-2018 Gregory Montoir (cyx@users.sourceforge.net) * Copyright (C) 2005-2019 Gregory Montoir (cyx@users.sourceforge.net)
*/ */
#ifdef USE_TREMOR #ifdef USE_TREMOR

View File

@ -1,7 +1,7 @@
/* /*
* REminiscence - Flashback interpreter * REminiscence - Flashback interpreter
* Copyright (C) 2005-2018 Gregory Montoir (cyx@users.sourceforge.net) * Copyright (C) 2005-2019 Gregory Montoir (cyx@users.sourceforge.net)
*/ */
#ifndef OGG_PLAYER_H__ #ifndef OGG_PLAYER_H__

View File

@ -1,7 +1,7 @@
/* /*
* REminiscence - Flashback interpreter * REminiscence - Flashback interpreter
* Copyright (C) 2005-2018 Gregory Montoir (cyx@users.sourceforge.net) * Copyright (C) 2005-2019 Gregory Montoir (cyx@users.sourceforge.net)
*/ */
#include "cutscene.h" #include "cutscene.h"
@ -1429,6 +1429,9 @@ int Game::pge_op_setCollisionState2(ObjectOpcodeArgs *args) {
int Game::pge_op_saveState(ObjectOpcodeArgs *args) { int Game::pge_op_saveState(ObjectOpcodeArgs *args) {
_saveStateCompleted = true; _saveStateCompleted = true;
_validSaveState = saveGameState(kIngameSaveSlot); _validSaveState = saveGameState(kIngameSaveSlot);
if (_validSaveState and g_options.play_gamesaved_sound) {
_mix.play(Resource::_gameSavedSoundData, Resource::_gameSavedSoundLen, 8000, Mixer::MAX_VOLUME);
}
return 0xFFFF; return 0xFFFF;
} }
@ -1877,8 +1880,11 @@ int Game::pge_op_setPiegePosModX(ObjectOpcodeArgs *args) {
int Game::pge_op_changeRoom(ObjectOpcodeArgs *args) { int Game::pge_op_changeRoom(ObjectOpcodeArgs *args) {
InitPGE *init_pge_1 = args->pge->init_PGE; InitPGE *init_pge_1 = args->pge->init_PGE;
assert(args->a >= 0 && args->a < 3); assert(args->a >= 0 && args->a < 3);
int16_t _ax = init_pge_1->counter_values[args->a]; const int16_t _ax = init_pge_1->counter_values[args->a];
int16_t _bx = init_pge_1->counter_values[args->a + 1]; if (_ax == 0 && !g_options.bypass_protection) {
warning("pge_op_changeRoom(): protection check");
}
const int16_t _bx = init_pge_1->counter_values[args->a + 1];
LivePGE *live_pge_1 = &_pgeLive[_bx]; LivePGE *live_pge_1 = &_pgeLive[_bx];
LivePGE *live_pge_2 = &_pgeLive[_ax]; LivePGE *live_pge_2 = &_pgeLive[_ax];
int8_t pge_room = live_pge_1->room_location; int8_t pge_room = live_pge_1->room_location;

265
protection.cpp Normal file
View File

@ -0,0 +1,265 @@
/*
* 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) {
uint8_t r = 0;
for (int b = 0; b < 8; ++b) {
if (ch & (1 << b)) {
r |= (1 << (7 - b));
}
}
return r;
}
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 &= ~0x20;
}
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;
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') {
c &= ~0x20;
}
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;
}

View File

@ -1,7 +1,7 @@
/* /*
* REminiscence - Flashback interpreter * REminiscence - Flashback interpreter
* Copyright (C) 2005-2018 Gregory Montoir (cyx@users.sourceforge.net) * Copyright (C) 2005-2019 Gregory Montoir (cyx@users.sourceforge.net)
*/ */
#include "decode_mac.h" #include "decode_mac.h"
@ -53,6 +53,7 @@ Resource::~Resource() {
free(_sfxList); free(_sfxList);
free(_bankData); free(_bankData);
delete _aba; delete _aba;
delete _mac;
} }
void Resource::init() { void Resource::init() {
@ -138,10 +139,6 @@ void Resource::load_DEM(const char *filename) {
void Resource::load_FIB(const char *fileName) { void Resource::load_FIB(const char *fileName) {
debug(DBG_RES, "Resource::load_FIB('%s')", fileName); debug(DBG_RES, "Resource::load_FIB('%s')", fileName);
static const uint8_t fibonacciTable[] = {
0xDE, 0xEB, 0xF3, 0xF8, 0xFB, 0xFD, 0xFE, 0xFF,
0x00, 0x01, 0x02, 0x03, 0x05, 0x08, 0x0D, 0x15
};
snprintf(_entryName, sizeof(_entryName), "%s.FIB", fileName); snprintf(_entryName, sizeof(_entryName), "%s.FIB", fileName);
File f; File f;
if (f.open(_entryName, "rb", _fs)) { if (f.open(_entryName, "rb", _fs)) {
@ -150,37 +147,48 @@ void Resource::load_FIB(const char *fileName) {
if (!_sfxList) { if (!_sfxList) {
error("Unable to allocate SoundFx table"); error("Unable to allocate SoundFx table");
} }
int i; for (int i = 0; i < _numSfx; ++i) {
for (i = 0; i < _numSfx; ++i) {
SoundFx *sfx = &_sfxList[i]; SoundFx *sfx = &_sfxList[i];
sfx->offset = f.readUint32LE(); sfx->offset = f.readUint32LE();
sfx->len = f.readUint16LE(); sfx->len = f.readUint16LE();
sfx->freq = 6000; sfx->freq = 6000;
sfx->data = 0; sfx->data = 0;
} }
for (i = 0; i < _numSfx; ++i) { for (int i = 0; i < _numSfx; ++i) {
SoundFx *sfx = &_sfxList[i]; SoundFx *sfx = &_sfxList[i];
if (sfx->len == 0) { if (sfx->len == 0) {
continue; continue;
} }
f.seek(sfx->offset); f.seek(sfx->offset);
uint8_t *data = (uint8_t *)malloc(sfx->len * 2); const int len = (sfx->len * 2) - 1;
uint8_t *data = (uint8_t *)malloc(len);
if (!data) { if (!data) {
error("Unable to allocate SoundFx data buffer"); error("Unable to allocate SoundFx data buffer");
} }
sfx->data = data; sfx->data = data;
uint8_t c = f.readByte();
// Fibonacci-delta decoding
static const int8_t codeToDelta[16] = { -34, -21, -13, -8, -5, -3, -2, -1, 0, 1, 2, 3, 5, 8, 13, 21 };
int c = (int8_t)f.readByte();
*data++ = c; *data++ = c;
*data++ = c; sfx->peak = ABS(c);
uint16_t sz = sfx->len - 1;
while (sz--) { for (int j = 1; j < sfx->len; ++j) {
uint8_t d = f.readByte(); const uint8_t d = f.readByte();
c += fibonacciTable[d >> 4];
*data++ = c; c += codeToDelta[d >> 4];
c += fibonacciTable[d & 15]; *data++ = CLIP(c, -128, 127);
*data++ = c; if (ABS(c) > sfx->peak) {
sfx->peak = ABS(c);
}
c += codeToDelta[d & 15];
*data++ = CLIP(c, -128, 127);
if (ABS(c) > sfx->peak) {
sfx->peak = ABS(c);
}
} }
sfx->len *= 2; sfx->len = len;
} }
if (f.ioErr()) { if (f.ioErr()) {
error("I/O error when reading '%s'", _entryName); error("I/O error when reading '%s'", _entryName);
@ -190,6 +198,19 @@ void Resource::load_FIB(const char *fileName) {
} }
} }
static void normalizeSPL(SoundFx *sfx) {
static const int kGain = 2;
sfx->peak = ABS(sfx->data[0]);
for (int i = 1; i < sfx->len; ++i) {
const int8_t sample = sfx->data[i];
if (ABS(sample) > sfx->peak) {
sfx->peak = ABS(sample);
}
sfx->data[i] = sample / kGain;
}
}
void Resource::load_SPL_demo() { void Resource::load_SPL_demo() {
_numSfx = NUM_SFXS; _numSfx = NUM_SFXS;
_sfxList = (SoundFx *)calloc(_numSfx, sizeof(SoundFx)); _sfxList = (SoundFx *)calloc(_numSfx, sizeof(SoundFx));
@ -207,6 +228,7 @@ void Resource::load_SPL_demo() {
sfx->offset = 0; sfx->offset = 0;
sfx->len = size; sfx->len = size;
sfx->freq = kPaulaFreq / 650; sfx->freq = kPaulaFreq / 650;
normalizeSPL(sfx);
} }
} }
} }
@ -277,7 +299,7 @@ void Resource::load_CMP_menu(const char *fileName) {
error("Failed to allocate CMP temporary buffer"); error("Failed to allocate CMP temporary buffer");
} }
f.read(tmp, size); f.read(tmp, size);
if (!delphine_unpack(_scratchBuffer, kScratchBufferSize, tmp, size)) { if (!bytekiller_unpack(_scratchBuffer, kScratchBufferSize, tmp, size)) {
error("Bad CRC for %s", fileName); error("Bad CRC for %s", fileName);
} }
free(tmp); free(tmp);
@ -668,7 +690,7 @@ void Resource::load(const char *objName, int objType, const char *ext) {
_pal = dat; _pal = dat;
break; break;
case OT_CT: case OT_CT:
if (!delphine_unpack((uint8_t *)_ctData, sizeof(_ctData), dat, size)) { if (!bytekiller_unpack((uint8_t *)_ctData, sizeof(_ctData), dat, size)) {
error("Bad CRC for '%s'", _entryName); error("Bad CRC for '%s'", _entryName);
} }
free(dat); free(dat);
@ -736,7 +758,7 @@ void Resource::load_CT(File *pf) {
error("Unable to allocate CT buffer"); error("Unable to allocate CT buffer");
} else { } else {
pf->read(tmp, len); pf->read(tmp, len);
if (!delphine_unpack((uint8_t *)_ctData, sizeof(_ctData), tmp, len)) { if (!bytekiller_unpack((uint8_t *)_ctData, sizeof(_ctData), tmp, len)) {
error("Bad CRC for collision data"); error("Bad CRC for collision data");
} }
free(tmp); free(tmp);
@ -936,7 +958,7 @@ void Resource::load_OBC(File *f) {
} }
f->seek(4); f->seek(4);
f->read(packedData, packedSize); f->read(packedData, packedSize);
if (!delphine_unpack(tmp, unpackedSize, packedData, packedSize)) { if (!bytekiller_unpack(tmp, unpackedSize, packedData, packedSize)) {
error("Bad CRC for compressed object data"); error("Bad CRC for compressed object data");
} }
free(packedData); free(packedData);
@ -1153,7 +1175,7 @@ void Resource::load_CMP(File *pf) {
} }
if (data[0].packedSize == data[0].size) { if (data[0].packedSize == data[0].size) {
memcpy(_pol, tmp + data[0].offset, data[0].packedSize); memcpy(_pol, tmp + data[0].offset, data[0].packedSize);
} else if (!delphine_unpack(_pol, data[0].size, tmp + data[0].offset, data[0].packedSize)) { } else if (!bytekiller_unpack(_pol, data[0].size, tmp + data[0].offset, data[0].packedSize)) {
error("Bad CRC for cutscene polygon data"); error("Bad CRC for cutscene polygon data");
} }
_cmd = (uint8_t *)malloc(data[1].size); _cmd = (uint8_t *)malloc(data[1].size);
@ -1162,7 +1184,7 @@ void Resource::load_CMP(File *pf) {
} }
if (data[1].packedSize == data[1].size) { if (data[1].packedSize == data[1].size) {
memcpy(_cmd, tmp + data[1].offset, data[1].packedSize); memcpy(_cmd, tmp + data[1].offset, data[1].packedSize);
} else if (!delphine_unpack(_cmd, data[1].size, tmp + data[1].offset, data[1].packedSize)) { } else if (!bytekiller_unpack(_cmd, data[1].size, tmp + data[1].offset, data[1].packedSize)) {
error("Bad CRC for cutscene command data"); error("Bad CRC for cutscene command data");
} }
free(tmp); free(tmp);
@ -1229,15 +1251,18 @@ void Resource::load_SPL(File *f) {
} }
debug(DBG_RES, "sfx=%d size=%d", i, size); debug(DBG_RES, "sfx=%d size=%d", i, size);
assert(size != 0 && (size & 1) == 0); assert(size != 0 && (size & 1) == 0);
if (i != 64) { if (i == 64) {
warning("Skipping sound #%d (%s) size %d", i, _splNames[i], size);
f->seek(offset + size);
} else {
_sfxList[i].offset = offset; _sfxList[i].offset = offset;
_sfxList[i].len = size;
_sfxList[i].freq = kPaulaFreq / 650; _sfxList[i].freq = kPaulaFreq / 650;
_sfxList[i].data = (uint8_t *)malloc(size); _sfxList[i].data = (uint8_t *)malloc(size);
assert(_sfxList[i].data); if (_sfxList[i].data) {
f->read(_sfxList[i].data, size); f->read(_sfxList[i].data, size);
} else { _sfxList[i].len = size;
f->seek(offset + size); normalizeSPL(&_sfxList[i]);
}
} }
offset += size; offset += size;
} }
@ -1278,7 +1303,7 @@ void Resource::load_SGD(File *f) {
if (!_sgd) { if (!_sgd) {
error("Unable to allocate SGD buffer"); error("Unable to allocate SGD buffer");
} }
if (!delphine_unpack(_sgd, size, tmp, len)) { if (!bytekiller_unpack(_sgd, size, tmp, len)) {
error("Bad CRC for SGD data"); error("Bad CRC for SGD data");
} }
free(tmp); free(tmp);
@ -1310,12 +1335,12 @@ void Resource::load_SPM(File *f) {
if (!_spr1) { if (!_spr1) {
error("Unable to allocate SPR1 buffer"); error("Unable to allocate SPR1 buffer");
} }
if (!delphine_unpack(_spr1, size, tmp, len)) { if (!bytekiller_unpack(_spr1, size, tmp, len)) {
error("Bad CRC for SPM data"); error("Bad CRC for SPM data");
} }
} else { } else {
assert(size <= sizeof(_sprm)); assert(size <= sizeof(_sprm));
if (!delphine_unpack(_sprm, sizeof(_sprm), tmp, len)) { if (!bytekiller_unpack(_sprm, sizeof(_sprm), tmp, len)) {
error("Bad CRC for SPM data"); error("Bad CRC for SPM data");
} }
} }
@ -1391,7 +1416,7 @@ uint8_t *Resource::loadBankData(uint16_t num) {
} else { } else {
assert(dataOffset > 4); assert(dataOffset > 4);
assert(size == (int)READ_BE_UINT32(data - 4)); assert(size == (int)READ_BE_UINT32(data - 4));
if (!delphine_unpack(_bankDataHead, _bankDataTail - _bankDataHead, data, 0)) { if (!bytekiller_unpack(_bankDataHead, _bankDataTail - _bankDataHead, data, 0)) {
error("Bad CRC for bank data %d", num); error("Bad CRC for bank data %d", num);
} }
} }

View File

@ -1,7 +1,7 @@
/* /*
* REminiscence - Flashback interpreter * REminiscence - Flashback interpreter
* Copyright (C) 2005-2018 Gregory Montoir (cyx@users.sourceforge.net) * Copyright (C) 2005-2019 Gregory Montoir (cyx@users.sourceforge.net)
*/ */
#ifndef RESOURCE_H__ #ifndef RESOURCE_H__
@ -119,6 +119,8 @@ struct Resource {
static const uint16_t _voicesOffsetsTable[]; static const uint16_t _voicesOffsetsTable[];
static const uint32_t _spmOffsetsTable[]; static const uint32_t _spmOffsetsTable[];
static const char *_splNames[]; static const char *_splNames[];
static const uint8_t _gameSavedSoundData[];
static const uint16_t _gameSavedSoundLen;
FileSystem *_fs; FileSystem *_fs;
ResourceType _type; ResourceType _type;

View File

@ -74,7 +74,7 @@ uint8_t *ResourceAba::loadEntry(const char *name, uint32_t *size) {
free(tmp); free(tmp);
return 0; return 0;
} }
const bool ret = delphine_unpack(dst, e->size, tmp, e->compressedSize); const bool ret = bytekiller_unpack(dst, e->size, tmp, e->compressedSize);
if (!ret) { if (!ret) {
error("Bad CRC for '%s'", name); error("Bad CRC for '%s'", name);
} }

20
rs.cfg
View File

@ -1,4 +1,4 @@
# display copy protection shapes # display copy protection (shapes or words lookup)
bypass_protection=true bypass_protection=true
# use original password level selection menu screen # use original password level selection menu screen
@ -11,22 +11,34 @@ enable_language_selection=true
fade_out_palette=false fade_out_palette=false
# use .BNQ & .LEV datafiles (tile based rendering) for backgrounds (instead of .MAP) # use .BNQ & .LEV datafiles (tile based rendering) for backgrounds (instead of .MAP)
use_tiledata=false use_tile_data=false
# display text instead of playing the polygon cutscenes # display text instead of playing the polygon cutscenes
use_text_cutscenes=false use_text_cutscenes=false
# enable playback of .SEQ cutscenes (always use polygon cutscenes if false) # enable playback of PC CD .SEQ cutscenes (use polygon cutscenes if false)
use_seq_cutscenes=true use_seq_cutscenes=true
# if copy protection is enabled, display the words manual lookup screen (as in DOS SSI version).
use_words_protection=false
# white t-shirt for Conrad
use_white_tshirt=false
# enable playback of 'ASC' cutscene # enable playback of 'ASC' cutscene
play_asc_cutscene=false play_asc_cutscene=true
# enable playback of 'CAILLOU-F.SET' cutscene # enable playback of 'CAILLOU-F.SET' cutscene
play_caillou_cutscene=true play_caillou_cutscene=true
# enable playback of 'CARTE' cutscene
play_carte_cutscene=true
# enable playback of 'METRO' cutscene # enable playback of 'METRO' cutscene
play_metro_cutscene=false play_metro_cutscene=false
# enable playback of 'SERRURE' cutscene # enable playback of 'SERRURE' cutscene
play_serrure_cutscene=true play_serrure_cutscene=true
# play 'Game saved' sample when saving with level checkpoints (as in the 3DO version)
play_gamesaved_sound=true

View File

@ -1,7 +1,7 @@
/* /*
* REminiscence - Flashback interpreter * REminiscence - Flashback interpreter
* Copyright (C) 2005-2018 Gregory Montoir (cyx@users.sourceforge.net) * Copyright (C) 2005-2019 Gregory Montoir (cyx@users.sourceforge.net)
*/ */
#include "scaler.h" #include "scaler.h"

View File

@ -1,7 +1,7 @@
/* /*
* REminiscence - Flashback interpreter * REminiscence - Flashback interpreter
* Copyright (C) 2005-2018 Gregory Montoir (cyx@users.sourceforge.net) * Copyright (C) 2005-2019 Gregory Montoir (cyx@users.sourceforge.net)
*/ */
#ifndef SCALER_H__ #ifndef SCALER_H__
@ -34,7 +34,7 @@ const Scaler *findScaler(const char *name);
#ifdef USE_STATIC_SCALER #ifdef USE_STATIC_SCALER
extern const Scaler scaler_nearest; extern const Scaler scaler_nearest;
extern const Scaler scaler_tv2x; extern const Scaler scaler_tv2x;
extern const Scaler scaler_xbrz; extern const Scaler scaler_xbr;
#endif #endif
#endif // SCALER_H__ #endif // SCALER_H__

View File

@ -68,3 +68,54 @@ void saveTGA(const char *filename, const uint8_t *rgba, int w, int h) {
} }
} }
} }
static const uint16_t TAG_BM = 0x4D42;
void saveBMP(const char *filename, const uint8_t *bits, const uint8_t *pal, int w, int h) {
File f;
if (f.open(filename, "wb", ".")) {
const int alignWidth = (w + 3) & ~3;
const int imageSize = alignWidth * h;
// Write file header
f.writeUint16LE(TAG_BM);
f.writeUint32LE(14 + 40 + 4 * 256 + imageSize);
f.writeUint16LE(0); // reserved1
f.writeUint16LE(0); // reserved2
f.writeUint32LE(14 + 40 + 4 * 256);
// Write info header
f.writeUint32LE(40);
f.writeUint32LE(w);
f.writeUint32LE(h);
f.writeUint16LE(1); // planes
f.writeUint16LE(8); // bit_count
f.writeUint32LE(0); // compression
f.writeUint32LE(imageSize); // size_image
f.writeUint32LE(0); // x_pels_per_meter
f.writeUint32LE(0); // y_pels_per_meter
f.writeUint32LE(0); // num_colors_used
f.writeUint32LE(0); // num_colors_important
// Write palette data
for (int i = 0; i < 256; ++i) {
f.writeByte(pal[2]);
f.writeByte(pal[1]);
f.writeByte(pal[0]);
f.writeByte(0);
pal += 3;
}
// Write bitmap data
const int pitch = w;
bits += h * pitch;
for (int i = 0; i < h; ++i) {
bits -= pitch;
f.write(bits, w);
int pad = alignWidth - w;
while (pad--) {
f.writeByte(0);
}
}
}
}

View File

@ -5,5 +5,6 @@
#include <stdint.h> #include <stdint.h>
void saveTGA(const char *filename, const uint8_t *rgb, int w, int h); void saveTGA(const char *filename, const uint8_t *rgb, int w, int h);
void saveBMP(const char *filename, const uint8_t *bits, const uint8_t *pal, int w, int h);
#endif #endif

View File

@ -1,7 +1,7 @@
/* /*
* REminiscence - Flashback interpreter * REminiscence - Flashback interpreter
* Copyright (C) 2005-2018 Gregory Montoir (cyx@users.sourceforge.net) * Copyright (C) 2005-2019 Gregory Montoir (cyx@users.sourceforge.net)
*/ */
#include "file.h" #include "file.h"
@ -226,10 +226,8 @@ SeqPlayer::~SeqPlayer() {
void SeqPlayer::play(File *f) { void SeqPlayer::play(File *f) {
if (_demux.open(f)) { if (_demux.open(f)) {
Color pal[256]; uint8_t palette[256 * 3];
for (int i = 0; i < 256; ++i) { _stub->getPalette(palette, 256);
_stub->getPaletteEntry(i, &pal[i]);
}
_mix->setPremixHook(mixCallback, this); _mix->setPremixHook(mixCallback, this);
memset(_buf, 0, 256 * 224); memset(_buf, 0, 256 * 224);
bool clearScreen = true; bool clearScreen = true;
@ -315,9 +313,8 @@ void SeqPlayer::play(File *f) {
_stub->sleep(diff); _stub->sleep(diff);
} }
} }
for (int i = 0; i < 256; ++i) { // restore level palette
_stub->setPaletteEntry(i, &pal[i]); _stub->setPalette(palette, 256);
}
_mix->setPremixHook(0, 0); _mix->setPremixHook(0, 0);
_demux.close(); _demux.close();
// flush sound queue // flush sound queue

View File

@ -1,7 +1,7 @@
/* /*
* REminiscence - Flashback interpreter * REminiscence - Flashback interpreter
* Copyright (C) 2005-2018 Gregory Montoir (cyx@users.sourceforge.net) * Copyright (C) 2005-2019 Gregory Montoir (cyx@users.sourceforge.net)
*/ */
#ifndef SEQ_PLAYER_H__ #ifndef SEQ_PLAYER_H__

View File

@ -1,13 +1,35 @@
/* /*
* REminiscence - Flashback interpreter * REminiscence - Flashback interpreter
* Copyright (C) 2005-2018 Gregory Montoir (cyx@users.sourceforge.net) * Copyright (C) 2005-2019 Gregory Montoir (cyx@users.sourceforge.net)
*/ */
#include "mixer.h" #include "mixer.h"
#include "sfx_player.h" #include "sfx_player.h"
#include "util.h" #include "util.h"
// volume instruments are either equal to 64 or 32 (this corresponds to aud0vol)
// use one third of the volume for master (for comparison, modplug uses a master volume of 128, max 512)
static const int kMasterVolume = 64 * 3;
// 12 dB/oct Butterworth low-pass filter at 3.3 kHz
static const bool kLowPassFilter = true;
#define NZEROS 2
#define NPOLES 2
static float bw_xf[NZEROS+1], bw_yf[NPOLES+1];
static const float GAIN = 7.655158005e+00;
static void butterworth(int16_t *p, int len) {
for (int i = 0; i < len; ++i) {
bw_xf[0] = bw_xf[1]; bw_xf[1] = bw_xf[2];
bw_xf[2] = p[i] / GAIN;
bw_yf[0] = bw_yf[1]; bw_yf[1] = bw_yf[2];
bw_yf[2] = (bw_xf[0] + bw_xf[2]) + 2 * bw_xf[1] + (-0.2729352339 * bw_yf[0]) + (0.7504117278 * bw_yf[1]);
p[i] = (int16_t)CLIP(bw_yf[2], -32768.f, 32767.f);
}
}
SfxPlayer::SfxPlayer(Mixer *mixer) SfxPlayer::SfxPlayer(Mixer *mixer)
: _mod(0), _playing(false), _mix(mixer) { : _mod(0), _playing(false), _mix(mixer) {
} }
@ -15,20 +37,23 @@ SfxPlayer::SfxPlayer(Mixer *mixer)
void SfxPlayer::play(uint8_t num) { void SfxPlayer::play(uint8_t num) {
debug(DBG_SFX, "SfxPlayer::play(%d)", num); debug(DBG_SFX, "SfxPlayer::play(%d)", num);
if (!_playing) { if (!_playing) {
if (num >= 68 && num <= 75) { assert(num >= 68 && num <= 75);
static const Module *modTable[] = { static const Module *modTable[] = {
&_module68, &_module68, &_module70, &_module70, &_module68, &_module68, &_module70, &_module70,
&_module72, &_module73, &_module74, &_module75 &_module72, &_module73, &_module74, &_module75
}; };
_mod = modTable[num - 68]; _mod = modTable[num - 68];
_curOrder = 0; _curOrder = 0;
_numOrders = READ_BE_UINT16(_mod->moduleData); _numOrders = READ_BE_UINT16(_mod->moduleData);
_orderDelay = 0; _orderDelay = 0;
_modData = _mod->moduleData + 0x22; _modData = _mod->moduleData + 0x22;
memset(_samples, 0, sizeof(_samples)); memset(_samples, 0, sizeof(_samples));
_samplesLeft = 0; _samplesLeft = 0;
_mix->setPremixHook(mixCallback, this); _mix->setPremixHook(mixCallback, this);
_playing = true; _playing = true;
if (kLowPassFilter) {
memset(bw_xf, 0, sizeof(bw_xf));
memset(bw_yf, 0, sizeof(bw_yf));
} }
} }
} }
@ -126,8 +151,8 @@ void SfxPlayer::mixSamples(int16_t *buf, int samplesLen) {
curLen = 0; curLen = 0;
} }
while (count--) { while (count--) {
const int out = si->getPCM(pos >> FRAC_BITS); const int out = si->getPCM(pos >> FRAC_BITS) * si->vol / kMasterVolume;
*mixbuf = ADDC_S16(*mixbuf, (out * si->vol / 64) << 8); *mixbuf = ADDC_S16(*mixbuf, S8_to_S16(out));
++mixbuf; ++mixbuf;
pos += deltaPos; pos += deltaPos;
} }
@ -153,6 +178,9 @@ bool SfxPlayer::mix(int16_t *buf, int len) {
_samplesLeft -= count; _samplesLeft -= count;
len -= count; len -= count;
mixSamples(buf, count); mixSamples(buf, count);
if (kLowPassFilter) {
butterworth(buf, count);
}
buf += count; buf += count;
} }
} }

View File

@ -1,7 +1,7 @@
/* /*
* REminiscence - Flashback interpreter * REminiscence - Flashback interpreter
* Copyright (C) 2005-2018 Gregory Montoir (cyx@users.sourceforge.net) * Copyright (C) 2005-2019 Gregory Montoir (cyx@users.sourceforge.net)
*/ */
#ifndef SFX_PLAYER_H__ #ifndef SFX_PLAYER_H__

View File

@ -1,7 +1,7 @@
/* /*
* REminiscence - Flashback interpreter * REminiscence - Flashback interpreter
* Copyright (C) 2005-2018 Gregory Montoir (cyx@users.sourceforge.net) * Copyright (C) 2005-2019 Gregory Montoir (cyx@users.sourceforge.net)
*/ */
#include "game.h" #include "game.h"
@ -30,7 +30,7 @@ const Cutscene::OpcodeStub Cutscene::_opcodeTable[] = {
&Cutscene::op_handleKeys &Cutscene::op_handleKeys
}; };
const char *Cutscene::_namesTable[] = { const char *Cutscene::_namesTableDOS[] = {
"DEBUT", "DEBUT",
"OBJET", "OBJET",
"CARTE", "CARTE",
@ -68,7 +68,7 @@ const char *Cutscene::_namesTable[] = {
"LOGOSSSI", "LOGOSSSI",
}; };
const uint16_t Cutscene::_offsetsTable[] = { const uint16_t Cutscene::_offsetsTableDOS[] = {
0x0000, 0x0000, 0x0001, 0x0003, 0x0001, 0x0004, 0xFFFF, 0x0000, 0x0001, 0x0002, 0x0000, 0x0000, 0x0001, 0x0003, 0x0001, 0x0004, 0xFFFF, 0x0000, 0x0001, 0x0002,
0x0003, 0x0000, 0x0004, 0x0000, 0xFFFF, 0x0100, 0xFFFF, 0x0000, 0x0006, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0xFFFF, 0x0100, 0xFFFF, 0x0000, 0x0006, 0x0000,
0x0001, 0x0001, 0xFFFF, 0x0000, 0xFFFF, 0x0200, 0x8007, 0x0000, 0x0003, 0x0001, 0x0001, 0x0001, 0xFFFF, 0x0000, 0xFFFF, 0x0200, 0x8007, 0x0000, 0x0003, 0x0001,
@ -87,6 +87,19 @@ const uint16_t Cutscene::_offsetsTable[] = {
0xFFFF, 0x0000 0xFFFF, 0x0000
}; };
const uint16_t Cutscene::_offsetsTableAmiga[] = {
0x0000, 0, 0x0001, 3, 0x0001, 4, 0x0002, 0, 0x0001, 2, 0x0003, 0, 0x0004, 0, 0xFFFF, 0,
0xFFFF, 0, 0x0006, 0, 0x0001, 1, 0xFFFF, 0, 0xFFFF, 0, 0x0007, 0, 0x0003, 1, 0x0001, 11,
0x0001, 5, 0x0009, 0, 0x0001, 6, 0x000A, 0, 0x000B, 0, 0x0001, 10, 0x000C, 1, 0xFFFF, 2,
0xFFFF, 0, 0x000D, 4, 0x000D, 0, 0x000D, 1, 0x000D, 2, 0x000D, 3, 0xFFFF, 0, 0xFFFF, 1,
0x0001, 12, 0x0001, 13, 0x0001, 14, 0x0001, 15, 0x0001, 16, 0x000F, 0, 0x000F, 1, 0x000F, 1,
0x000F, 3, 0x000F, 2, 0x000F, 4, 0x0001, 8, 0x0001, 7, 0x000F, 5, 0xFFFF, 0, 0x0004, 1,
0x0011, 0, 0x0001, 9, 0x0012, 0, 0xFFFF, 0, 0x0014, 0, 0x0015, 0, 0x0016, 0, 0x0016, 1,
0xFFFF, 18, 0x0017, 0, 0x0001, 17, 0x0018, 0, 0x0001, 19, 0x0019, 0, 0x001A, 0, 0x0019, 1,
0x001B, 0, 0x001C, 0, 0x000F, 6, 0x000F, 6, 0x000F, 7, 0x000F, 8, 0x000F, 9, 0x000F, 10,
0x001D, 0, 0x001B, 1
};
const uint8_t Cutscene::_amigaDemoOffsetsTable[] = { const uint8_t Cutscene::_amigaDemoOffsetsTable[] = {
1, 32, 0, /* HOLOCUBE */ 1, 32, 0, /* HOLOCUBE */
6, 33, 0, /* CHUTE2 */ 6, 33, 0, /* CHUTE2 */
@ -2928,7 +2941,7 @@ const char *Resource::_splNames[] = {
"touche.spl", "touche.spl",
"coup", "coup",
"chenille.spl", "chenille.spl",
"robot.spl", "robot",
"tombe.spl", "tombe.spl",
"porte_ferme.spl", "porte_ferme.spl",
"canon_down", "canon_down",
@ -2972,6 +2985,512 @@ const char *Resource::_splNames[] = {
0 0
}; };
const uint8_t Resource::_gameSavedSoundData[] = {
0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0xff, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x06, 0x06, 0xfd, 0x00, 0xff, 0xfc, 0x00, 0xff, 0xfd, 0x00,
0xfe, 0xfe, 0xff, 0xff, 0xff, 0xff, 0x00, 0xff, 0x00, 0x00, 0xff, 0x00, 0xff, 0x00, 0x00, 0xff,
0x00, 0xff, 0xfd, 0xfe, 0xfa, 0xfd, 0xfc, 0xfd, 0x07, 0x09, 0x07, 0x00, 0x04, 0x01, 0x02, 0x05,
0x01, 0x04, 0xfe, 0x02, 0x04, 0x06, 0x03, 0x02, 0x02, 0x00, 0xff, 0xff, 0xff, 0xfd, 0xff, 0xfb,
0xfe, 0xfc, 0xfd, 0xfe, 0xfd, 0xfd, 0xff, 0xff, 0xfe, 0x01, 0xff, 0x00, 0x00, 0x00, 0x01, 0x00,
0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0xff, 0x00, 0xff, 0x01, 0x02,
0x00, 0x01, 0x00, 0x02, 0x00, 0x00, 0x04, 0x00, 0x01, 0x03, 0x00, 0x02, 0x00, 0x00, 0x01, 0xff,
0x00, 0x00, 0xff, 0xfe, 0xff, 0xfd, 0xfe, 0xff, 0xfb, 0xfc, 0xf9, 0xf8, 0xf7, 0xf5, 0xf5, 0xf5,
0xf2, 0xf3, 0xf3, 0xf3, 0xf6, 0xf5, 0xf7, 0xfd, 0xfa, 0xfc, 0x05, 0x00, 0x02, 0x07, 0x06, 0x04,
0x06, 0x08, 0x02, 0x04, 0x00, 0x00, 0xfe, 0xf8, 0xfd, 0xfb, 0xf4, 0xfa, 0x01, 0xf8, 0xfe, 0x0a,
0x02, 0x04, 0x0e, 0x0d, 0x07, 0x0e, 0x0e, 0x08, 0x06, 0x07, 0x02, 0xfe, 0xfc, 0xf9, 0xf3, 0xef,
0xf2, 0xee, 0xeb, 0xed, 0xec, 0xe7, 0xe6, 0xe9, 0xe3, 0xde, 0xdf, 0xdf, 0xd9, 0xea, 0xf1, 0xda,
0x00, 0x02, 0xf0, 0x0c, 0x0c, 0x0c, 0x02, 0x15, 0x0c, 0x01, 0x08, 0xfb, 0x01, 0xee, 0xf1, 0xf3,
0xe9, 0xe7, 0xe8, 0xf3, 0xe7, 0xf1, 0xfc, 0xfa, 0xff, 0x02, 0x0d, 0x07, 0x0f, 0x18, 0x0e, 0x13,
0x14, 0x0e, 0x09, 0x0b, 0x02, 0x01, 0x00, 0xfd, 0x00, 0xff, 0x00, 0x01, 0x03, 0xff, 0x01, 0x00,
0xfc, 0xf9, 0xf8, 0xf2, 0xec, 0xee, 0xe0, 0xd8, 0xda, 0xd6, 0xca, 0xd2, 0xd6, 0xed, 0xf6, 0xe7,
0x1a, 0x1d, 0x0f, 0x23, 0x31, 0x26, 0x10, 0x22, 0x0f, 0xff, 0xf8, 0xed, 0xee, 0xdd, 0xdf, 0xe4,
0xed, 0xed, 0xf4, 0x0d, 0x0b, 0x13, 0x1e, 0x24, 0x1f, 0x1c, 0x24, 0x17, 0x10, 0x0c, 0x08, 0xfe,
0xfc, 0xfb, 0xfb, 0xfe, 0x00, 0x09, 0x0d, 0x13, 0x17, 0x1b, 0x18, 0x15, 0x0f, 0x07, 0x00, 0xf7,
0xf0, 0xe8, 0xe4, 0xdf, 0xd6, 0xd6, 0xd4, 0xd2, 0xdd, 0xd3, 0xe1, 0xf5, 0x19, 0x09, 0x03, 0x4b,
0x21, 0x18, 0x26, 0x20, 0x0b, 0xf0, 0xff, 0xe2, 0xe9, 0xdc, 0xd8, 0xf5, 0xf0, 0xf8, 0x05, 0x1f,
0x18, 0x19, 0x2b, 0x1c, 0x1a, 0x0e, 0x05, 0xfb, 0xf3, 0xfa, 0xf1, 0xf6, 0x04, 0x08, 0x09, 0x1b,
0x21, 0x1c, 0x21, 0x21, 0x1b, 0x15, 0x0f, 0x07, 0x04, 0xfd, 0xf6, 0xf5, 0xf2, 0xed, 0xef, 0xf1,
0xf5, 0xf4, 0xf1, 0xf5, 0xe9, 0xe9, 0xee, 0xd7, 0xcb, 0xdf, 0xf6, 0x09, 0xef, 0x0b, 0x3b, 0x1a,
0x18, 0x1e, 0x24, 0x0b, 0xf6, 0xf9, 0xea, 0xf0, 0xdd, 0xe0, 0xf9, 0xfc, 0x00, 0x07, 0x21, 0x1d,
0x19, 0x1d, 0x14, 0x11, 0x00, 0xf7, 0xf2, 0xfa, 0xf9, 0xfe, 0x0c, 0x11, 0x19, 0x1f, 0x24, 0x1d,
0x1d, 0x17, 0x0e, 0x08, 0x03, 0x00, 0xfd, 0xfc, 0xfa, 0xfa, 0xf8, 0xf7, 0xf4, 0xf2, 0xf5, 0xf5,
0xf0, 0xee, 0xe5, 0xdc, 0xe4, 0xd3, 0xc3, 0xcf, 0xfb, 0x26, 0xef, 0x16, 0x46, 0x23, 0x20, 0x0a,
0x14, 0xfc, 0xec, 0xdf, 0xcb, 0xf3, 0xe7, 0xe1, 0x00, 0x15, 0x1d, 0x16, 0x28, 0x20, 0x16, 0x12,
0xf4, 0xf4, 0xeb, 0xe9, 0xeb, 0xf8, 0x03, 0x0f, 0x25, 0x22, 0x2a, 0x2f, 0x24, 0x11, 0x10, 0x05,
0xf8, 0xf9, 0xf9, 0xfa, 0xfe, 0x01, 0x00, 0x03, 0x00, 0xf7, 0xf5, 0xf8, 0xed, 0xe5, 0xe7, 0xdf,
0xd0, 0xd7, 0xcc, 0xc8, 0xc7, 0xee, 0x55, 0x0b, 0xf4, 0x4a, 0x3a, 0x13, 0xec, 0x00, 0xf3, 0xdc,
0xde, 0xbd, 0xec, 0x0f, 0xf3, 0x01, 0x24, 0x3a, 0x18, 0x15, 0x19, 0xfd, 0x00, 0xe1, 0xd7, 0xe1,
0xf7, 0xf5, 0xfd, 0x22, 0x2b, 0x30, 0x29, 0x2a, 0x21, 0x17, 0xfe, 0xf5, 0xfd, 0xf9, 0xf7, 0xff,
0x0d, 0x0a, 0x0b, 0x01, 0xfd, 0xf7, 0xea, 0xe8, 0xe8, 0xe3, 0xdd, 0xdf, 0xd2, 0xcf, 0xd4, 0xc9,
0xba, 0x1b, 0x4f, 0xe6, 0x15, 0x52, 0x26, 0x02, 0xf3, 0xfc, 0xe1, 0xe3, 0xd6, 0xbe, 0x00, 0x10,
0xf2, 0x0f, 0x2f, 0x31, 0x10, 0x17, 0x0a, 0xf3, 0xfb, 0xd9, 0xd6, 0xea, 0xfc, 0xfb, 0x0c, 0x29,
0x2c, 0x31, 0x29, 0x23, 0x1a, 0x0e, 0xf8, 0xf5, 0xfc, 0xfa, 0xfd, 0x07, 0x11, 0x0e, 0x0f, 0x05,
0xfe, 0xf4, 0xe6, 0xd9, 0xda, 0xdd, 0xd2, 0xd3, 0xd7, 0xcc, 0xca, 0xe2, 0xd9, 0x2c, 0x45, 0xea,
0x25, 0x43, 0x19, 0xec, 0xea, 0xfd, 0xd5, 0xdd, 0xdd, 0xd5, 0x0d, 0x11, 0x00, 0x19, 0x31, 0x2b,
0x00, 0x10, 0x00, 0xec, 0xed, 0xda, 0xe3, 0xf0, 0x08, 0x08, 0x18, 0x2c, 0x2f, 0x29, 0x21, 0x1a,
0x0c, 0x05, 0xf9, 0xf8, 0xfc, 0x01, 0x04, 0x0b, 0x10, 0x0d, 0x08, 0x00, 0xf8, 0xea, 0xe2, 0xdb,
0xdc, 0xd4, 0xd8, 0xe1, 0xcf, 0xcd, 0xe2, 0xca, 0xdf, 0x5f, 0x11, 0xdc, 0x4c, 0x3c, 0x00, 0xe6,
0x03, 0xf3, 0xd2, 0xea, 0xcc, 0xe4, 0x1c, 0xf9, 0xfd, 0x24, 0x37, 0x11, 0x04, 0x1d, 0xf6, 0xf6,
0xea, 0xdc, 0xeb, 0xf8, 0xfe, 0x02, 0x23, 0x29, 0x27, 0x28, 0x29, 0x18, 0x0e, 0x04, 0xf8, 0xfd,
0xfd, 0xfb, 0x01, 0x10, 0x0b, 0x08, 0x06, 0x01, 0xf8, 0xea, 0xdf, 0xe1, 0xe0, 0xd1, 0xd1, 0xd7,
0xd6, 0xc4, 0xcc, 0xc7, 0x16, 0x46, 0xe1, 0x1b, 0x5a, 0x26, 0xfc, 0xf8, 0x0d, 0xe5, 0xd4, 0xd5,
0xcb, 0xfe, 0xfc, 0xe7, 0x13, 0x2f, 0x2a, 0x0b, 0x22, 0x1a, 0xff, 0xf9, 0xdf, 0xe9, 0xee, 0xeb,
0xef, 0x0b, 0x1e, 0x1e, 0x26, 0x2d, 0x2e, 0x1f, 0x10, 0x07, 0x04, 0xfc, 0xf3, 0xf9, 0x03, 0x05,
0x01, 0x04, 0x06, 0x01, 0xf2, 0xe6, 0xe9, 0xe9, 0xda, 0xd4, 0xdf, 0xd4, 0xcf, 0xd4, 0xd5, 0xb4,
0x0a, 0x54, 0xd3, 0x10, 0x67, 0x23, 0xfe, 0x05, 0x16, 0xe4, 0xe0, 0xda, 0xc4, 0x00, 0xf1, 0xd8,
0x0e, 0x2e, 0x1b, 0x07, 0x33, 0x1e, 0x06, 0x06, 0xec, 0xf3, 0xf2, 0xe6, 0xe8, 0x09, 0x0d, 0x0f,
0x25, 0x2f, 0x28, 0x24, 0x1e, 0x0e, 0x0b, 0x00, 0xf4, 0xfa, 0x00, 0xfb, 0xfc, 0x01, 0xff, 0xfc,
0xf0, 0xf1, 0xf3, 0xe7, 0xdc, 0xdd, 0xe0, 0xd5, 0xc8, 0xc8, 0xc8, 0xc0, 0x2e, 0x1d, 0xd1, 0x4d,
0x54, 0x0c, 0x04, 0x25, 0x0b, 0xda, 0xe8, 0xd1, 0xdc, 0xf4, 0xd0, 0xea, 0x1a, 0x19, 0x04, 0x24,
0x38, 0x0f, 0x16, 0x0b, 0xff, 0xfc, 0xee, 0xe6, 0xf3, 0x00, 0xf8, 0x11, 0x24, 0x1f, 0x23, 0x2b,
0x1e, 0x16, 0x11, 0x02, 0x00, 0x00, 0xfa, 0xf8, 0xfb, 0xf4, 0xf7, 0xf1, 0xea, 0xf1, 0xe9, 0xdc,
0xe8, 0xec, 0xd5, 0xd7, 0xde, 0xca, 0xae, 0x18, 0x1a, 0xba, 0x2c, 0x4d, 0x02, 0x07, 0x32, 0x17,
0xf0, 0x00, 0xe6, 0xea, 0xf9, 0xcf, 0xe5, 0x0e, 0xfe, 0xef, 0x1f, 0x28, 0x03, 0x1d, 0x1b, 0x0a,
0x0b, 0x02, 0xf9, 0x01, 0xff, 0xf5, 0x0c, 0x10, 0x07, 0x14, 0x1e, 0x13, 0x15, 0x16, 0x11, 0x10,
0x08, 0x05, 0x06, 0xfc, 0xf2, 0xf7, 0xee, 0xe3, 0xe5, 0xe6, 0xdc, 0xdd, 0xdf, 0xe0, 0xe8, 0xd4,
0xc6, 0xcd, 0x23, 0xf9, 0xb9, 0x41, 0x36, 0xee, 0x0a, 0x3a, 0x15, 0xea, 0x06, 0xfb, 0x00, 0xe8,
0xd9, 0x07, 0x00, 0xe4, 0xfb, 0x28, 0x06, 0xfa, 0x23, 0x18, 0x08, 0x0a, 0x0e, 0x0c, 0x07, 0xfa,
0x05, 0x10, 0xfe, 0x02, 0x14, 0x0e, 0x08, 0x16, 0x16, 0x10, 0x0f, 0x10, 0x10, 0x07, 0xfc, 0xfe,
0xfa, 0xe9, 0xe8, 0xe9, 0xdd, 0xda, 0xe2, 0xdf, 0xd9, 0xde, 0xd1, 0xcf, 0xcb, 0x00, 0x06, 0xce,
0x25, 0x2d, 0xff, 0x12, 0x2c, 0x15, 0xfc, 0x10, 0xfc, 0xff, 0xf4, 0xe6, 0xff, 0xfb, 0xeb, 0xfc,
0x15, 0x00, 0x00, 0x1c, 0x10, 0x09, 0x14, 0x0f, 0x09, 0x0a, 0x03, 0x04, 0x04, 0x00, 0x05, 0x08,
0x06, 0x0c, 0x11, 0x0f, 0x13, 0x15, 0x12, 0x11, 0x0d, 0x07, 0x03, 0xfc, 0xf4, 0xf0, 0xe8, 0xe0,
0xde, 0xdb, 0xd6, 0xd4, 0xd4, 0xce, 0xd4, 0xc3, 0xf4, 0x0a, 0xca, 0x1e, 0x2d, 0xf3, 0x17, 0x32,
0x0f, 0x00, 0x20, 0x04, 0x00, 0x00, 0xf2, 0x03, 0xf6, 0xec, 0x00, 0x08, 0xf1, 0x03, 0x15, 0xfe,
0x09, 0x18, 0x0a, 0x07, 0x13, 0x0b, 0x05, 0x06, 0x08, 0x07, 0x02, 0x07, 0x0d, 0x08, 0x0a, 0x15,
0x11, 0x0f, 0x15, 0x11, 0x0a, 0x0a, 0x03, 0xfd, 0xf7, 0xf0, 0xea, 0xe4, 0xda, 0xdb, 0xd7, 0xcb,
0xce, 0xd2, 0xc2, 0xda, 0x06, 0xc9, 0xff, 0x2b, 0xea, 0x11, 0x2c, 0x11, 0x0a, 0x26, 0x0e, 0x0b,
0x0f, 0xfc, 0x0d, 0xfc, 0xf1, 0x05, 0xfe, 0xeb, 0x02, 0x06, 0xef, 0x03, 0x0d, 0x01, 0x04, 0x0f,
0x0f, 0x07, 0x0d, 0x11, 0x0a, 0x06, 0x0e, 0x0b, 0x06, 0x0e, 0x10, 0x0b, 0x0f, 0x13, 0x0c, 0x0b,
0x0c, 0x06, 0xff, 0xfb, 0xf8, 0xef, 0xe7, 0xe2, 0xdf, 0xd6, 0xd5, 0xcf, 0xcd, 0xc7, 0xde, 0xee,
0xc3, 0x04, 0x06, 0xe2, 0x15, 0x14, 0x08, 0x13, 0x21, 0x11, 0x17, 0x12, 0x11, 0x14, 0xfe, 0x0c,
0x0b, 0xf7, 0xff, 0x06, 0xf3, 0xf9, 0x02, 0xfa, 0xfe, 0x02, 0x02, 0x05, 0x04, 0x09, 0x0a, 0x06,
0x0d, 0x0c, 0x08, 0x0f, 0x0f, 0x0b, 0x12, 0x12, 0x0e, 0x10, 0x0d, 0x0a, 0x09, 0x04, 0xff, 0xfb,
0xf8, 0xef, 0xe9, 0xe4, 0xe4, 0xd8, 0xd9, 0xd5, 0xd1, 0xcd, 0xe7, 0xde, 0xce, 0x0e, 0xe7, 0xf0,
0x15, 0x00, 0x0b, 0x15, 0x15, 0x14, 0x19, 0x0f, 0x1d, 0x0c, 0x08, 0x18, 0x02, 0x02, 0x0c, 0xfd,
0xfb, 0x03, 0xfa, 0xfd, 0x00, 0xfe, 0x00, 0x00, 0x03, 0x06, 0x02, 0x09, 0x09, 0x06, 0x0e, 0x0c,
0x0a, 0x11, 0x10, 0x0e, 0x12, 0x0d, 0x0b, 0x0c, 0x08, 0x02, 0xff, 0xfe, 0xf8, 0xee, 0xef, 0xe9,
0xe3, 0xe1, 0xde, 0xd9, 0xd8, 0xd3, 0xe1, 0xe1, 0xd1, 0x01, 0xe7, 0xe8, 0x0e, 0xf9, 0x06, 0x0e,
0x10, 0x0f, 0x15, 0x11, 0x1a, 0x0e, 0x0d, 0x1a, 0x05, 0x0a, 0x0e, 0x00, 0x00, 0x05, 0xfa, 0xff,
0x00, 0xfb, 0xfe, 0xff, 0x00, 0x00, 0x02, 0x04, 0x04, 0x04, 0x0a, 0x08, 0x09, 0x0f, 0x0c, 0x0c,
0x10, 0x0b, 0x09, 0x0c, 0x07, 0x02, 0x00, 0xff, 0xf9, 0xf3, 0xf1, 0xec, 0xe9, 0xe5, 0xe1, 0xdf,
0xdc, 0xd9, 0xdd, 0xe4, 0xdb, 0xee, 0xf3, 0xe8, 0xff, 0xff, 0x00, 0x08, 0x0e, 0x0b, 0x0e, 0x15,
0x0f, 0x11, 0x0f, 0x12, 0x0d, 0x0a, 0x0c, 0x07, 0x03, 0x03, 0x03, 0xff, 0x00, 0x00, 0xfe, 0xff,
0x01, 0x00, 0x00, 0x03, 0x02, 0x04, 0x07, 0x08, 0x09, 0x0b, 0x0c, 0x0d, 0x0d, 0x0c, 0x0c, 0x0b,
0x09, 0x06, 0x03, 0x02, 0xfc, 0xf8, 0xf8, 0xf4, 0xee, 0xeb, 0xeb, 0xe7, 0xe4, 0xe2, 0xe1, 0xe4,
0xe8, 0xe8, 0xee, 0xf5, 0xf5, 0xf9, 0x00, 0x04, 0x06, 0x08, 0x0c, 0x0e, 0x0e, 0x0e, 0x10, 0x0f,
0x0e, 0x0d, 0x0c, 0x0a, 0x08, 0x06, 0x05, 0x04, 0x03, 0x01, 0x00, 0x01, 0x02, 0x01, 0x02, 0x04,
0x05, 0x06, 0x07, 0x09, 0x0a, 0x0a, 0x0c, 0x0c, 0x0a, 0x0b, 0x0c, 0x09, 0x07, 0x07, 0x07, 0x01,
0x00, 0x00, 0xfc, 0xf9, 0xf6, 0xf4, 0xf2, 0xee, 0xeb, 0xea, 0xe9, 0xe6, 0xe6, 0xeb, 0xed, 0xec,
0xef, 0xf2, 0xf5, 0xf9, 0xfe, 0x00, 0x04, 0x08, 0x09, 0x0b, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0a,
0x08, 0x07, 0x05, 0x04, 0x03, 0x02, 0x01, 0x01, 0x01, 0x01, 0x02, 0x03, 0x05, 0x07, 0x09, 0x0a,
0x0d, 0x10, 0x10, 0x10, 0x12, 0x12, 0x0f, 0x0e, 0x0e, 0x0d, 0x07, 0x06, 0x06, 0x01, 0xfe, 0xfc,
0xfa, 0xf6, 0xf3, 0xf0, 0xef, 0xec, 0xe9, 0xea, 0xec, 0xef, 0xf0, 0xf3, 0xf6, 0xf8, 0xfc, 0x00,
0x03, 0x04, 0x07, 0x09, 0x09, 0x09, 0x09, 0x0a, 0x09, 0x08, 0x07, 0x05, 0x04, 0x03, 0x02, 0x01,
0x00, 0xfe, 0xfe, 0xfe, 0xfd, 0xfd, 0xfc, 0xfd, 0xfe, 0xfd, 0xfe, 0xff, 0x00, 0x00, 0xff, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0xfd, 0xfc, 0xfc, 0xf8, 0xf6, 0xf4, 0xf1, 0xee, 0xec, 0xea, 0xe8,
0xe5, 0xe2, 0xe5, 0xe7, 0xe8, 0xea, 0xee, 0xf1, 0xf4, 0xfa, 0xfd, 0x00, 0x03, 0x04, 0x06, 0x07,
0x08, 0x09, 0x0a, 0x09, 0x08, 0x08, 0x07, 0x06, 0x05, 0x05, 0x04, 0x02, 0x01, 0x01, 0x01, 0x00,
0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x02, 0x02, 0x02, 0x01, 0x02, 0x02, 0x00, 0x01, 0x00, 0x00,
0xfd, 0xfc, 0xfb, 0xf7, 0xf5, 0xf2, 0xf0, 0xec, 0xe9, 0xe8, 0xe5, 0xe2, 0xe0, 0xe4, 0xe4, 0xe5,
0xe9, 0xec, 0xf0, 0xf4, 0xfa, 0xfd, 0x00, 0x03, 0x04, 0x05, 0x08, 0x08, 0x08, 0x0a, 0x09, 0x08,
0x08, 0x07, 0x07, 0x06, 0x05, 0x05, 0x04, 0x01, 0x01, 0x02, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01,
0x02, 0x02, 0x02, 0x03, 0x02, 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, 0xfd, 0xfc, 0xfc, 0xf8, 0xf5,
0xf3, 0xf1, 0xed, 0xeb, 0xe9, 0xe7, 0xe4, 0xe3, 0xe7, 0xe7, 0xe7, 0xec, 0xef, 0xf2, 0xf7, 0xfb,
0xfe, 0x01, 0x04, 0x06, 0x07, 0x09, 0x0a, 0x0a, 0x0b, 0x0b, 0x0a, 0x09, 0x08, 0x08, 0x07, 0x06,
0x06, 0x05, 0x03, 0x03, 0x03, 0x02, 0x01, 0x01, 0x01, 0x01, 0x02, 0x03, 0x03, 0x03, 0x03, 0x03,
0x03, 0x03, 0x02, 0x02, 0x02, 0x00, 0xff, 0xff, 0xfd, 0xfa, 0xf8, 0xf6, 0xf4, 0xf1, 0xef, 0xed,
0xeb, 0xe9, 0xe7, 0xe8, 0xe8, 0xe9, 0xec, 0xee, 0xf2, 0xf5, 0xf9, 0xfd, 0x00, 0x02, 0x06, 0x08,
0x09, 0x0a, 0x0c, 0x0c, 0x0c, 0x0b, 0x0a, 0x09, 0x09, 0x08, 0x07, 0x06, 0x06, 0x04, 0x03, 0x03,
0x03, 0x02, 0x02, 0x02, 0x02, 0x03, 0x03, 0x03, 0x04, 0x04, 0x05, 0x05, 0x05, 0x05, 0x04, 0x03,
0x02, 0x02, 0x00, 0xff, 0xfe, 0xfb, 0xfa, 0xf7, 0xf5, 0xf3, 0xf1, 0xef, 0xee, 0xec, 0xea, 0xea,
0xe9, 0xeb, 0xed, 0xee, 0xf0, 0xf3, 0xf8, 0xfb, 0xfe, 0x00, 0x04, 0x06, 0x07, 0x09, 0x0a, 0x0b,
0x0b, 0x0a, 0x0a, 0x09, 0x08, 0x07, 0x06, 0x06, 0x05, 0x03, 0x02, 0x02, 0x01, 0x01, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0xfe, 0xfd,
0xfc, 0xfa, 0xf9, 0xf7, 0xf6, 0xf4, 0xf2, 0xf1, 0xf0, 0xef, 0xee, 0xed, 0xec, 0xec, 0xee, 0xee,
0xf0, 0xf2, 0xf4, 0xf6, 0xfa, 0xfe, 0x00, 0x03, 0x06, 0x08, 0x0a, 0x0b, 0x0c, 0x0c, 0x0c, 0x0c,
0x0c, 0x0a, 0x0a, 0x0a, 0x0a, 0x08, 0x08, 0x07, 0x06, 0x06, 0x06, 0x06, 0x05, 0x05, 0x05, 0x05,
0x05, 0x05, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x05, 0x05, 0x04, 0x03, 0x02, 0x00, 0x00, 0xfe,
0xfd, 0xfb, 0xf9, 0xf8, 0xf7, 0xf5, 0xf4, 0xf3, 0xf1, 0xf0, 0xef, 0xee, 0xee, 0xef, 0xf0, 0xf2,
0xf4, 0xf6, 0xfa, 0xfe, 0x00, 0x02, 0x05, 0x06, 0x08, 0x0a, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0a,
0x08, 0x09, 0x09, 0x08, 0x07, 0x07, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x05, 0x06, 0x06, 0x06,
0x07, 0x06, 0x07, 0x07, 0x07, 0x07, 0x06, 0x05, 0x05, 0x04, 0x03, 0x02, 0x01, 0x00, 0xff, 0xfd,
0xfc, 0xfb, 0xfa, 0xf9, 0xf8, 0xf7, 0xf6, 0xf6, 0xf5, 0xf5, 0xf5, 0xf4, 0xf5, 0xf6, 0xf6, 0xf7,
0xf9, 0xfb, 0xfc, 0xfd, 0xff, 0x01, 0x02, 0x04, 0x05, 0x07, 0x08, 0x08, 0x09, 0x09, 0x09, 0x09,
0x08, 0x07, 0x07, 0x08, 0x08, 0x07, 0x07, 0x07, 0x07, 0x07, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06,
0x06, 0x05, 0x05, 0x05, 0x05, 0x05, 0x04, 0x04, 0x04, 0x03, 0x03, 0x03, 0x02, 0x02, 0x01, 0x00,
0x00, 0xff, 0xfd, 0xfc, 0xfb, 0xfb, 0xfa, 0xf9, 0xf8, 0xf8, 0xf7, 0xf6, 0xf6, 0xf5, 0xf4, 0xf4,
0xf4, 0xf3, 0xf2, 0xf2, 0xf3, 0xf4, 0xf3, 0xf4, 0xf5, 0xf6, 0xf8, 0xfa, 0xfa, 0xfc, 0xfd, 0xfe,
0xff, 0x00, 0x01, 0x03, 0x05, 0x07, 0x08, 0x0b, 0x0c, 0x0d, 0x0f, 0x0f, 0x10, 0x10, 0x10, 0x10,
0x0e, 0x0d, 0x0c, 0x0a, 0x09, 0x08, 0x07, 0x07, 0x05, 0x05, 0x05, 0x03, 0x01, 0x00, 0xff, 0xfc,
0xfc, 0xf9, 0xfa, 0xf9, 0xf9, 0xfb, 0xf8, 0xfb, 0xf9, 0xfb, 0xf9, 0xfc, 0xf9, 0xfc, 0xfc, 0xf9,
0xfc, 0xf8, 0xfe, 0xf6, 0xf9, 0xf6, 0xf8, 0xf7, 0xf5, 0xf7, 0xf5, 0xf8, 0xf5, 0xf8, 0xf5, 0xf9,
0xf6, 0xf8, 0xf8, 0xf6, 0xfa, 0xf7, 0xfa, 0xf8, 0xf9, 0xfa, 0xf8, 0xf8, 0xf8, 0xf9, 0xf8, 0xf7,
0xf8, 0xf7, 0xf8, 0xf8, 0xf7, 0xf9, 0xfa, 0xfb, 0xfb, 0xfd, 0xfd, 0xff, 0xff, 0x00, 0x03, 0x05,
0x04, 0x05, 0x07, 0x09, 0x0a, 0x09, 0x0a, 0x0b, 0x0a, 0x0a, 0x0b, 0x0a, 0x0a, 0x09, 0x0a, 0x09,
0x07, 0x06, 0x05, 0x03, 0x03, 0x02, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00,
0x00, 0x00, 0x01, 0x01, 0x03, 0x02, 0x04, 0x05, 0x05, 0x05, 0x05, 0x06, 0x05, 0x04, 0x04, 0x03,
0x03, 0x00, 0x00, 0xff, 0xfe, 0xfe, 0xfd, 0xfd, 0xfc, 0xfc, 0xfb, 0xfc, 0xfb, 0xfb, 0xfb, 0xfb,
0xfb, 0xfa, 0xfa, 0xf9, 0xfb, 0xf9, 0xf9, 0xf9, 0xfa, 0xfa, 0xf8, 0xfa, 0xfa, 0xfa, 0xf9, 0xfa,
0xf9, 0xfa, 0xfa, 0xfa, 0xf9, 0xf8, 0xf9, 0xf7, 0xf9, 0xf9, 0xf9, 0xf8, 0xf9, 0xf8, 0xf9, 0xf8,
0xf9, 0xf8, 0xf8, 0xf8, 0xf8, 0xf9, 0xf6, 0xf8, 0xf7, 0xf7, 0xf8, 0xf7, 0xf9, 0xf9, 0xf9, 0xfa,
0xfb, 0xfb, 0xfa, 0xfc, 0xfc, 0xfd, 0xfc, 0xfc, 0xfe, 0xfc, 0xfe, 0xfd, 0xff, 0xfe, 0xff, 0xfe,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0xff, 0xfd, 0xff, 0x00, 0xff, 0xfe, 0xff, 0xff, 0x00,
0xfe, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0x01, 0x01, 0x00, 0x03, 0x01, 0x02, 0x00, 0x02, 0x01,
0x00, 0x01, 0x01, 0x02, 0x00, 0x01, 0x01, 0x02, 0x00, 0x00, 0x01, 0x01, 0x01, 0x00, 0x01, 0x00,
0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0xfe, 0x01, 0xff, 0x00, 0xff, 0x00, 0x00, 0xff, 0x00, 0xfe, 0x00, 0xff, 0xff, 0xfe, 0xff,
0xff, 0xff, 0xff, 0xfe, 0x00, 0xff, 0x00, 0xfe, 0x00, 0xff, 0xfe, 0xff, 0xfe, 0x01, 0xff, 0xff,
0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00,
0x00, 0xff, 0xff, 0x00, 0xff, 0xfe, 0x00, 0x00, 0x00, 0xfe, 0xfe, 0xff, 0xff, 0x00, 0xfe, 0xfe,
0xff, 0xff, 0x00, 0xff, 0xfe, 0x00, 0xff, 0xfe, 0xff, 0xfe, 0xff, 0xff, 0xfd, 0xff, 0xfe, 0xfe,
0xfe, 0xfd, 0xfe, 0xfd, 0xfe, 0xfe, 0xfd, 0xfd, 0xff, 0xfd, 0xfe, 0xfd, 0xff, 0x00, 0xfe, 0xff,
0xfe, 0x00, 0xfd, 0xff, 0xff, 0x00, 0x00, 0xfd, 0x00, 0xfe, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00,
0xfe, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0xff, 0x00, 0x00, 0x00,
0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0xfe, 0x00, 0x01, 0x00, 0x00, 0xff, 0x01,
0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00,
0xff, 0x00, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0xff, 0xff, 0xfe, 0xfe, 0xff, 0xfe, 0xff, 0x00,
0xfe, 0xfe, 0xfe, 0xfe, 0xff, 0xff, 0xfe, 0x00, 0xff, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xfe, 0xfd, 0xff, 0xff, 0xfe, 0xff, 0xfe, 0xff, 0xfe, 0xfe, 0xff, 0xff, 0xfe,
0xfe, 0xfe, 0xff, 0xff, 0xfe, 0xff, 0xff, 0x00, 0xfe, 0x00, 0xff, 0x00, 0x01, 0xff, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xfe, 0xff, 0xff, 0xfd,
0xff, 0xff, 0xfd, 0xff, 0xfe, 0xff, 0x00, 0xfe, 0xff, 0xfe, 0xff, 0xfe, 0xfe, 0x00, 0xff, 0xff,
0x00, 0xff, 0xff, 0x00, 0xff, 0x00, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff,
0xff, 0xff, 0xff, 0xfe, 0x00, 0xfe, 0xfe, 0xfe, 0xff, 0xfe, 0xfe, 0xfe, 0xfe, 0xfd, 0xfd, 0xfe,
0xfd, 0xfd, 0xfc, 0xfe, 0xfd, 0xfc, 0xfc, 0xfd, 0xfb, 0xfb, 0xfb, 0xfb, 0xfd, 0xfc, 0xfb, 0xfc,
0xfc, 0xfc, 0xfc, 0xfb, 0xff, 0xfc, 0xfd, 0xfc, 0xfc, 0xfd, 0xfc, 0xfd, 0xfb, 0xfd, 0xfd, 0xfc,
0xfb, 0xfc, 0xfd, 0xfc, 0xfb, 0xfd, 0xfb, 0xfb, 0xfc, 0xfb, 0xfd, 0xfc, 0xfc, 0xfc, 0xfb, 0xfb,
0xfd, 0xfd, 0xfc, 0xfc, 0xfb, 0xfd, 0xfc, 0xfd, 0xfc, 0xfd, 0xfe, 0xfd, 0xfd, 0xfc, 0xfe, 0xfc,
0xfd, 0xfc, 0xfd, 0xfe, 0xfc, 0xfd, 0xfe, 0xfe, 0xfc, 0xfd, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe,
0xfe, 0xfe, 0xfe, 0xff, 0xfe, 0xff, 0xfe, 0xfe, 0xfe, 0xfe, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff,
0xfe, 0xff, 0xfe, 0x00, 0xff, 0xff, 0xff, 0xfe, 0xfe, 0xff, 0xfe, 0xff, 0x00, 0xfe, 0x00, 0xfe,
0xff, 0x00, 0xfe, 0x00, 0x00, 0xff, 0x00, 0xff, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x01,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x01, 0x00, 0x00, 0xff, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0x01, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x01,
0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0xff, 0x02, 0x01, 0x00,
0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0xff, 0x00, 0xff, 0x00, 0x00, 0x00,
0xff, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0xff,
0x00, 0x00, 0x00, 0x00, 0xfe, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xfe, 0xff, 0x00, 0xff,
0x00, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00,
0x00, 0x00, 0x00, 0x00, 0xfe, 0x02, 0x00, 0x00, 0x00, 0xfe, 0x01, 0x02, 0x00, 0x04, 0x05, 0x02,
0x03, 0x02, 0x04, 0x02, 0x00, 0xff, 0x02, 0x00, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02,
0x00, 0x00, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff,
0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff,
0xfd, 0xfd, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfd, 0xfd, 0xfe, 0xff, 0xfe, 0xfd, 0xfd,
0xfd, 0xfd, 0xfb, 0xfb, 0xfb, 0xf9, 0xf8, 0xf7, 0xf7, 0xf7, 0xf6, 0xf5, 0xf4, 0xf3, 0xf1, 0xef,
0xed, 0xef, 0xf8, 0xfc, 0xfc, 0x00, 0x04, 0x08, 0x0a, 0x09, 0x08, 0x0b, 0x0b, 0x06, 0x03, 0x03,
0x04, 0x02, 0x01, 0x02, 0x03, 0x06, 0x06, 0x05, 0x08, 0x0a, 0x0a, 0x0a, 0x0a, 0x0b, 0x0b, 0x08,
0x04, 0x03, 0x02, 0x00, 0xfd, 0xfb, 0xfb, 0xfa, 0xf9, 0xf7, 0xf6, 0xf4, 0xf1, 0xee, 0xe9, 0xe3,
0xdd, 0xd7, 0xd5, 0xef, 0xfb, 0xf1, 0xfd, 0x0b, 0x11, 0x15, 0x11, 0x09, 0x10, 0x12, 0x02, 0xfa,
0xf8, 0xf4, 0xf1, 0xed, 0xe8, 0xee, 0xf9, 0xfb, 0xfb, 0x03, 0x0c, 0x11, 0x13, 0x11, 0x13, 0x18,
0x14, 0x0c, 0x0a, 0x08, 0x04, 0x00, 0xfb, 0xfc, 0x00, 0x00, 0x00, 0x04, 0x09, 0x0c, 0x0c, 0x09,
0x08, 0x07, 0x01, 0xfb, 0xf6, 0xf2, 0xee, 0xe8, 0xe2, 0xdd, 0xdc, 0xd6, 0xd2, 0xcc, 0xeb, 0x0f,
0xff, 0x05, 0x1c, 0x23, 0x26, 0x1e, 0x0b, 0x0a, 0x12, 0xfd, 0xe7, 0xe5, 0xe6, 0xeb, 0xef, 0xe9,
0xf0, 0x09, 0x17, 0x12, 0x15, 0x1f, 0x21, 0x1f, 0x10, 0x03, 0x04, 0x01, 0xf2, 0xe9, 0xf3, 0xfc,
0x01, 0x05, 0x0b, 0x18, 0x20, 0x1b, 0x15, 0x17, 0x16, 0x0e, 0x05, 0x00, 0x01, 0x01, 0xfa, 0xf6,
0xf9, 0xfb, 0xf9, 0xf7, 0xf7, 0xf9, 0xf7, 0xf0, 0xea, 0xe7, 0xde, 0xd9, 0xce, 0xd2, 0x0a, 0x06,
0xf2, 0x13, 0x1e, 0x22, 0x22, 0x10, 0x02, 0x0d, 0x0a, 0xe5, 0xdf, 0xee, 0xeb, 0xf0, 0xf3, 0xf1,
0x07, 0x1e, 0x14, 0x0b, 0x1b, 0x1b, 0x10, 0x07, 0xf6, 0xf2, 0xfa, 0xf2, 0xe1, 0xec, 0xfc, 0x01,
0x0d, 0x12, 0x1c, 0x2a, 0x23, 0x14, 0x14, 0x0f, 0x04, 0xfc, 0xf6, 0xf9, 0x00, 0xff, 0xfc, 0x05,
0x0b, 0x09, 0x04, 0x00, 0x00, 0x00, 0xf6, 0xed, 0xeb, 0xe7, 0xe0, 0xda, 0xd6, 0xd4, 0xd6, 0xd6,
0x09, 0x20, 0x01, 0x1e, 0x2d, 0x22, 0x1e, 0x08, 0xf5, 0xf4, 0xf8, 0xdd, 0xce, 0xed, 0xf2, 0xf8,
0x08, 0x07, 0x17, 0x29, 0x20, 0x09, 0x0c, 0x0d, 0xf6, 0xee, 0xe4, 0xde, 0xef, 0xf4, 0xee, 0xfa,
0x14, 0x16, 0x14, 0x17, 0x14, 0x1b, 0x15, 0x03, 0xfd, 0x04, 0x01, 0xfb, 0xfb, 0x01, 0x0d, 0x10,
0x09, 0x0b, 0x14, 0x0f, 0x05, 0x00, 0xff, 0xfc, 0xf5, 0xeb, 0xed, 0xf2, 0xee, 0xea, 0xeb, 0xee,
0xed, 0xe4, 0xda, 0xdc, 0xd7, 0x08, 0x28, 0xf7, 0x0d, 0x28, 0x16, 0x11, 0x00, 0xf1, 0xf3, 0xfd,
0xe8, 0xd1, 0xf9, 0x05, 0xfc, 0x0b, 0x09, 0x13, 0x21, 0x14, 0xfb, 0xfb, 0x08, 0xf1, 0xe7, 0xed,
0xea, 0xfc, 0x02, 0xfa, 0x01, 0x17, 0x18, 0x05, 0x08, 0x03, 0x01, 0x07, 0xf8, 0xf9, 0x09, 0x0a,
0x04, 0x0b, 0x0e, 0x12, 0x16, 0x09, 0x04, 0x0c, 0x06, 0xfa, 0xfc, 0xfd, 0xfd, 0xff, 0xfc, 0xfb,
0x00, 0xfd, 0xf4, 0xf4, 0xf1, 0xef, 0xec, 0xe8, 0xe2, 0xe1, 0xda, 0xd1, 0xd4, 0x00, 0x2c, 0x01,
0x08, 0x33, 0x1f, 0x18, 0x03, 0xe8, 0xf0, 0xf7, 0xe4, 0xcc, 0xeb, 0x0a, 0x00, 0x0c, 0x11, 0x14,
0x28, 0x17, 0xfa, 0xf4, 0x00, 0xf2, 0xdd, 0xe6, 0xea, 0xfb, 0x0a, 0x00, 0x06, 0x1c, 0x20, 0x0a,
0x01, 0x01, 0xfa, 0xf7, 0xf8, 0xf6, 0xfe, 0x0e, 0x0c, 0x0d, 0x17, 0x17, 0x0f, 0x0d, 0x07, 0x01,
0x02, 0xfd, 0xfa, 0x01, 0x03, 0x00, 0x03, 0x03, 0x01, 0x00, 0xfb, 0xf4, 0xf4, 0xf1, 0xe9, 0xea,
0xec, 0xea, 0xe9, 0xe4, 0xe3, 0xe1, 0xdc, 0xdb, 0x12, 0x2f, 0xf8, 0x12, 0x2f, 0x0f, 0x0e, 0xf8,
0xe0, 0xed, 0xf5, 0xe4, 0xd3, 0xfd, 0x14, 0x03, 0x16, 0x14, 0x12, 0x21, 0x08, 0xed, 0xec, 0xf9,
0xea, 0xdd, 0xf0, 0xf5, 0x05, 0x13, 0x06, 0x0d, 0x1d, 0x16, 0xfd, 0xf6, 0xf9, 0xef, 0xf0, 0xfd,
0xfa, 0x03, 0x18, 0x13, 0x10, 0x18, 0x12, 0x06, 0x05, 0xfd, 0xfb, 0x00, 0xfd, 0xfd, 0x06, 0x07,
0x03, 0x05, 0x01, 0xff, 0xff, 0xf7, 0xf2, 0xf5, 0xf5, 0xef, 0xee, 0xef, 0xf1, 0xf2, 0xec, 0xe9,
0xea, 0xe6, 0xdc, 0xd7, 0xea, 0x2c, 0x15, 0xef, 0x29, 0x23, 0x0c, 0x08, 0xe8, 0xe8, 0xf7, 0xef,
0xd8, 0xde, 0x10, 0x0d, 0x01, 0x17, 0x0f, 0x1b, 0x19, 0xf7, 0xeb, 0xf6, 0xfa, 0xe1, 0xe2, 0xf9,
0xfd, 0x0d, 0x0d, 0x03, 0x16, 0x1b, 0x08, 0xf3, 0xf8, 0xf9, 0xf1, 0xfe, 0xfa, 0xff, 0x15, 0x16,
0x0b, 0x13, 0x15, 0x09, 0x04, 0xfe, 0xfb, 0x02, 0x01, 0xfb, 0x05, 0x0c, 0x05, 0x03, 0x03, 0x00,
0xff, 0xfc, 0xf3, 0xf4, 0xf9, 0xf3, 0xf1, 0xf1, 0xf3, 0xf3, 0xef, 0xed, 0xef, 0xee, 0xe7, 0xe2,
0xe0, 0xd8, 0xfb, 0x33, 0x00, 0xf9, 0x34, 0x19, 0x05, 0x01, 0xe9, 0xec, 0xf5, 0xeb, 0xd7, 0xed,
0x1a, 0x04, 0x04, 0x1c, 0x11, 0x18, 0x0e, 0xf3, 0xee, 0xf9, 0xf4, 0xdc, 0xeb, 0x01, 0xfe, 0x0c,
0x0b, 0x09, 0x17, 0x14, 0xff, 0xf2, 0xfd, 0xf9, 0xfb, 0xfd, 0xfa, 0x0b, 0x16, 0x0c, 0x0a, 0x15,
0x0f, 0x06, 0x02, 0x00, 0x02, 0x04, 0xfd, 0xff, 0x0a, 0x07, 0x01, 0x01, 0x02, 0x00, 0xfd, 0xf7,
0xf6, 0xf9, 0xf7, 0xf0, 0xf2, 0xf4, 0xf2, 0xf1, 0xef, 0xef, 0xf3, 0xee, 0xe5, 0xe8, 0xe7, 0xdd,
0xe5, 0x25, 0x1f, 0xec, 0x22, 0x2a, 0x03, 0x08, 0xf4, 0xe6, 0xf2, 0xf2, 0xe0, 0xde, 0x0c, 0x13,
0xfb, 0x16, 0x17, 0x10, 0x14, 0xfd, 0xee, 0xf4, 0xfa, 0xe6, 0xe0, 0xff, 0x00, 0x02, 0x0f, 0x0a,
0x12, 0x17, 0x0a, 0xf7, 0xf9, 0x03, 0x00, 0xf7, 0xfc, 0x0a, 0x0f, 0x0c, 0x09, 0x0f, 0x13, 0x0c,
0x02, 0x02, 0x06, 0x02, 0xfd, 0xff, 0x05, 0x05, 0x00, 0xff, 0x02, 0x03, 0xfe, 0xfa, 0xfb, 0xfb,
0xf7, 0xf2, 0xf4, 0xf6, 0xf3, 0xee, 0xef, 0xf3, 0xf4, 0xed, 0xed, 0xee, 0xed, 0xeb, 0xe7, 0xe7,
0x08, 0x29, 0xfc, 0x00, 0x2a, 0x10, 0x00, 0xfb, 0xf0, 0xf5, 0xf6, 0xee, 0xe4, 0xfa, 0x17, 0x00,
0x01, 0x18, 0x11, 0x0e, 0x03, 0xf8, 0xf7, 0xfb, 0xf4, 0xe3, 0xf4, 0x06, 0xfd, 0x03, 0x09, 0x0e,
0x10, 0x0a, 0x00, 0xfd, 0x14, 0x02, 0xf2, 0x03, 0x0b, 0x02, 0x01, 0x08, 0x09, 0x12, 0x09, 0x02,
0x0b, 0x0f, 0xff, 0xfd, 0x03, 0x01, 0xfe, 0xfc, 0xfd, 0x02, 0x05, 0xfb, 0xfd, 0x01, 0xfc, 0xf3,
0xf3, 0xf3, 0xf2, 0xef, 0xeb, 0xf2, 0xf5, 0xf0, 0xed, 0xf0, 0xf2, 0xec, 0xe8, 0xed, 0xef, 0x11,
0x1f, 0xf2, 0x0d, 0x28, 0x06, 0xfd, 0xfa, 0xf7, 0xf6, 0xef, 0xec, 0xe9, 0x02, 0x10, 0xf6, 0x08,
0x1b, 0x0f, 0x08, 0x00, 0x00, 0xfc, 0xf9, 0xef, 0xe7, 0xfd, 0x00, 0xf5, 0x02, 0x0b, 0x10, 0x0a,
0x09, 0x05, 0x06, 0x16, 0xfd, 0xfa, 0x0a, 0x06, 0xf9, 0x01, 0x09, 0x06, 0x0c, 0x08, 0x0b, 0x11,
0x0c, 0x00, 0x05, 0x06, 0xfe, 0xfa, 0xfd, 0xfe, 0xff, 0xfe, 0xfc, 0x01, 0x00, 0xf9, 0xf7, 0xfb,
0xf4, 0xf0, 0xee, 0xeb, 0xf2, 0xef, 0xe7, 0xef, 0xf5, 0xea, 0xe9, 0xf4, 0xf1, 0xff, 0x22, 0x03,
0xfb, 0x27, 0x13, 0xf8, 0xfe, 0xfd, 0xf5, 0xee, 0xf0, 0xec, 0xf4, 0x0c, 0xfd, 0xfc, 0x1a, 0x13,
0x06, 0x06, 0x07, 0x00, 0xf8, 0xf6, 0xed, 0xf3, 0xff, 0xf1, 0xf9, 0x0a, 0x0b, 0x07, 0x09, 0x0f,
0x07, 0x14, 0x08, 0xfc, 0x0b, 0x07, 0xf8, 0xfe, 0x09, 0x00, 0x05, 0x09, 0x09, 0x0e, 0x0f, 0x04,
0x06, 0x0b, 0x00, 0xfb, 0x00, 0xfe, 0xf9, 0xfd, 0xfc, 0xff, 0x00, 0xfa, 0xfa, 0xff, 0xfa, 0xf1,
0xf4, 0xf4, 0xef, 0xf0, 0xee, 0xee, 0xf0, 0xee, 0xea, 0xec, 0xee, 0xe9, 0xf0, 0x1b, 0x12, 0xed,
0x20, 0x24, 0xfe, 0x02, 0x02, 0xfb, 0xf4, 0xee, 0xea, 0xec, 0x02, 0xfd, 0xee, 0x14, 0x17, 0x05,
0x0c, 0x10, 0x0b, 0x00, 0xfc, 0xf4, 0xf2, 0xfc, 0xed, 0xed, 0x04, 0x02, 0x00, 0x06, 0x12, 0x0e,
0x11, 0x13, 0x08, 0x0e, 0x09, 0xfb, 0xfd, 0x04, 0xfb, 0xfc, 0x06, 0x07, 0x0b, 0x0c, 0x0a, 0x0d,
0x0e, 0x02, 0x01, 0x04, 0xfe, 0xf9, 0xfc, 0xfd, 0xfc, 0xfd, 0xfa, 0xfc, 0xfe, 0xf9, 0xf7, 0xf8,
0xf5, 0xf7, 0xf0, 0xed, 0xf3, 0xed, 0xe6, 0xe9, 0xef, 0xe7, 0xe9, 0xed, 0xfe, 0x1e, 0x00, 0xfe,
0x2e, 0x16, 0xfe, 0x05, 0x05, 0xfe, 0xeb, 0xeb, 0xef, 0xf2, 0xfd, 0xee, 0xfd, 0x1b, 0x09, 0x05,
0x13, 0x16, 0x09, 0xfd, 0x00, 0xfb, 0xf7, 0xf2, 0xe8, 0xfa, 0xfd, 0xf4, 0xfd, 0x09, 0x0e, 0x04,
0x14, 0x1a, 0x0e, 0x0c, 0x0c, 0x06, 0x00, 0xfe, 0xf8, 0x00, 0x00, 0xfe, 0x04, 0x0c, 0x0a, 0x0a,
0x0e, 0x0b, 0x09, 0x04, 0x00, 0xff, 0xfc, 0xf6, 0xf8, 0xfa, 0xf6, 0xf6, 0xf9, 0xf9, 0xf8, 0xf6,
0xf7, 0xf9, 0xf3, 0xf3, 0xf4, 0xf1, 0xef, 0xef, 0xed, 0xec, 0xef, 0xeb, 0xed, 0xfc, 0x12, 0xff,
0xfd, 0x26, 0x11, 0xfe, 0x0e, 0x0a, 0xfe, 0xf6, 0xf4, 0xf5, 0xf3, 0xf5, 0xee, 0xfb, 0x0c, 0xfc,
0x01, 0x14, 0x0f, 0x07, 0x06, 0x0b, 0x04, 0xfd, 0xfa, 0xf5, 0xfc, 0xf5, 0xed, 0xfc, 0x02, 0x00,
0x02, 0x11, 0x12, 0x0f, 0x10, 0x11, 0x0e, 0x07, 0x02, 0x02, 0x01, 0xfb, 0xfd, 0x02, 0x02, 0x01,
0x05, 0x09, 0x0a, 0x07, 0x06, 0x08, 0x05, 0x00, 0xfe, 0xfe, 0xf9, 0xf5, 0xf4, 0xf6, 0xf5, 0xf2,
0xf5, 0xf8, 0xf8, 0xf5, 0xf9, 0xf8, 0xf6, 0xf3, 0xf3, 0xf2, 0xed, 0xea, 0xe8, 0xec, 0xec, 0x06,
0x00, 0xef, 0x1f, 0x18, 0xfc, 0x14, 0x17, 0x06, 0xff, 0xfe, 0xff, 0xf3, 0xf2, 0xee, 0xf0, 0x00,
0xf0, 0xf6, 0x0d, 0x05, 0x03, 0x0d, 0x12, 0x0a, 0x05, 0x08, 0x00, 0x00, 0xf9, 0xf0, 0xfb, 0xf5,
0xee, 0xf8, 0xff, 0x0a, 0x03, 0x07, 0x1c, 0x13, 0x09, 0x11, 0x14, 0x04, 0x03, 0x03, 0x00, 0xfc,
0xfa, 0xfe, 0x00, 0xfe, 0xff, 0x09, 0x06, 0x02, 0x07, 0x09, 0x03, 0x01, 0x00, 0xfd, 0xf9, 0xf4,
0xf4, 0xf3, 0xf0, 0xef, 0xf1, 0xf2, 0xf3, 0xf5, 0xf7, 0xf8, 0xf7, 0xf8, 0xf7, 0xf5, 0xf1, 0xf1,
0xef, 0xec, 0x05, 0xfb, 0xe7, 0x18, 0x0d, 0xf3, 0x14, 0x12, 0x03, 0x05, 0x07, 0x05, 0xfd, 0xfb,
0xf8, 0xf8, 0xfe, 0xeb, 0xf8, 0x05, 0xf4, 0xfc, 0x08, 0x04, 0x02, 0x07, 0x0d, 0x04, 0x05, 0x04,
0x00, 0x03, 0xf7, 0xf9, 0xff, 0xf8, 0xf5, 0x09, 0x01, 0xfa, 0x14, 0x0b, 0x05, 0x11, 0x11, 0x07,
0x0d, 0x08, 0x05, 0x07, 0xfe, 0x00, 0x02, 0xfc, 0xfb, 0x03, 0xfc, 0xfe, 0x04, 0x00, 0x00, 0x03,
0x01, 0x00, 0xff, 0xfb, 0xfe, 0xf9, 0xf4, 0xf7, 0xf6, 0xf0, 0xf4, 0xf6, 0xf2, 0xf4, 0xf7, 0xf6,
0xf6, 0xf8, 0xf5, 0xf7, 0xf5, 0xf3, 0xf4, 0xf9, 0x02, 0xf7, 0xfb, 0x12, 0xff, 0xfe, 0x12, 0x06,
0x02, 0x07, 0x05, 0x04, 0xfe, 0xfe, 0xff, 0xfc, 0xf7, 0xf6, 0x00, 0xf9, 0xf4, 0x02, 0x00, 0xfd,
0x03, 0x06, 0x05, 0x03, 0x06, 0x05, 0x02, 0x01, 0xff, 0x01, 0xfb, 0xfb, 0x06, 0xfa, 0xfc, 0x0a,
0x01, 0x00, 0x0c, 0x08, 0x04, 0x0a, 0x08, 0x07, 0x06, 0x03, 0x05, 0x02, 0xfe, 0x01, 0x00, 0xfa,
0x00, 0x00, 0xfd, 0xff, 0x00, 0xff, 0x00, 0x00, 0xfe, 0xff, 0xfc, 0xfc, 0xfc, 0xf8, 0xf7, 0xfa,
0xf6, 0xf4, 0xf9, 0xf7, 0xf4, 0xfa, 0xf8, 0xf5, 0xfa, 0xf7, 0xf4, 0xf7, 0xf4, 0xf3, 0xf5, 0xf4,
0x02, 0xf7, 0xf7, 0x14, 0xfc, 0xff, 0x16, 0x03, 0x04, 0x0b, 0x05, 0x05, 0xff, 0x00, 0x00, 0xf9,
0xf8, 0xf9, 0xfe, 0xf4, 0xf6, 0x02, 0xf8, 0xfb, 0x03, 0x00, 0x01, 0x02, 0x06, 0x05, 0x01, 0x05,
0x04, 0x02, 0xfd, 0x02, 0x07, 0xf6, 0x02, 0x09, 0xfa, 0x01, 0x0a, 0x00, 0x01, 0x0a, 0x04, 0x05,
0x05, 0x06, 0x06, 0x01, 0x03, 0x05, 0xff, 0x00, 0x04, 0xfd, 0xfe, 0x01, 0xfe, 0xfd, 0x00, 0xfe,
0xfe, 0xfe, 0xfd, 0xff, 0xfc, 0xfc, 0xfd, 0xfb, 0xf9, 0xfb, 0xf8, 0xf6, 0xf9, 0xf6, 0xf6, 0xf8,
0xf7, 0xf8, 0xf7, 0xf8, 0xf9, 0xf5, 0xf5, 0xf8, 0xf1, 0x00, 0xfd, 0xe9, 0x13, 0x01, 0xed, 0x1a,
0x03, 0xfb, 0x12, 0x03, 0x05, 0x07, 0xfe, 0x09, 0xfe, 0xfa, 0x00, 0xfe, 0xf6, 0xf6, 0x01, 0xf4,
0xf7, 0x03, 0xf9, 0xfd, 0x02, 0x00, 0x05, 0x00, 0x04, 0x07, 0x00, 0x02, 0x03, 0x01, 0xfa, 0x08,
0x00, 0xf9, 0x0b, 0x00, 0xff, 0x07, 0x05, 0x00, 0x08, 0x04, 0x04, 0x05, 0x03, 0x07, 0x01, 0x03,
0x05, 0x01, 0x00, 0x05, 0x00, 0xff, 0x03, 0xff, 0xfe, 0x00, 0xff, 0xfd, 0xff, 0xfd, 0xfd, 0xfc,
0xfd, 0xfd, 0xfa, 0xfc, 0xfd, 0xf9, 0xf9, 0xfc, 0xf8, 0xf9, 0xf9, 0xf8, 0xf9, 0xf8, 0xf8, 0xf8,
0xf8, 0xf7, 0xf6, 0xf7, 0xf4, 0x00, 0xfb, 0xef, 0x10, 0xfd, 0xf4, 0x16, 0xff, 0xff, 0x10, 0x00,
0x08, 0x06, 0xff, 0x0b, 0xfe, 0xfd, 0x04, 0xfb, 0xf8, 0xfd, 0xfd, 0xf5, 0xfa, 0x00, 0xf7, 0xfd,
0x00, 0xff, 0x02, 0xfe, 0x05, 0x03, 0xff, 0x05, 0x04, 0xff, 0x01, 0x02, 0x03, 0x00, 0xfe, 0x0b,
0xff, 0x00, 0x0a, 0x01, 0x00, 0x09, 0x04, 0x00, 0x08, 0x03, 0x03, 0x03, 0x04, 0x03, 0x00, 0x02,
0x02, 0xff, 0x00, 0x01, 0xfd, 0x00, 0x00, 0xfd, 0xfd, 0xff, 0xfc, 0xfb, 0xfc, 0xfb, 0xfa, 0xf9,
0xfc, 0xfb, 0xfa, 0xfb, 0xfc, 0xf9, 0xfb, 0xfd, 0xf8, 0xfb, 0xfa, 0xf7, 0xf8, 0xf6, 0xf6, 0xf4,
0xf1, 0xf7, 0xff, 0xef, 0xf9, 0x0d, 0xf0, 0x00, 0x11, 0xf8, 0x06, 0x0b, 0x00, 0x0b, 0x01, 0x03,
0x0b, 0xf9, 0x02, 0x05, 0xf7, 0xfd, 0x00, 0xf9, 0xf7, 0xfd, 0xfb, 0xf7, 0xfd, 0xfd, 0xfd, 0xfd,
0xfe, 0x03, 0xfe, 0x00, 0x07, 0x00, 0x00, 0x05, 0x01, 0x00, 0x00, 0x07, 0xff, 0xfe, 0x0b, 0xfd,
0xff, 0x08, 0x00, 0xff, 0x08, 0x03, 0x00, 0x07, 0x04, 0x03, 0x02, 0x06, 0x02, 0x00, 0x03, 0x01,
0xfd, 0x00, 0x01, 0xfa, 0xff, 0x00, 0xfc, 0xfc, 0xff, 0xfc, 0xfb, 0xfe, 0xfc, 0xfc, 0xfb, 0xfd,
0xfb, 0xfa, 0xfd, 0xfb, 0xf8, 0xfc, 0xfc, 0xf8, 0xfc, 0xfb, 0xf9, 0xfa, 0xf9, 0xf8, 0xf7, 0xf5,
0xf5, 0xf4, 0xfb, 0xf7, 0xef, 0x08, 0xfa, 0xf2, 0x10, 0xfd, 0xfd, 0x0f, 0x00, 0x06, 0x09, 0xff,
0x0c, 0x00, 0xfe, 0x0a, 0xfa, 0xfb, 0x01, 0xfa, 0xf8, 0xfc, 0xfc, 0xf8, 0xfb, 0xfe, 0xfb, 0xfd,
0xfe, 0x00, 0x00, 0xfd, 0x05, 0x01, 0xff, 0x06, 0x02, 0x02, 0x04, 0x03, 0x03, 0x03, 0x03, 0x03,
0x03, 0x02, 0x03, 0x01, 0x03, 0x02, 0x00, 0x03, 0x03, 0x00, 0x01, 0x04, 0x01, 0x01, 0x03, 0x01,
0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xfd, 0xfd, 0xfd, 0xfd, 0xfc, 0xfd, 0xfd,
0xfc, 0xfc, 0xfd, 0xfd, 0xfc, 0xfe, 0xfd, 0xfd, 0xfd, 0xfc, 0xfc, 0xfb, 0xfc, 0xfa, 0xf9, 0xfa,
0xf8, 0xf8, 0xf7, 0xf5, 0xf8, 0xfe, 0xf0, 0xfb, 0x08, 0xed, 0x02, 0x0c, 0xf4, 0x07, 0x0a, 0xfe,
0x0a, 0x04, 0x05, 0x0b, 0xfd, 0x08, 0x06, 0xfa, 0x03, 0x01, 0xfb, 0xfc, 0x00, 0xfb, 0xf9, 0x00,
0xfd, 0xfa, 0x00, 0xff, 0xff, 0xff, 0x00, 0x04, 0x00, 0x01, 0x06, 0x02, 0x02, 0x08, 0x04, 0x03,
0x08, 0x04, 0x03, 0x06, 0x05, 0x02, 0x04, 0x03, 0x01, 0x02, 0x02, 0x01, 0x00, 0x02, 0x02, 0x00,
0x02, 0x03, 0x01, 0x01, 0x02, 0x00, 0x00, 0x01, 0x00, 0xff, 0x00, 0xff, 0xfd, 0xfe, 0xfe, 0xfc,
0xfc, 0xfd, 0xfc, 0xfc, 0xfd, 0xfc, 0xfc, 0xfd, 0xfd, 0xfc, 0xfe, 0xfc, 0xfc, 0xfc, 0xfc, 0xfa,
0xfb, 0xfa, 0xf8, 0xf8, 0xf6, 0xf6, 0xf9, 0xfc, 0xf1, 0xfd, 0x04, 0xee, 0x02, 0x08, 0xf5, 0x05,
0x09, 0xfe, 0x06, 0x06, 0x03, 0x05, 0x00, 0x07, 0x01, 0xfd, 0x02, 0xff, 0xfc, 0xfb, 0x00, 0xfc,
0xf7, 0xff, 0xfd, 0xf7, 0xff, 0xfe, 0xfa, 0xfe, 0xff, 0x00, 0xfc, 0x03, 0x03, 0xfd, 0x08, 0x04,
0x00, 0x06, 0x07, 0x00, 0x04, 0x07, 0x00, 0x03, 0x04, 0x01, 0x00, 0x03, 0x00, 0xff, 0x01, 0x00,
0xff, 0x00, 0x00, 0xff, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0xfe, 0xff, 0xff, 0xfd, 0xfc, 0xfd,
0xfc, 0xfa, 0xfc, 0xfb, 0xfa, 0xfb, 0xfb, 0xfa, 0xfb, 0xfc, 0xfb, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc,
0xfb, 0xfa, 0xfb, 0xfa, 0xf9, 0xf8, 0xf7, 0xf6, 0xfb, 0xfc, 0xf1, 0x00, 0x01, 0xf1, 0x03, 0x06,
0xf7, 0x01, 0x09, 0xff, 0x00, 0x07, 0x05, 0xfe, 0x02, 0x08, 0xfd, 0x00, 0x03, 0xfd, 0xfd, 0xfe,
0xff, 0xfb, 0xf9, 0xfe, 0xfc, 0xf9, 0xfe, 0xff, 0xfc, 0xfd, 0x00, 0x01, 0xfc, 0x01, 0x04, 0xff,
0x01, 0x05, 0x01, 0x00, 0x06, 0x03, 0x00, 0x05, 0x03, 0x00, 0x02, 0x03, 0x00, 0x00, 0x02, 0x00,
0xff, 0x00, 0x00, 0xfe, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xfe, 0xfd, 0xfd,
0xfd, 0xfc, 0xfb, 0xfb, 0xfb, 0xfa, 0xfb, 0xfb, 0xfa, 0xfb, 0xfb, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc,
0xfc, 0xfc, 0xfb, 0xfa, 0xfa, 0xfa, 0xf9, 0xf8, 0xf8, 0xf7, 0xf7, 0xfc, 0xf9, 0xf4, 0x00, 0x00,
0xf6, 0x00, 0x05, 0xfe, 0xfe, 0x06, 0x06, 0xff, 0x02, 0x09, 0x00, 0xff, 0x06, 0x01, 0xfd, 0x00,
0x00, 0xfd, 0xfb, 0xff, 0xfd, 0xfa, 0xfe, 0xfd, 0xfc, 0xfd, 0xfe, 0xfd, 0xfe, 0xff, 0xff, 0xff,
0x00, 0x00, 0x00, 0x05, 0x00, 0x01, 0x05, 0x03, 0x01, 0x04, 0x05, 0x01, 0x03, 0x04, 0x01, 0x01,
0x02, 0x00, 0x00, 0x01, 0xff, 0xff, 0x00, 0xff, 0xfe, 0x00, 0x00, 0xfd, 0x00, 0x00, 0xfe, 0xff,
0xff, 0xfd, 0xfd, 0xfe, 0xfc, 0xfd, 0xfd, 0xfb, 0xfb, 0xfd, 0xfc, 0xfb, 0xfd, 0xfd, 0xfc, 0xfd,
0xfd, 0xfc, 0xfd, 0xfd, 0xfc, 0xfc, 0xfd, 0xfc, 0xfb, 0xfb, 0xfb, 0xfa, 0xfa, 0xf9, 0xfa, 0xfd,
0xfb, 0xf9, 0xfe, 0x00, 0xfd, 0xfe, 0x01, 0x01, 0x00, 0x02, 0x03, 0x01, 0x02, 0x03, 0x01, 0x01,
0x02, 0x00, 0x00, 0x00, 0x00, 0xff, 0xfe, 0xff, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe,
0xfe, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x02, 0x02, 0x01, 0x02, 0x02, 0x00,
0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfd, 0xfe, 0xfe,
0xfd, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfd, 0xfd, 0xfd, 0xfd, 0xfc, 0xfc, 0xfc,
0xfc, 0xfc, 0xfc, 0xfc, 0xfb, 0xfb, 0xfc, 0xfc, 0xfb, 0xfc, 0xfc, 0xfd, 0xff, 0xfe, 0xff, 0x00,
0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff,
0xff, 0xff, 0xfe, 0xfe, 0xfe, 0xfd, 0xfe, 0xfe, 0xfe, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff,
0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00,
0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x02, 0x02, 0x03, 0x04, 0x04,
0x05, 0x05, 0x05, 0x06, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x04, 0x05, 0x04, 0x04, 0x04, 0x04,
0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
0x04, 0x04, 0x03, 0x03, 0x03, 0x02, 0x02, 0x01, 0x01, 0x00, 0x00, 0x00, 0xff, 0xff, 0xfe, 0xfe,
0xfd, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfd, 0xfd, 0xfd, 0xfe, 0xff, 0xff, 0xff,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xfe, 0xfe, 0xfd, 0xfd, 0xfd,
0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfc, 0xfc, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
0xfb, 0xfb, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfa, 0xfa, 0xfa, 0xf9, 0xf9,
0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9,
0xf9, 0xf9, 0xfa, 0xf9, 0xf9, 0xf9, 0xfa, 0xf9, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa,
0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfb, 0xfa, 0xfa, 0xfa, 0xfa, 0xfb,
0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfd, 0xfd, 0xfd, 0xfd,
0xfd, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff,
0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x01, 0x01, 0x01, 0x00,
0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00,
0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff,
0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x01, 0x02, 0x02, 0x02, 0x02,
0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xfe,
0xfe, 0xff, 0xfe, 0xfd, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfd,
0xfd, 0xfe, 0xfd, 0xfc, 0xfc, 0xfd, 0xfd, 0xfc, 0xfc, 0xfc, 0xfd, 0xfd, 0xfd, 0xfd, 0xfe, 0xff,
0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xfe,
0xff, 0xff, 0xfe, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xfe, 0xff, 0xfe, 0xfe, 0xfe,
0xff, 0xfe, 0xfd, 0xfe, 0xfe, 0xff, 0xfe, 0xfd, 0xfe, 0xfe, 0xfe, 0xfe, 0xfd, 0xfe, 0xfe, 0xfe,
0xfe, 0xfe, 0xfe, 0xfe, 0xff, 0xfd, 0xfd, 0xfe, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfc, 0xfc,
0xfd, 0xfd, 0xfc, 0xfd, 0xfd, 0xfd, 0xfc, 0xfc, 0xfc, 0xfe, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd,
0xfc, 0xfc, 0xfd, 0xfc, 0xfd, 0xfd, 0xfe, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe,
0xfe, 0xfe, 0xfd, 0xfc, 0xfc, 0xfc, 0xfb, 0xfc, 0xfb, 0xf9, 0xfc, 0xf9, 0xfa, 0xf9, 0xf9, 0xfc,
0xf8, 0xf9, 0xf9, 0xf9, 0xf8, 0xfa, 0xf6, 0xf8, 0xfb, 0xf6, 0xf9, 0xf7, 0xf9, 0xfa, 0xf7, 0xfa,
0xf9, 0xfa, 0xfb, 0xfa, 0xfb, 0xfd, 0xfd, 0xfc, 0xff, 0xff, 0xfe, 0x00, 0x00, 0xfe, 0xff, 0x00,
0xfe, 0xfd, 0xff, 0xff, 0xff, 0x00, 0x01, 0x02, 0x02, 0x02, 0x03, 0x03, 0x03, 0x03, 0x03, 0x02,
0x02, 0x03, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01,
0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xfe, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe,
0xff, 0xff, 0xff, 0xfe, 0xff, 0x05, 0x02, 0x00, 0x01, 0xfd, 0x01, 0x00, 0xfc, 0x01, 0xff, 0xff,
0x00, 0xff, 0x00, 0xff, 0xfe, 0x05, 0x07, 0x01, 0x03, 0x06, 0x03, 0x01, 0x00, 0xff, 0xff, 0xfc,
0xfe, 0x00, 0x00, 0x00, 0x02, 0x03, 0x02, 0x01, 0xff, 0x01, 0x00, 0x00, 0xff, 0x00, 0x01, 0x01,
0x02, 0x00, 0x00, 0xfd, 0xff, 0xfe, 0xfd, 0x00, 0xfe, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff,
0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xfe, 0xfe, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xff,
0xfe, 0xfe, 0xfd, 0xfe, 0xfe, 0xfd, 0xff, 0xfe, 0xff, 0xfe, 0xfd, 0xff, 0xfd, 0xfe, 0xfc, 0xfe,
0xfe, 0xfe, 0xff, 0xfc, 0x00, 0xfc, 0x00, 0xfc, 0xff, 0xfe, 0xfc, 0xff, 0xfa, 0x00, 0xfa, 0xfe,
0xfb, 0xfd, 0xfe, 0xfa, 0xff, 0xfb, 0xfe, 0xfc, 0xfd, 0xfd, 0x00, 0x00, 0xff, 0x01, 0xff, 0xff,
0xfa, 0xfb, 0xfc, 0xfd, 0xfa, 0xfc, 0xfe, 0xfd, 0xfe, 0xfc, 0xfe, 0xfe, 0xff, 0xfc, 0xfe, 0xfe,
0xfd, 0xfd, 0xfb, 0xfc, 0xfa, 0xfc, 0xfa, 0xfd, 0xfc, 0xfe, 0xfd, 0xfc, 0xfe, 0xfc, 0xfe, 0xfb,
0xfe, 0xfb, 0xfc, 0xfb, 0xfc, 0xfd, 0xfb, 0xfd, 0xfb, 0xfd, 0xfc, 0xfc, 0xfc, 0xfd, 0xfd, 0xfc,
0xff, 0xfb, 0xfe, 0xfd, 0xfd, 0xfd, 0xfd, 0xfe, 0xfc, 0xfe, 0xfc, 0xfd, 0xfd, 0xfc, 0xfe, 0xfe,
0xfe, 0xfd, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0x00, 0xfe, 0xff, 0xfe, 0xfc, 0xff, 0xfc, 0xfd,
0xfd, 0xff, 0xfe, 0xfc, 0xfe, 0xfc, 0xff, 0xfb, 0xfe, 0xfe, 0xfd, 0xff, 0xfc, 0xff, 0xfd, 0xfe,
0xfc, 0xfe, 0xfe, 0xfd, 0xfe, 0xfc, 0xff, 0xfd, 0xfe, 0xfc, 0xfe, 0xfe, 0xfd, 0xfd, 0xfc, 0xfe,
0xfb, 0xfc, 0xfb, 0xfe, 0xfc, 0xfc, 0xfe, 0xfd, 0xfe, 0xfb, 0xfe, 0xfd, 0xfd, 0xfc, 0xfd, 0xff,
0xfd, 0xff, 0xfe, 0x00, 0xfe, 0xff, 0xfe, 0x00, 0xff, 0xfe, 0xff, 0xff, 0x00, 0xff, 0x00, 0xff,
0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0xfe, 0x00, 0xfe, 0x00, 0xff, 0x00, 0x00, 0x00, 0x01, 0xff,
0x01, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00,
0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xfe, 0xff, 0xff, 0xfe, 0xfe, 0xff,
0xff, 0xff, 0xfe, 0xfe, 0xff, 0xff, 0xfe, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00,
0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0xfe, 0xff, 0xfe, 0xff, 0xfd, 0xfe,
0xfd, 0xfd, 0xfd, 0xfd, 0xfe, 0xfd, 0xfe, 0xfe, 0xfe, 0xff, 0x00, 0x00, 0x00, 0x00, 0x02, 0x02,
0x02, 0x02, 0x02, 0x03, 0x02, 0x02, 0x02, 0x02, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff,
0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0x00, 0x01, 0x01, 0x01, 0x00,
0x01, 0x00, 0x00, 0xff, 0x00, 0xff, 0xfe, 0xfe, 0xfd, 0xfd, 0xfd, 0xfd, 0xfc, 0xfd, 0xfe, 0xfd,
0xff, 0xfe, 0xff, 0xfe, 0xff, 0xfe, 0xff, 0x00, 0x00, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x01,
0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x03, 0x03, 0x03, 0x03, 0x04, 0x04,
0x04, 0x03, 0x04, 0x04, 0x05, 0x04, 0x05, 0x05, 0x06, 0x06, 0x05, 0x05, 0x06, 0x06, 0x05, 0x05,
0x05, 0x05, 0x04, 0x04, 0x04, 0x04, 0x04, 0x03, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
0x04, 0x04, 0x04, 0x04, 0x04, 0x03, 0x03, 0x03, 0x03, 0x03, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
0x03, 0x03, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x02, 0x02, 0x03,
0x03, 0x02, 0x02, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xfe, 0xfe, 0xfd, 0xfc,
0xfc, 0xfc, 0xfb, 0xfb, 0xfa, 0xfa, 0xf9, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8,
0xf8, 0xf9, 0xf9, 0xfa, 0xfa, 0xfa, 0xfb, 0xfb, 0xfb, 0xfc, 0xfc, 0xfd, 0xfd, 0xfd, 0xfe, 0xfe,
0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xff, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe,
0xfe, 0xfe, 0xfe, 0xfe, 0xfd, 0xfd, 0xfd, 0xfe, 0xfe, 0xfd, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe,
0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe,
0xfe, 0xfe, 0xfe, 0xfe, 0xfd, 0xfd, 0xfd, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe,
0xff, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe,
0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe,
0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe,
0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe,
0xff, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe,
0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe,
0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe,
0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe,
0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xfe, 0xfe, 0xff, 0xfe, 0xfe, 0xfe,
0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe,
0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe,
0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe,
0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe,
0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe,
0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe,
0xfe, 0xfe, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff,
0xff, 0xff, 0x00, 0x00, 0x00
};
const uint16_t Resource::_gameSavedSoundLen = 8005;
const Game::pge_OpcodeProc Game::_pge_opcodeTable[] = { const Game::pge_OpcodeProc Game::_pge_opcodeTable[] = {
/* 0x00 */ /* 0x00 */
0, 0,
@ -3214,6 +3733,174 @@ const uint8_t Game::_protectionCodeData[] = {
0x37, 0x37, 0x39, 0xF9 0x37, 0x37, 0x39, 0xF9
}; };
const uint8_t Game::_protectionWordData[] = {
0x01, 0x01, 0x02, 0x05, 0xD7, 0x77, 0x3F, 0xF7, 0x27, 0x7F, 0xFF, 0x1F, 0xF7, 0x51, 0x51, 0x51,
0x51, 0x00, 0x01, 0x01, 0x05, 0x03, 0xDF, 0xFF, 0xF7, 0x9F, 0x7F, 0x51, 0x51, 0x51, 0x51, 0x51,
0x51, 0x51, 0x51, 0x00, 0x01, 0x01, 0x05, 0x06, 0x97, 0x67, 0xF7, 0x3F, 0xF7, 0x1F, 0x51, 0x51,
0x51, 0x51, 0x51, 0x51, 0x51, 0x00, 0x01, 0x02, 0x02, 0x07, 0x17, 0xD7, 0x97, 0x87, 0xFF, 0x5F,
0x51, 0x51, 0x51, 0x51, 0x51, 0x51, 0x51, 0x00, 0x01, 0x02, 0x07, 0x01, 0x87, 0xF7, 0xCF, 0x17,
0xA7, 0xD7, 0x1F, 0x77, 0x51, 0x51, 0x51, 0x51, 0x51, 0x00, 0x02, 0x01, 0x03, 0x01, 0x5F, 0xA7,
0xBF, 0xF7, 0x1F, 0x51, 0x51, 0x51, 0x51, 0x51, 0x51, 0x51, 0x51, 0x00, 0x02, 0x01, 0x05, 0x01,
0xA7, 0x1F, 0xB7, 0xD7, 0x27, 0xC7, 0x0F, 0xD7, 0x7F, 0xC7, 0xA7, 0x27, 0x51, 0x00, 0x02, 0x02,
0x01, 0x05, 0x97, 0xA7, 0x67, 0xA7, 0x27, 0xCF, 0x51, 0x51, 0x51, 0x51, 0x51, 0x51, 0x51, 0x00,
0x02, 0x02, 0x02, 0x01, 0xE7, 0xA7, 0xA7, 0x27, 0x51, 0x51, 0x51, 0x51, 0x51, 0x51, 0x51, 0x51,
0x51, 0x00, 0x02, 0x02, 0x05, 0x01, 0x1F, 0xF7, 0x37, 0xFF, 0xB7, 0xF7, 0xF7, 0x9F, 0x51, 0x51,
0x51, 0x51, 0x51, 0x00, 0x02, 0x03, 0x02, 0x04, 0x5F, 0x1F, 0xC7, 0x9F, 0xA7, 0x27, 0x51, 0x51,
0x51, 0x51, 0x51, 0x51, 0x51, 0x00, 0x03, 0x01, 0x01, 0x04, 0x7F, 0xC7, 0x7F, 0xD7, 0x27, 0xB1,
0x9F, 0x51, 0x51, 0x51, 0x51, 0x51, 0x51, 0x00, 0x03, 0x01, 0x04, 0x01, 0xD7, 0x97, 0x97, 0xC7,
0x77, 0xF7, 0x27, 0x7F, 0x51, 0x51, 0x51, 0x51, 0x51, 0x00, 0x03, 0x02, 0x01, 0x01, 0x5F, 0x1F,
0xA7, 0x37, 0xF7, 0x9F, 0x9F, 0xA7, 0x1F, 0x51, 0x51, 0x51, 0x51, 0x00, 0x03, 0x02, 0x02, 0x05,
0x7F, 0xF7, 0x67, 0xF7, 0x5F, 0xA7, 0x1F, 0x7F, 0xF7, 0x1F, 0x51, 0x51, 0x51, 0x00, 0x03, 0x02,
0x04, 0x02, 0x97, 0x1F, 0xC7, 0xE7, 0xC7, 0x27, 0xD7, 0x67, 0x9F, 0x51, 0x51, 0x51, 0x51, 0x00,
0x03, 0x03, 0x01, 0x01, 0x9F, 0x97, 0xC7, 0xF7, 0x27, 0x7F, 0xC7, 0x9F, 0x7F, 0x9F, 0x51, 0x51,
0x51, 0x00, 0x03, 0x03, 0x02, 0x03, 0xB7, 0xF7, 0x27, 0xF7, 0x7F, 0xC7, 0x97, 0x51, 0x51, 0x51,
0x51, 0x51, 0x51, 0x00, 0x03, 0x03, 0x04, 0x01, 0xE7, 0xFF, 0x7F, 0xD7, 0x7F, 0xC7, 0xA7, 0x27,
0x9F, 0x51, 0x51, 0x51, 0x51, 0x00, 0x04, 0x01, 0x01, 0x05, 0xF7, 0xCF, 0xF7, 0x9F, 0x51, 0x51,
0x51, 0x51, 0x51, 0x51, 0x51, 0x51, 0x51, 0x00, 0x04, 0x01, 0x02, 0x01, 0x9F, 0xFF, 0x1F, 0x1F,
0xA7, 0xFF, 0x27, 0x77, 0xC7, 0x27, 0xB7, 0x51, 0x51, 0x00, 0x04, 0x01, 0x03, 0x05, 0x17, 0x1F,
0xD7, 0xC7, 0x27, 0x51, 0x51, 0x51, 0x51, 0x51, 0x51, 0x51, 0x51, 0x00, 0x04, 0x01, 0x04, 0x06,
0xB7, 0x1F, 0xF7, 0xD7, 0x7F, 0x51, 0x51, 0x51, 0x51, 0x51, 0x51, 0x51, 0x51, 0x00, 0x05, 0x01,
0x01, 0x03, 0xC7, 0x27, 0x7F, 0x1F, 0xA7, 0x77, 0xFF, 0x97, 0x7F, 0xC7, 0xA7, 0x27, 0x51, 0x00,
0x05, 0x01, 0x05, 0x07, 0xA7, 0x5F, 0x7F, 0xC7, 0xA7, 0x27, 0x9F, 0x51, 0x51, 0x51, 0x51, 0x51,
0x51, 0x00, 0x05, 0x01, 0x06, 0x06, 0xD7, 0x1F, 0x1F, 0xA7, 0xBF, 0x51, 0x51, 0x51, 0x51, 0x51,
0x51, 0x51, 0x51, 0x00, 0x05, 0x02, 0x02, 0x05, 0x9F, 0x87, 0xC7, 0x67, 0x67, 0x51, 0x51, 0x51,
0x51, 0x51, 0x51, 0x51, 0x51, 0x00, 0x05, 0x02, 0x03, 0x06, 0xB7, 0xD7, 0xE7, 0xF7, 0x51, 0x51,
0x51, 0x51, 0x51, 0x51, 0x51, 0x51, 0x51, 0x00, 0x06, 0x01, 0x03, 0x02, 0x9F, 0xFF, 0x1F, 0x3F,
0xC7, 0x3F, 0xD7, 0x67, 0x51, 0x51, 0x51, 0x51, 0x51, 0x00, 0x06, 0x01, 0x06, 0x06, 0xC7, 0x27,
0x3F, 0xC7, 0x9F, 0xC7, 0x17, 0x67, 0xF7, 0x51, 0x51, 0x51, 0x51, 0x00, 0x06, 0x01, 0x07, 0x01,
0x9F, 0x47, 0xC7, 0xF7, 0x67, 0x77, 0x51, 0x51, 0x51, 0x51, 0x51, 0x51, 0x51, 0x00, 0x06, 0x02,
0x05, 0x06, 0x9F, 0xC7, 0x7F, 0xFF, 0xD7, 0x7F, 0xC7, 0xA7, 0x27, 0x9F, 0x51, 0x51, 0x51, 0x00,
0x06, 0x02, 0x06, 0x05, 0x77, 0xF7, 0xD7, 0x7F, 0x47, 0x51, 0x51, 0x51, 0x51, 0x51, 0x51, 0x51,
0x51, 0x00, 0x07, 0x01, 0x02, 0x03, 0x5F, 0x1F, 0xF7, 0x97, 0xC7, 0x9F, 0xF7, 0x51, 0x51, 0x51,
0x51, 0x51, 0x51, 0x00, 0x07, 0x02, 0x07, 0x05, 0x97, 0xD7, 0x1F, 0x77, 0x51, 0x51, 0x51, 0x51,
0x51, 0x51, 0x51, 0x51, 0x51, 0x00, 0x08, 0x01, 0x0A, 0x02, 0x97, 0xC7, 0x27, 0xF7, 0xE7, 0xD7,
0x7F, 0xC7, 0x97, 0x51, 0x51, 0x51, 0x51, 0x00, 0x08, 0x02, 0x04, 0x06, 0xC7, 0x27, 0x3F, 0xF7,
0x27, 0x7F, 0xA7, 0x1F, 0xCF, 0x51, 0x51, 0x51, 0x51, 0x00, 0x08, 0x01, 0x10, 0x02, 0x77, 0xF7,
0x3F, 0xC7, 0x97, 0xF7, 0x51, 0x51, 0x51, 0x51, 0x51, 0x51, 0x51, 0x00, 0x09, 0x01, 0x01, 0x04,
0x7F, 0xD7, 0x4F, 0xC7, 0x51, 0x51, 0x51, 0x51, 0x51, 0x51, 0x51, 0x51, 0x51, 0x00, 0x09, 0x01,
0x03, 0x04, 0x9F, 0xFF, 0x17, 0xBF, 0xD7, 0xCF, 0x51, 0x51, 0x51, 0x51, 0x51, 0x51, 0x51, 0x00
};
const uint8_t Game::_protectionNumberDataAmiga[] = {
0xE5, 0xE3, 0x91, 0xE2, 0xE1, 0x91, 0xE6, 0xEF, 0xE3, 0x8D, 0xE1, 0xE5, 0xE3, 0x85, 0xE2, 0xE1,
0x83, 0xE3, 0xEE, 0xE2, 0xE1, 0x93, 0xE2, 0xE1, 0xEE, 0xE1, 0xE1, 0xE2, 0x93, 0xE5, 0xE4, 0xE1,
0xE2, 0x9D, 0xE1, 0x8E, 0xEE, 0x90, 0xEF, 0xEE, 0xE1, 0x91, 0xE4, 0xE3, 0xEF, 0xE2, 0xE3, 0x90,
0xE6, 0x95, 0xE5, 0xE2, 0x83, 0xE3, 0xE2, 0x9F, 0xE3, 0xE1, 0x90, 0xE6, 0xEE, 0xE6, 0xE4, 0xE3,
0x93, 0xE1, 0x9C, 0x9B, 0xE4, 0xE0, 0xEF, 0x99, 0xE1, 0x9B, 0x91, 0xE3, 0x81, 0xE4, 0xE0, 0xE0,
0x81, 0xEE, 0xE1, 0x93, 0xEE, 0x91, 0xE3, 0xEE, 0xE5, 0xE5, 0x83, 0x8E, 0xE2, 0xE3, 0xE1, 0x93,
0xE6, 0x9C, 0xE5, 0xE4, 0x82, 0x91, 0xE4, 0xE4, 0xE6, 0xE5, 0x99, 0x87, 0x9F, 0x93, 0x9D, 0xE1,
0xE3, 0xE2, 0xE3, 0x9B, 0xE2, 0xE1, 0x96, 0xE3, 0x87, 0xEF, 0x86, 0xE3, 0xE4, 0xE5, 0xE4, 0x90,
0xE6, 0xE2, 0xE4, 0x93, 0xE6, 0x81, 0xE1, 0x94, 0xE2, 0x93, 0xE6, 0x91, 0xE5, 0xE4, 0x90, 0xE2,
0xE6, 0x95, 0xEE, 0xE2, 0xE4, 0xE5, 0x90, 0xE6, 0xE2, 0x95, 0xE4, 0x96, 0xE6, 0x90, 0xE4, 0xE5,
0xE6, 0x91, 0x9C, 0xE1, 0xE2, 0xE0, 0xE0, 0xE4, 0xEF, 0x94, 0xE3, 0xE2, 0xE1, 0x93, 0xE6, 0x91,
0xE5, 0xE4, 0xE1, 0xE3, 0xE3, 0xE2, 0xE1, 0x85, 0x83, 0x95, 0xE6, 0xE5, 0xE5, 0x91, 0xE2, 0xE1,
0xEE, 0x91, 0xE3, 0x90, 0xE1, 0xE2, 0x9D, 0x99, 0xE4, 0xE6, 0xEE, 0xE3, 0xE6, 0x95, 0xE5, 0xE4,
0x91, 0xEE, 0xE3, 0xE3, 0x90, 0x91, 0xE2, 0xE1, 0xE6, 0xE1, 0xE4, 0x91, 0xEE, 0xE2, 0xE6, 0x94,
0xE2, 0x93, 0xE1, 0xE0, 0xE6, 0xE4, 0x9D, 0xE5, 0xE6, 0x8E, 0xE3, 0xE3, 0x90, 0xE2, 0xE1, 0x85,
0x9D, 0x91, 0xE3, 0xE1, 0xE2, 0xE6, 0xE3, 0x91, 0xE2, 0xE2, 0x92, 0xE3, 0xE2, 0x93, 0xE3, 0xE1,
0x9A, 0x80, 0xEF, 0xE3, 0x93, 0xE4, 0x92, 0xE3, 0xEE, 0xEE, 0x91, 0xE2, 0xE1, 0x85, 0x85, 0x96,
0xE2, 0xE1, 0xEE, 0xEF, 0xEF, 0x95, 0xE2, 0xE3, 0x93, 0x96, 0xE2, 0x9F, 0xE6, 0xE5, 0x9F, 0x9A,
0xE6, 0x94, 0xE5, 0xE4, 0x93, 0xE3, 0xE5, 0xEE, 0x81, 0xE3, 0xE1, 0xE3, 0xE1, 0x8F, 0xE5, 0x94,
0xE2, 0xE1, 0xE4, 0x80, 0xE1, 0x93, 0xE2, 0xE3, 0x92, 0xE2, 0xE1, 0x91, 0xE6, 0xE4, 0xE6, 0x94,
0xE4, 0xE5, 0x93, 0xE2, 0xE3, 0x91, 0xE1, 0xE2, 0x90, 0x99, 0xE4, 0xE5, 0xE3, 0xEE, 0x93, 0xE2,
0xE4, 0x8F, 0xE1, 0x84, 0x92, 0xEE, 0xE0, 0x91, 0x91, 0xE2, 0xE6, 0xE4, 0x99, 0x9C, 0x93, 0x9B,
0x9D, 0xEF, 0xE2, 0x9C, 0xE6, 0xE4, 0xEE, 0x85, 0x9B, 0xE2, 0x93, 0xE3, 0xE2, 0xE2, 0x83, 0x8E,
0x93, 0xEE, 0xE2, 0xE4, 0x90, 0x95, 0xE3, 0x84, 0x83, 0xE2, 0xE1, 0xE1, 0xE4, 0xEE, 0xEF, 0xE5,
0xE0, 0xE1, 0xE2, 0x84, 0x91, 0x9F, 0xE6, 0xE5, 0x9F, 0xE4, 0x90, 0xE2, 0xE6, 0xE4, 0x90, 0xE5,
0xE3, 0xE2, 0x90, 0x9A, 0x99, 0xEE, 0xEE, 0xE1, 0xE2, 0x90, 0x85, 0xE1, 0xE6, 0xE5, 0xE6, 0x95,
0xEE, 0xE2, 0x91, 0xE4, 0x9A, 0xE2, 0xE1, 0xE2, 0x91, 0xE4, 0xEE, 0x81, 0xE4, 0xE2, 0x93, 0xE4,
0xEE, 0x95, 0xE4, 0x91, 0x90, 0xE4, 0xE6, 0x92, 0xE4, 0xE2, 0x84, 0xE1, 0xE6, 0x91, 0xE5, 0x92,
0xE4, 0x92, 0xE3, 0x90, 0x9F, 0x87, 0x9A, 0xE6, 0xE5, 0xE5, 0xE1, 0xE4, 0xE6, 0xE1, 0xEE, 0x93,
0xE2, 0xE1, 0xE0, 0xE5, 0xE1, 0x91, 0xE5, 0xE4, 0x93, 0xE6, 0xE2, 0x92, 0xE3, 0xE1, 0xE7, 0x84,
0xE3, 0xE2, 0x91, 0x92, 0xE1, 0xE6, 0xE3, 0xE3, 0x91, 0xE2, 0xE1, 0x93, 0xE2, 0x85, 0xE3, 0xE1,
0x92, 0xE6, 0xE6, 0xEE, 0x93, 0xEF, 0xE1, 0xE1, 0xEF, 0x8D, 0xE3, 0xE1, 0x93, 0xE4, 0xE3, 0xE2,
0x91, 0xE4, 0xE6, 0x93, 0xEE, 0x84, 0x96, 0xE1, 0xE6, 0xE5, 0xE3, 0x8D, 0xE1, 0x94, 0x94, 0xE2,
0xE6, 0xE6, 0x93, 0xE5, 0xEE, 0xEE, 0xE6, 0x91, 0xE5, 0xE4, 0x93, 0xE2, 0x81, 0x9E, 0x98, 0xE6,
0xE5, 0x84, 0xE6, 0x91, 0xE4, 0x83, 0x92, 0x85, 0x86, 0x9B, 0xE2, 0x93, 0xEE, 0xE4, 0xEE, 0x94,
0xE1, 0xE5, 0xE5, 0xE4, 0x93, 0xE2, 0xE3, 0x84, 0xE1, 0xE4, 0xE6, 0x93, 0xE5, 0x84, 0xE4, 0xE4,
0x84, 0xE3, 0xE2, 0x84, 0x86, 0xE1, 0x93, 0x93, 0xE2, 0xE3, 0x84, 0xE4, 0x85, 0x90, 0xE3, 0xE2,
0xE1, 0x9A, 0xE6, 0x84, 0xE1, 0x84, 0xE2, 0xEE, 0xE2, 0xE3, 0x9D, 0x9F, 0xE1, 0xE6, 0xEF, 0x91,
0xE3, 0xE1, 0x93, 0xE2, 0x85, 0xEE, 0x92, 0xE3, 0xE2, 0xE1, 0x90, 0xE2, 0x91, 0xE1, 0x91, 0xE6,
0xEE, 0x90, 0xE1, 0x91, 0xE4, 0xE5, 0xE1, 0x91, 0xE3, 0xE4, 0x93, 0xE2, 0x91, 0xE6, 0xE4, 0x93,
0xE1, 0xE4, 0x96, 0xEE, 0x9D, 0x9F, 0x90, 0x9A, 0x9C, 0x9A, 0x93, 0xE1, 0xE2, 0xE4, 0xEF, 0xE3,
0xE1, 0xE2, 0xE6, 0xE5, 0xE0, 0xE0, 0xEE, 0xE4, 0xE6, 0x96, 0xE2, 0x8D, 0xE1, 0xE4, 0xE6, 0xEE,
0x9D, 0x9F, 0x94, 0xE2, 0xE1, 0xE6, 0xE4, 0x8D, 0xE2, 0xE4, 0x91, 0xE6, 0xE1, 0x84, 0xE3, 0xE4,
0xE2, 0xE5, 0xE6, 0x95, 0xE4, 0x91, 0xE5, 0xE4, 0xEE, 0x91, 0xE2, 0x93, 0xE4, 0xE6, 0xE4, 0x91,
0xE6, 0xE6, 0x93, 0xE4, 0xEF, 0x91, 0xE3, 0x93, 0xE1, 0xE5, 0x80, 0x85, 0x95, 0xE2, 0xE1, 0xE5,
0xE2, 0x84, 0xE3, 0xE6, 0x94, 0xE5, 0xE4, 0x86, 0xE6, 0xE2, 0x84, 0xE1, 0x93, 0x93, 0x84, 0x91,
0xE1, 0xE2, 0x91, 0x93, 0x90, 0x93, 0x84, 0xE1, 0xE5, 0xE4, 0x84, 0xE3, 0x86, 0xE1, 0xE6, 0x96,
0x90, 0xE5, 0xE4, 0xE6, 0x85, 0xE6, 0x96, 0xE3, 0xE2, 0xE1, 0xE5, 0xE3, 0x93, 0xE2, 0xE1, 0x96,
0xE5, 0x84, 0xE3, 0xE1, 0x96, 0xE2, 0xE5, 0xE6, 0x93, 0xE2, 0xE1, 0x92, 0x90, 0x91, 0x93, 0xE2,
0xE3, 0xE1, 0xEE, 0x93, 0xE3, 0x84, 0xE1, 0xE5, 0xEE, 0x9F, 0x90, 0x9D, 0xE4, 0xE6, 0xE4, 0xE6,
0xE5, 0xE4, 0x93, 0x86, 0xEE, 0x8E, 0x83, 0x8E, 0xE5, 0xE5, 0xE2, 0xE2, 0xE3, 0x9D, 0x9C, 0x9B,
0xEE, 0xE0, 0x8E, 0x92, 0x83, 0x85, 0xE3, 0xE3, 0x92, 0x8D, 0x96, 0xE6, 0xE1, 0xE3, 0xE3, 0xE2,
0x96, 0x92, 0xE1, 0xE3, 0xE2, 0xE2, 0xE0, 0x9A, 0xE3, 0xE3, 0x9C, 0x9B, 0x9D, 0xE4, 0xE3, 0xE3,
0xE2, 0xE6, 0xE4, 0x9C, 0xE1, 0xE4, 0xE6, 0xE5, 0x92, 0x84, 0xEE, 0xE0, 0xE2, 0xE2, 0x9C, 0x9D,
0xE6, 0xE6, 0xE4, 0xE5, 0xE1, 0x93, 0xE2, 0xE3, 0x9C, 0x9D, 0xEE, 0xEF, 0xE2, 0xE3, 0x92, 0x85,
0x83, 0xEE, 0x92, 0x85, 0x8E, 0x95, 0xE1, 0xE5, 0x93, 0x84, 0x94, 0xE6, 0xE5, 0xE4, 0xE2, 0x91,
0xE3, 0x93, 0xE1, 0xE4
};
const uint8_t Game::_protectionCodeDataAmiga[] = {
0xE2, 0x93, 0xE3, 0xE2, 0x92, 0xE1, 0xE3, 0xE2, 0x84, 0xE1, 0x92, 0x85, 0xEF, 0xE0, 0x85, 0xEE,
0xE2, 0xE3, 0xE0, 0xE0, 0xEF, 0x91, 0xEE, 0xE2, 0xE0, 0x93, 0xE6, 0x9D, 0x93, 0x9F, 0xEE, 0xE3,
0x9F, 0xE2, 0xE6, 0xE5, 0x9F, 0x9B, 0xE5, 0xE3, 0xE1, 0x92, 0xE2, 0xE3, 0x99, 0xE1, 0x90, 0xE3,
0xE2, 0x90, 0xE0, 0xE1, 0x99, 0xE3, 0xE0, 0xEF, 0xE1, 0xE6, 0x90, 0x9A, 0xE2, 0x91, 0xE4, 0x9D,
0x91, 0xE6, 0x90, 0xE2, 0xE3, 0x9F, 0xE5, 0xE5, 0xE6, 0x95, 0xE4, 0xE1, 0x91, 0xE2, 0xE1, 0x9D,
0xE2, 0xE1, 0xE6, 0xE6, 0xE4, 0x95, 0xE6, 0xE0, 0xEF, 0x96, 0x9F, 0x93, 0x9C, 0xE1, 0xE2, 0x93,
0xE6, 0xE2, 0x9C, 0xE1, 0x82, 0xE6, 0xE0, 0xEF, 0x8F, 0xE6, 0xE2, 0x94, 0x9F, 0xE2, 0x91, 0xE5,
0x9C, 0x91, 0xEF, 0xEE, 0x96, 0x93, 0xE0, 0x95, 0xE6, 0xE6, 0x95, 0xE5, 0xE3, 0x90, 0xE1, 0x9B,
0xE2, 0x93, 0xE3, 0xE1, 0xE0, 0xE2, 0x90, 0xE3, 0x91, 0x93, 0xE2, 0x95, 0xE6, 0xEE, 0xE2, 0xE4,
0xE2, 0x90, 0xE1, 0xE6, 0x91, 0xE4, 0xE2, 0xE2, 0x99, 0xE1, 0xE5, 0xE4, 0xE2, 0xE6, 0x95, 0xEE,
0xE2, 0x93, 0x9B, 0x9C, 0xE1, 0x90, 0xE2, 0x95, 0x8D, 0x85, 0x86, 0x95, 0xE5, 0xE5, 0xEF, 0xEF,
0xE3, 0x95, 0xE1, 0xE6, 0xE3, 0xE5, 0x94, 0x99, 0x9C, 0x9B, 0xE6, 0x91, 0xE2, 0x9F, 0x94, 0xEE,
0xE1, 0xE2, 0xE1, 0x91, 0xE6, 0xE6, 0xE5, 0xE1, 0x94, 0xE4, 0x81, 0x95, 0x95, 0x94, 0x99, 0xE1,
0xE2, 0xE4, 0xE2, 0x90, 0x95, 0xE3, 0xE1, 0x90, 0xE3, 0xEE, 0xE2, 0xE3, 0xE4, 0xE6, 0xE2, 0x90,
0xE6, 0xE4, 0x85, 0xE5, 0xE6, 0xE5, 0x90, 0xE4, 0xEE, 0xEE, 0xE6, 0x90, 0xE5, 0xE4, 0x85, 0x92,
0xE5, 0xE6, 0x91, 0xE4, 0x92, 0xE2, 0xE2, 0x90, 0xE6, 0x99, 0x99, 0x92, 0x84, 0xE5, 0xE6, 0xE4,
0x84, 0x92, 0xEE, 0xEF, 0xEE, 0x91, 0xE2, 0xE6, 0x9A, 0x8F, 0xE6, 0xE2, 0x84, 0xE5, 0xE6, 0xE6,
0xE5, 0xE3, 0xE4, 0xE5, 0xE6, 0xE4, 0xE3, 0xE5, 0x85, 0x91, 0xE6, 0xE3, 0x81, 0xE2, 0x91, 0xE3,
0xE4, 0x91, 0xE3, 0xE2, 0xE1, 0x92, 0xE0, 0xE3, 0x91, 0xE1, 0xE2, 0xE6, 0xE6, 0x9C, 0x90, 0xE1,
0xE2, 0xE0, 0xE6, 0x91, 0xE5, 0xE4, 0x92, 0xE1, 0x87, 0x9B, 0xE2, 0x93, 0xE1, 0xE3, 0x94, 0x85,
0xE2, 0x93, 0xE1, 0xE5, 0xE1, 0xE4, 0x85, 0xEE, 0xEF, 0xE0, 0xE6, 0xE6, 0xE2, 0x90, 0xEF, 0xE2,
0x9F, 0x91, 0x91, 0xE3, 0xE2, 0xE4, 0xE6, 0xE2, 0x95, 0xE1, 0x90, 0x9B, 0xE6, 0x9F, 0x9B, 0x9A,
0x91, 0xEE, 0xE5, 0xE4, 0x99, 0xE1, 0x9F, 0xE2, 0x9C, 0x9C, 0x95, 0x91, 0xE6, 0xE5, 0xE3, 0x91,
0xE2, 0x92, 0xE1, 0x94, 0xE6, 0x93, 0xE5, 0xEE, 0x92, 0x9D, 0x9B, 0x9A, 0x9F, 0xEE, 0xE2, 0x91,
0xE6, 0x95, 0xE5, 0xE4, 0xEE, 0xE2, 0xE3, 0xE3, 0xEE, 0xE1, 0x90, 0xE5, 0xE3, 0x9F, 0xE2, 0x83,
0xE1, 0xE2, 0x9B, 0x99, 0xE1, 0xE4, 0xE5, 0xE6, 0xEE, 0x9F, 0xE3, 0xE1, 0x83, 0xE4, 0xE2, 0x90,
0x91, 0xE3, 0xE1, 0xE6, 0xE0, 0xE4, 0x9F, 0xE6, 0xE4, 0xE5, 0xEE, 0x81, 0xE6, 0xE5, 0xE4, 0x93,
0xE2, 0x81, 0xE4, 0xEE, 0x84, 0xE3, 0xEE, 0xE3, 0xE2, 0x90, 0xE4, 0xE6, 0xEE, 0x95, 0xE2, 0xE1,
0x91, 0xE5, 0xE6, 0xE2, 0x8D, 0xEE, 0xE1, 0x93, 0xE6, 0x91, 0xE2, 0x92, 0xE1, 0xE0, 0xE0, 0x91,
0xE3, 0xE2, 0x92, 0xE5, 0xE1, 0x90, 0x85, 0xE4, 0xE5, 0xE6, 0xE3, 0x93, 0xE4, 0xE4, 0xEE, 0xE2,
0xE5, 0xE4, 0x84, 0xE3, 0xE6, 0xE5, 0xE6, 0xE5, 0x90, 0xE4, 0xE5, 0x91, 0xEE, 0xEE, 0x94, 0xE1,
0xE4, 0x93, 0xE6, 0xE2, 0x84, 0x8F, 0x80, 0xEE, 0xE0, 0xE0, 0x95, 0xEE, 0x85, 0xE4, 0xE5, 0xE3,
0x91, 0x93, 0x93, 0xE6, 0xE1, 0x94, 0xE3, 0x93, 0x84, 0xE1, 0x9B, 0x91, 0x9A, 0xEE, 0xE2, 0xE1,
0xE1, 0x81, 0xE2, 0x91, 0xE1, 0xE4, 0xEE, 0xEE, 0x95, 0x90, 0xE3, 0xE5, 0xEE, 0xE0, 0x83, 0xE3,
0xE1, 0x91, 0xE4, 0x90, 0x9F, 0xE2, 0x95, 0xEE, 0xE1, 0x95, 0xE3, 0x84, 0xE1, 0x83, 0xE6, 0xE6,
0xE5, 0xE1, 0xEF, 0xE5, 0x95, 0x91, 0xE3, 0x93, 0xE1, 0xE6, 0xE1, 0x91, 0xE2, 0xEE, 0xE2, 0xE3,
0xEE, 0x91, 0xE3, 0xE2, 0x93, 0xE4, 0xE2, 0x93, 0xE3, 0xE2, 0x84, 0x9B, 0xEE, 0x91, 0xE3, 0x93,
0xE2, 0x84, 0x92, 0x8D, 0xEF, 0xE2, 0xE1, 0xE6, 0xE2, 0x83, 0xE3, 0xE5, 0x90, 0xE4, 0xE3, 0x90,
0xEE, 0xEE, 0x90, 0xE3, 0xE6, 0xE6, 0x90, 0xE4, 0x91, 0xE3, 0xEE, 0xEE, 0x99, 0xE6, 0xE5, 0x90,
0xE1, 0x84, 0xE5, 0xE4, 0xE3, 0xE2, 0xEE, 0xEE, 0xE1, 0x91, 0xE2, 0xE1, 0x9D, 0x9C, 0x9B, 0xE2,
0xE1, 0xE5, 0x83, 0x93, 0xE5, 0x83, 0x93, 0xE4, 0xE3, 0x91, 0xE1, 0x93, 0xE5, 0x99, 0x9C, 0x9C,
0xEE, 0xE2, 0xE1, 0xE4, 0xEE, 0xE1, 0xE1, 0x91, 0x92, 0xE4, 0xE3, 0x91, 0xE2, 0x92, 0xE1, 0xE1,
0xE1, 0x93, 0xE2, 0xE3, 0xE5, 0xE6, 0xEE, 0x91, 0x93, 0xE3, 0xE1, 0xE5, 0xE6, 0x91, 0xE2, 0x93,
0xE1, 0xE5, 0x85, 0x83, 0xEE, 0xE1, 0xE2, 0x91, 0xE1, 0x91, 0xE1, 0x91, 0xE6, 0xE5, 0xEE, 0x91,
0xEF, 0xE1, 0x93, 0xE4, 0xE2, 0x91, 0x91, 0x96, 0x84, 0xE2, 0x87, 0x9A, 0x84, 0xEE, 0xE1, 0xE4,
0xE1, 0x84, 0x84, 0xE3, 0xE2, 0x93, 0xE4, 0xE6, 0x93, 0xE5, 0xE4, 0x84, 0x9D, 0x91, 0xE1, 0xE6,
0xE5, 0xE4, 0xEE, 0x93, 0xE3, 0x84, 0xE1, 0xE5, 0xE1, 0x93, 0xE2, 0x84, 0xE1, 0x99, 0x92, 0xE1,
0x83, 0xEF, 0x84, 0xE2, 0x92, 0xE2, 0xE3, 0xEF, 0xE1, 0x93, 0x91, 0xE6, 0xE2, 0xE5, 0x92, 0xE1,
0x96, 0xE3, 0xE2, 0xE1, 0x84, 0x92, 0xE3, 0xE2, 0x8D, 0xE1, 0xEF, 0xE0, 0xE6, 0x93, 0xE1, 0x86,
0x9D, 0xE5, 0xE4, 0x91, 0xE6, 0xEE, 0xE1, 0xE1, 0xEE, 0x91, 0xE3, 0x93, 0xE3, 0xE3, 0xE6, 0x90,
0x91, 0xE1, 0x91, 0xE5, 0xE1, 0x9F, 0xE2, 0xE1, 0xE1, 0x90, 0xE5, 0xE6, 0x9A, 0x9C, 0x9D, 0x90,
0xE1, 0xE3, 0x90, 0x90, 0x9F, 0x99, 0xE1, 0xE3, 0x9C, 0x9D, 0x9F, 0x9B, 0xE6, 0xE4, 0xE4, 0xE3,
0xE2, 0xE1, 0xE3, 0xE6, 0xE5, 0xE3, 0xE0, 0x9C, 0x96, 0x85, 0x93, 0xE2, 0xE3, 0x81, 0xE0, 0xEF,
0xE2, 0x82, 0x83, 0xE5, 0xE4, 0xE3, 0x9A, 0x9D, 0x98, 0x87, 0xE6, 0xE3, 0xE2, 0xE1, 0x95, 0x81,
0xE1, 0xE3, 0x87, 0x82, 0x99, 0xE2, 0xE4, 0xE4, 0x9F, 0x90, 0x91, 0x99, 0xE2, 0xE3, 0x9C, 0x81,
0x94, 0xE4, 0x9F, 0x90, 0xE2, 0xE3, 0x93, 0xE6, 0xEE, 0xEF, 0x90, 0xE1, 0x91, 0xE6, 0xE2, 0x90,
0xE3, 0x91, 0xE1, 0x8E
};
const uint8_t Game::_protectionPal[] = { const uint8_t Game::_protectionPal[] = {
// DOS // DOS
0x00, 0x00, 0x00, 0x42, 0x00, 0x63, 0x00, 0x00, 0x0F, 0xFF, 0x0F, 0xF0, 0x07, 0x77, 0x00, 0x00, 0x00, 0x00, 0x00, 0x42, 0x00, 0x63, 0x00, 0x00, 0x0F, 0xFF, 0x0F, 0xF0, 0x07, 0x77, 0x00, 0x00,
@ -3223,7 +3910,7 @@ const uint8_t Game::_protectionPal[] = {
0x08, 0x88, 0x09, 0x99, 0x0A, 0xAA, 0x0B, 0xBB, 0x0C, 0xCC, 0x0D, 0xDD, 0x0E, 0xEE, 0x0F, 0xFF 0x08, 0x88, 0x09, 0x99, 0x0A, 0xAA, 0x0B, 0xBB, 0x0C, 0xCC, 0x0D, 0xDD, 0x0E, 0xEE, 0x0F, 0xFF
}; };
const char *Menu::_levelNames[] { const char *Menu::_levelNames[] = {
"Titan / The Jungle", "Titan / The Jungle",
"Titan / New Washington", "Titan / New Washington",
"Titan / Death Tower Show", "Titan / Death Tower Show",
@ -4145,7 +4832,7 @@ const uint8_t SfxPlayer::_musicDataSample1[] = {
0x2A, 0x26, 0x21, 0x1C, 0x16, 0x10, 0x0A, 0x04, 0xFE, 0xF8, 0xF2, 0xEC, 0xE6, 0xE0, 0xDB, 0xD6, 0x2A, 0x26, 0x21, 0x1C, 0x16, 0x10, 0x0A, 0x04, 0xFE, 0xF8, 0xF2, 0xEC, 0xE6, 0xE0, 0xDB, 0xD6,
0xD1, 0xCC, 0xC8, 0xC4, 0xC0, 0xBD, 0xB9, 0xB6, 0xB3, 0xB1, 0xAE, 0xAB, 0xA8, 0xA5, 0xA3, 0xA0, 0xD1, 0xCC, 0xC8, 0xC4, 0xC0, 0xBD, 0xB9, 0xB6, 0xB3, 0xB1, 0xAE, 0xAB, 0xA8, 0xA5, 0xA3, 0xA0,
0x9D, 0x9A, 0x98, 0x95, 0x92, 0x8F, 0x8D, 0x8A, 0x88, 0x86, 0x84, 0x83, 0x82, 0x82, 0x82, 0x82, 0x9D, 0x9A, 0x98, 0x95, 0x92, 0x8F, 0x8D, 0x8A, 0x88, 0x86, 0x84, 0x83, 0x82, 0x82, 0x82, 0x82,
0x82, 0x83, 0x84, 0x86, 0x86 0x82, 0x83, 0x84, 0x86
}; };
const uint8_t SfxPlayer::_musicDataSample2[] = { const uint8_t SfxPlayer::_musicDataSample2[] = {
@ -4366,7 +5053,7 @@ const uint8_t SfxPlayer::_musicDataSample2[] = {
0x0E, 0x16, 0x14, 0x16, 0x10, 0x0E, 0x12, 0x16, 0x16, 0x14, 0x18, 0x10, 0x10, 0x10, 0x12, 0x10, 0x0E, 0x16, 0x14, 0x16, 0x10, 0x0E, 0x12, 0x16, 0x16, 0x14, 0x18, 0x10, 0x10, 0x10, 0x12, 0x10,
0x10, 0x08, 0x08, 0x06, 0x08, 0x08, 0x06, 0x0C, 0x08, 0x08, 0x06, 0x08, 0x08, 0x0A, 0x08, 0x0A, 0x10, 0x08, 0x08, 0x06, 0x08, 0x08, 0x06, 0x0C, 0x08, 0x08, 0x06, 0x08, 0x08, 0x0A, 0x08, 0x0A,
0x0C, 0x06, 0x04, 0x04, 0x06, 0x04, 0x04, 0x06, 0x04, 0x02, 0x06, 0x06, 0x02, 0x04, 0x04, 0x06, 0x0C, 0x06, 0x04, 0x04, 0x06, 0x04, 0x04, 0x06, 0x04, 0x02, 0x06, 0x06, 0x02, 0x04, 0x04, 0x06,
0x08, 0x06, 0x04, 0x04, 0x06, 0x0C, 0x0A, 0x02, 0x02 0x08, 0x06, 0x04, 0x04, 0x06, 0x0C, 0x0A, 0x02
}; };
const uint8_t SfxPlayer::_musicDataSample3[] = { const uint8_t SfxPlayer::_musicDataSample3[] = {
@ -4651,7 +5338,7 @@ const uint8_t SfxPlayer::_musicDataSample3[] = {
0x0E, 0x09, 0xFF, 0xFB, 0x01, 0x10, 0x1C, 0x1A, 0x04, 0xEB, 0xE8, 0xFB, 0x0C, 0x0E, 0xF8, 0xE0, 0x0E, 0x09, 0xFF, 0xFB, 0x01, 0x10, 0x1C, 0x1A, 0x04, 0xEB, 0xE8, 0xFB, 0x0C, 0x0E, 0xF8, 0xE0,
0xD8, 0xE3, 0xF5, 0x05, 0x11, 0x14, 0x0E, 0xFF, 0xF3, 0xE9, 0xF0, 0xFE, 0x03, 0xF3, 0xE3, 0xE1, 0xD8, 0xE3, 0xF5, 0x05, 0x11, 0x14, 0x0E, 0xFF, 0xF3, 0xE9, 0xF0, 0xFE, 0x03, 0xF3, 0xE3, 0xE1,
0xF0, 0x03, 0x0E, 0x09, 0xFD, 0xF6, 0xF1, 0xED, 0xEA, 0xE7, 0xE9, 0xF6, 0xFD, 0xFD, 0xFF, 0x0B, 0xF0, 0x03, 0x0E, 0x09, 0xFD, 0xF6, 0xF1, 0xED, 0xEA, 0xE7, 0xE9, 0xF6, 0xFD, 0xFD, 0xFF, 0x0B,
0x16, 0x18, 0x11, 0x08, 0x08 0x16, 0x18, 0x11, 0x08
}; };
const uint8_t SfxPlayer::_musicDataSample4[] = { const uint8_t SfxPlayer::_musicDataSample4[] = {
@ -4686,8 +5373,7 @@ const uint8_t SfxPlayer::_musicDataSample4[] = {
0x07, 0x0F, 0x0D, 0x07, 0x0F, 0x19, 0x11, 0x0F, 0x10, 0x0D, 0x09, 0x0B, 0x13, 0x14, 0x10, 0x0E, 0x07, 0x0F, 0x0D, 0x07, 0x0F, 0x19, 0x11, 0x0F, 0x10, 0x0D, 0x09, 0x0B, 0x13, 0x14, 0x10, 0x0E,
0x11, 0x0A, 0x07, 0x0B, 0x17, 0x0F, 0x0D, 0x17, 0x14, 0x0B, 0x10, 0x11, 0x06, 0x06, 0x10, 0x13, 0x11, 0x0A, 0x07, 0x0B, 0x17, 0x0F, 0x0D, 0x17, 0x14, 0x0B, 0x10, 0x11, 0x06, 0x06, 0x10, 0x13,
0x0F, 0x10, 0x13, 0x0E, 0x07, 0x0E, 0x13, 0x0E, 0x0D, 0x13, 0x0F, 0x0E, 0x15, 0x11, 0x0B, 0x0D, 0x0F, 0x10, 0x13, 0x0E, 0x07, 0x0E, 0x13, 0x0E, 0x0D, 0x13, 0x0F, 0x0E, 0x15, 0x11, 0x0B, 0x0D,
0x0F, 0x0A, 0x0E, 0x14, 0x14, 0x0C, 0x0F, 0x0B, 0x0B, 0x11, 0x13, 0x10, 0x0C, 0x0B, 0x0F, 0x10, 0x0F, 0x0A, 0x0E, 0x14, 0x14, 0x0C, 0x0F, 0x0B, 0x0B, 0x11, 0x13, 0x10, 0x0C, 0x0B, 0x0F, 0x10
0x10
}; };
const uint8_t SfxPlayer::_musicDataSample5[] = { const uint8_t SfxPlayer::_musicDataSample5[] = {
@ -4849,7 +5535,7 @@ const uint8_t SfxPlayer::_musicDataSample5[] = {
0x04, 0x18, 0x18, 0x08, 0x04, 0x18, 0x24, 0x14, 0x04, 0xF8, 0xE4, 0x00, 0x00, 0x04, 0x08, 0xFC, 0x04, 0x18, 0x18, 0x08, 0x04, 0x18, 0x24, 0x14, 0x04, 0xF8, 0xE4, 0x00, 0x00, 0x04, 0x08, 0xFC,
0xF4, 0xFC, 0xF0, 0xF0, 0x00, 0x08, 0xF4, 0xE4, 0xF4, 0xEC, 0xEC, 0xEC, 0xE8, 0xF4, 0xE8, 0xD8, 0xF4, 0xFC, 0xF0, 0xF0, 0x00, 0x08, 0xF4, 0xE4, 0xF4, 0xEC, 0xEC, 0xEC, 0xE8, 0xF4, 0xE8, 0xD8,
0xDC, 0xE4, 0xF0, 0xEC, 0xF0, 0x04, 0xF4, 0xE8, 0xFC, 0xFC, 0xE4, 0xE8, 0xF4, 0xF4, 0x04, 0xEC, 0xDC, 0xE4, 0xF0, 0xEC, 0xF0, 0x04, 0xF4, 0xE8, 0xFC, 0xFC, 0xE4, 0xE8, 0xF4, 0xF4, 0x04, 0xEC,
0xF0, 0xE4, 0xE8, 0x10, 0x0C, 0xF4, 0xEC, 0x00, 0x14, 0x0C, 0x0C, 0x04, 0x04 0xF0, 0xE4, 0xE8, 0x10, 0x0C, 0xF4, 0xEC, 0x00, 0x14, 0x0C, 0x0C, 0x04
}; };
const uint8_t SfxPlayer::_musicDataSample6[] = { const uint8_t SfxPlayer::_musicDataSample6[] = {
@ -5000,7 +5686,7 @@ const uint8_t SfxPlayer::_musicDataSample6[] = {
0xEB, 0xEF, 0xF7, 0xF8, 0xFF, 0xFA, 0xEA, 0xD8, 0xD0, 0xCD, 0xD4, 0xE0, 0xEC, 0xF7, 0x0C, 0x15, 0xEB, 0xEF, 0xF7, 0xF8, 0xFF, 0xFA, 0xEA, 0xD8, 0xD0, 0xCD, 0xD4, 0xE0, 0xEC, 0xF7, 0x0C, 0x15,
0x12, 0x19, 0x1D, 0x26, 0x30, 0x3E, 0x47, 0x49, 0x44, 0x32, 0x1E, 0x0F, 0xFE, 0xF0, 0xE8, 0xE7, 0x12, 0x19, 0x1D, 0x26, 0x30, 0x3E, 0x47, 0x49, 0x44, 0x32, 0x1E, 0x0F, 0xFE, 0xF0, 0xE8, 0xE7,
0xEA, 0xF2, 0xF5, 0xFA, 0xFF, 0xF8, 0xE6, 0xD6, 0xCF, 0xCD, 0xD5, 0xE3, 0xEB, 0xFB, 0x0F, 0x12, 0xEA, 0xF2, 0xF5, 0xFA, 0xFF, 0xF8, 0xE6, 0xD6, 0xCF, 0xCD, 0xD5, 0xE3, 0xEB, 0xFB, 0x0F, 0x12,
0x15, 0x19, 0x1F, 0x27, 0x34, 0x3F, 0x3F 0x15, 0x19, 0x1F, 0x27, 0x34, 0x3F
}; };
const uint8_t SfxPlayer::_musicDataSample7[] = { const uint8_t SfxPlayer::_musicDataSample7[] = {
@ -5056,11 +5742,11 @@ const uint8_t SfxPlayer::_musicDataSample7[] = {
0x0E, 0x0F, 0x0A, 0x11, 0x0B, 0x0E, 0x0C, 0x10, 0x0E, 0x08, 0x14, 0x0B, 0x0E, 0x14, 0x04, 0x16, 0x0E, 0x0F, 0x0A, 0x11, 0x0B, 0x0E, 0x0C, 0x10, 0x0E, 0x08, 0x14, 0x0B, 0x0E, 0x14, 0x04, 0x16,
0x0A, 0x10, 0x0E, 0x0C, 0x0F, 0x02, 0x1B, 0x07, 0x10, 0x13, 0x08, 0x14, 0x07, 0x12, 0x07, 0x10, 0x0A, 0x10, 0x0E, 0x0C, 0x0F, 0x02, 0x1B, 0x07, 0x10, 0x13, 0x08, 0x14, 0x07, 0x12, 0x07, 0x10,
0x0F, 0x08, 0x14, 0x0B, 0x0E, 0x0F, 0x0E, 0x10, 0x0B, 0x11, 0x10, 0x0E, 0x0D, 0x08, 0x14, 0x0B, 0x0F, 0x08, 0x14, 0x0B, 0x0E, 0x0F, 0x0E, 0x10, 0x0B, 0x11, 0x10, 0x0E, 0x0D, 0x08, 0x14, 0x0B,
0x0E, 0x0F, 0x10, 0x0D, 0x0C, 0x13, 0x0C, 0x10, 0x0F, 0x0C, 0x0E, 0x0E, 0x0F, 0x00, 0x00 0x0E, 0x0F, 0x10, 0x0D, 0x0C, 0x13, 0x0C, 0x10, 0x0F, 0x0C, 0x0E, 0x0E, 0x0F, 0x00
}; };
const uint8_t SfxPlayer::_musicDataSample8[] = { const uint8_t SfxPlayer::_musicDataSample8[] = {
0x00, 0x02, 0x00, 0x40, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00 0x00, 0x02, 0x00, 0x40, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00
}; };
const SfxPlayer::Module SfxPlayer::_module68 = { const SfxPlayer::Module SfxPlayer::_module68 = {

View File

@ -1,7 +1,7 @@
/* /*
* REminiscence - Flashback interpreter * REminiscence - Flashback interpreter
* Copyright (C) 2005-2018 Gregory Montoir (cyx@users.sourceforge.net) * Copyright (C) 2005-2019 Gregory Montoir (cyx@users.sourceforge.net)
*/ */
#ifndef SYSTEMSTUB_H__ #ifndef SYSTEMSTUB_H__
@ -55,20 +55,24 @@ struct SystemStub {
virtual ~SystemStub() {} virtual ~SystemStub() {}
virtual void init(const char *title, int w, int h, bool fullscreen, bool widescreen, ScalerParameters *scalerParameters) = 0; virtual void init(const char *title, int w, int h, bool fullscreen, int widescreenMode, ScalerParameters *scalerParameters) = 0;
virtual void destroy() = 0; virtual void destroy() = 0;
virtual bool hasWidescreen() const = 0; virtual bool hasWidescreen() const = 0;
virtual void setScreenSize(int w, int h) = 0; virtual void setScreenSize(int w, int h) = 0;
virtual void setPalette(const uint8_t *pal, int n) = 0; virtual void setPalette(const uint8_t *pal, int n) = 0;
virtual void getPalette(uint8_t *pal, int n) = 0;
virtual void setPaletteEntry(int i, const Color *c) = 0; virtual void setPaletteEntry(int i, const Color *c) = 0;
virtual void getPaletteEntry(int i, Color *c) = 0; virtual void getPaletteEntry(int i, Color *c) = 0;
virtual void setOverscanColor(int i) = 0; virtual void setOverscanColor(int i) = 0;
virtual void copyRect(int x, int y, int w, int h, const uint8_t *buf, int pitch) = 0; virtual void copyRect(int x, int y, int w, int h, const uint8_t *buf, int pitch) = 0;
virtual void copyRectRgb24(int x, int y, int w, int h, const uint8_t *rgb) = 0; virtual void copyRectRgb24(int x, int y, int w, int h, const uint8_t *rgb) = 0;
virtual void copyRectLeftBorder(int w, int h, const uint8_t *buf) = 0; virtual void copyWidescreenLeft(int w, int h, const uint8_t *buf) = 0;
virtual void copyRectRightBorder(int w, int h, const uint8_t *buf) = 0; virtual void copyWidescreenRight(int w, int h, const uint8_t *buf) = 0;
virtual void copyRectMirrorBorders(int w, int h, const uint8_t *buf) = 0; virtual void copyWidescreenMirror(int w, int h, const uint8_t *buf) = 0;
virtual void copyWidescreenBlur(int w, int h, const uint8_t *buf) = 0;
virtual void clearWidescreen() = 0;
virtual void enableWidescreen(bool enable) = 0;
virtual void fadeScreen() = 0; virtual void fadeScreen() = 0;
virtual void updateScreen(int shakeOffset) = 0; virtual void updateScreen(int shakeOffset) = 0;

View File

@ -1,7 +1,7 @@
/* /*
* REminiscence - Flashback interpreter * REminiscence - Flashback interpreter
* Copyright (C) 2005-2018 Gregory Montoir (cyx@users.sourceforge.net) * Copyright (C) 2005-2019 Gregory Montoir (cyx@users.sourceforge.net)
*/ */
#include <SDL.h> #include <SDL.h>
@ -49,24 +49,29 @@ struct SystemStub_SDL : SystemStub {
ScalerType _scalerType; ScalerType _scalerType;
const Scaler *_scaler; const Scaler *_scaler;
int _scaleFactor; int _scaleFactor;
bool _widescreen; int _widescreenMode;
SDL_Texture *_wideTexture; SDL_Texture *_widescreenTexture;
int _wideMargin; int _wideMargin;
bool _enableWidescreen;
virtual ~SystemStub_SDL() {} virtual ~SystemStub_SDL() {}
virtual void init(const char *title, int w, int h, bool fullscreen, bool widescreen, ScalerParameters *scalerParameters); virtual void init(const char *title, int w, int h, bool fullscreen, int widescreenMode, ScalerParameters *scalerParameters);
virtual void destroy(); virtual void destroy();
virtual bool hasWidescreen() const; virtual bool hasWidescreen() const;
virtual void setScreenSize(int w, int h); virtual void setScreenSize(int w, int h);
virtual void setPalette(const uint8_t *pal, int n); virtual void setPalette(const uint8_t *pal, int n);
virtual void getPalette(uint8_t *pal, int n);
virtual void setPaletteEntry(int i, const Color *c); virtual void setPaletteEntry(int i, const Color *c);
virtual void getPaletteEntry(int i, Color *c); virtual void getPaletteEntry(int i, Color *c);
virtual void setOverscanColor(int i); virtual void setOverscanColor(int i);
virtual void copyRect(int x, int y, int w, int h, const uint8_t *buf, int pitch); virtual void copyRect(int x, int y, int w, int h, const uint8_t *buf, int pitch);
virtual void copyRectRgb24(int x, int y, int w, int h, const uint8_t *rgb); virtual void copyRectRgb24(int x, int y, int w, int h, const uint8_t *rgb);
virtual void copyRectLeftBorder(int w, int h, const uint8_t *buf); virtual void copyWidescreenLeft(int w, int h, const uint8_t *buf);
virtual void copyRectRightBorder(int w, int h, const uint8_t *buf); virtual void copyWidescreenRight(int w, int h, const uint8_t *buf);
virtual void copyRectMirrorBorders(int w, int h, const uint8_t *buf); virtual void copyWidescreenMirror(int w, int h, const uint8_t *buf);
virtual void copyWidescreenBlur(int w, int h, const uint8_t *buf);
virtual void clearWidescreen();
virtual void enableWidescreen(bool enable);
virtual void fadeScreen(); virtual void fadeScreen();
virtual void updateScreen(int shakeOffset); virtual void updateScreen(int shakeOffset);
virtual void processEvents(); virtual void processEvents();
@ -91,7 +96,7 @@ SystemStub *SystemStub_SDL_create() {
return new SystemStub_SDL(); return new SystemStub_SDL();
} }
void SystemStub_SDL::init(const char *title, int w, int h, bool fullscreen, bool widescreen, ScalerParameters *scalerParameters) { void SystemStub_SDL::init(const char *title, int w, int h, bool fullscreen, int widescreenMode, ScalerParameters *scalerParameters) {
SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_JOYSTICK); SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_JOYSTICK);
SDL_ShowCursor(SDL_DISABLE); SDL_ShowCursor(SDL_DISABLE);
_caption = title; _caption = title;
@ -109,9 +114,10 @@ void SystemStub_SDL::init(const char *title, int w, int h, bool fullscreen, bool
memset(_rgbPalette, 0, sizeof(_rgbPalette)); memset(_rgbPalette, 0, sizeof(_rgbPalette));
memset(_darkPalette, 0, sizeof(_darkPalette)); memset(_darkPalette, 0, sizeof(_darkPalette));
_screenW = _screenH = 0; _screenW = _screenH = 0;
_widescreen = widescreen; _widescreenMode = widescreenMode;
_wideTexture = 0; _widescreenTexture = 0;
_wideMargin = 0; _wideMargin = 0;
_enableWidescreen = false;
setScreenSize(w, h); setScreenSize(w, h);
_joystick = 0; _joystick = 0;
_controller = 0; _controller = 0;
@ -149,7 +155,7 @@ void SystemStub_SDL::destroy() {
} }
bool SystemStub_SDL::hasWidescreen() const { bool SystemStub_SDL::hasWidescreen() const {
return _widescreen; return _widescreenMode != kWidescreenNone;
} }
void SystemStub_SDL::setScreenSize(int w, int h) { void SystemStub_SDL::setScreenSize(int w, int h) {
@ -184,6 +190,14 @@ void SystemStub_SDL::setPalette(const uint8_t *pal, int n) {
} }
} }
void SystemStub_SDL::getPalette(uint8_t *pal, int n) {
assert(n <= 256);
for (int i = 0; i < n; ++i) {
SDL_GetRGB(_rgbPalette[i], _fmt, &pal[0], &pal[1], &pal[2]);
pal += 3;
}
}
void SystemStub_SDL::setPaletteEntry(int i, const Color *c) { void SystemStub_SDL::setPaletteEntry(int i, const Color *c) {
setPaletteColor(i, c->r, c->g, c->b); setPaletteColor(i, c->r, c->g, c->b);
} }
@ -259,7 +273,7 @@ static void clearTexture(SDL_Texture *texture, int h, SDL_PixelFormat *fmt) {
} }
} }
void SystemStub_SDL::copyRectLeftBorder(int w, int h, const uint8_t *buf) { void SystemStub_SDL::copyWidescreenLeft(int w, int h, const uint8_t *buf) {
assert(w >= _wideMargin); assert(w >= _wideMargin);
uint32_t *rgb = (uint32_t *)malloc(w * h * sizeof(uint32_t)); uint32_t *rgb = (uint32_t *)malloc(w * h * sizeof(uint32_t));
if (rgb) { if (rgb) {
@ -279,12 +293,12 @@ void SystemStub_SDL::copyRectLeftBorder(int w, int h, const uint8_t *buf) {
r.y = 0; r.y = 0;
r.w = _wideMargin; r.w = _wideMargin;
r.h = h; r.h = h;
SDL_UpdateTexture(_wideTexture, &r, rgb + xOffset, w * sizeof(uint32_t)); SDL_UpdateTexture(_widescreenTexture, &r, rgb + xOffset, w * sizeof(uint32_t));
free(rgb); free(rgb);
} }
} }
void SystemStub_SDL::copyRectRightBorder(int w, int h, const uint8_t *buf) { void SystemStub_SDL::copyWidescreenRight(int w, int h, const uint8_t *buf) {
assert(w >= _wideMargin); assert(w >= _wideMargin);
uint32_t *rgb = (uint32_t *)malloc(w * h * sizeof(uint32_t)); uint32_t *rgb = (uint32_t *)malloc(w * h * sizeof(uint32_t));
if (rgb) { if (rgb) {
@ -304,12 +318,12 @@ void SystemStub_SDL::copyRectRightBorder(int w, int h, const uint8_t *buf) {
r.y = 0; r.y = 0;
r.w = _wideMargin; r.w = _wideMargin;
r.h = h; r.h = h;
SDL_UpdateTexture(_wideTexture, &r, rgb + xOffset, w * sizeof(uint32_t)); SDL_UpdateTexture(_widescreenTexture, &r, rgb + xOffset, w * sizeof(uint32_t));
free(rgb); free(rgb);
} }
} }
void SystemStub_SDL::copyRectMirrorBorders(int w, int h, const uint8_t *buf) { void SystemStub_SDL::copyWidescreenMirror(int w, int h, const uint8_t *buf) {
assert(w >= _wideMargin); assert(w >= _wideMargin);
uint32_t *rgb = (uint32_t *)malloc(w * h * sizeof(uint32_t)); uint32_t *rgb = (uint32_t *)malloc(w * h * sizeof(uint32_t));
if (rgb) { if (rgb) {
@ -318,7 +332,7 @@ void SystemStub_SDL::copyRectMirrorBorders(int w, int h, const uint8_t *buf) {
} }
void *dst = 0; void *dst = 0;
int pitch = 0; int pitch = 0;
if (SDL_LockTexture(_wideTexture, 0, &dst, &pitch) == 0) { if (SDL_LockTexture(_widescreenTexture, 0, &dst, &pitch) == 0) {
assert((pitch & 3) == 0); assert((pitch & 3) == 0);
uint32_t *p = (uint32_t *)dst; uint32_t *p = (uint32_t *)dst;
for (int y = 0; y < h; ++y) { for (int y = 0; y < h; ++y) {
@ -332,12 +346,64 @@ void SystemStub_SDL::copyRectMirrorBorders(int w, int h, const uint8_t *buf) {
} }
p += pitch / sizeof(uint32_t); p += pitch / sizeof(uint32_t);
} }
SDL_UnlockTexture(_wideTexture); SDL_UnlockTexture(_widescreenTexture);
} }
free(rgb); free(rgb);
} }
} }
static uint32_t blurPixel(int x, int y, const uint8_t *src, const uint32_t *pal, int pitch, int w, int h, const SDL_PixelFormat *fmt) {
static const uint8_t blurMat[3 * 3] = {
2, 4, 2,
4, 8, 4,
2, 4, 2
};
static const int blurMatSigma = 32 * 2;
const uint32_t redBlueMask = fmt->Rmask | fmt->Bmask;
const uint32_t greenMask = fmt->Gmask;
uint32_t redBlueBlurSum = 0;
uint32_t greenBlurSum = 0;
for (int v = 0; v < 3; ++v) {
const int ym = CLIP(y + v - 1, 0, h - 1);
for (int u = 0; u < 3; ++u) {
const int xm = CLIP(x + u - 1, 0, w - 1);
const uint32_t color = pal[src[ym * pitch + xm]];
const int mul = blurMat[v * 3 + u];
redBlueBlurSum += (color & redBlueMask) * mul;
greenBlurSum += (color & greenMask) * mul;
}
}
return ((redBlueBlurSum / blurMatSigma) & redBlueMask) | ((greenBlurSum / blurMatSigma) & greenMask);
}
void SystemStub_SDL::copyWidescreenBlur(int w, int h, const uint8_t *buf) {
assert(w == _screenW && h == _screenH);
void *dst = 0;
int pitch = 0;
if (SDL_LockTexture(_widescreenTexture, 0, &dst, &pitch) == 0) {
assert((pitch & 3) == 0);
uint32_t *p = (uint32_t *)dst;
for (int y = 0; y < h; ++y) {
for (int x = 0; x < w; ++x) {
p[x] = blurPixel(x, y, buf, _rgbPalette, w, w, h, _fmt);
}
p += pitch / sizeof(uint32_t);
}
SDL_UnlockTexture(_widescreenTexture);
}
}
void SystemStub_SDL::clearWidescreen() {
clearTexture(_widescreenTexture, _screenH, _fmt);
}
void SystemStub_SDL::enableWidescreen(bool enable) {
_enableWidescreen = enable;
}
void SystemStub_SDL::fadeScreen() { void SystemStub_SDL::fadeScreen() {
_fadeOnUpdateScreen = true; _fadeOnUpdateScreen = true;
} }
@ -355,9 +421,11 @@ void SystemStub_SDL::updateScreen(int shakeOffset) {
SDL_UpdateTexture(_texture, 0, _screenBuffer, _screenW * sizeof(uint32_t)); SDL_UpdateTexture(_texture, 0, _screenBuffer, _screenW * sizeof(uint32_t));
} }
SDL_RenderClear(_renderer); SDL_RenderClear(_renderer);
if (_widescreen) { if (_widescreenMode != kWidescreenNone) {
// borders / background screen if (_enableWidescreen) {
SDL_RenderCopy(_renderer, _wideTexture, 0, 0); // borders / background screen
SDL_RenderCopy(_renderer, _widescreenTexture, 0, 0);
}
// game screen // game screen
SDL_Rect r; SDL_Rect r;
r.y = shakeOffset * _scaleFactor; r.y = shakeOffset * _scaleFactor;
@ -756,6 +824,10 @@ void SystemStub_SDL::unlockAudio() {
SDL_UnlockAudio(); SDL_UnlockAudio();
} }
static bool is16_9(const SDL_DisplayMode *mode) {
return (mode->w / (float)mode->h) >= (16 / 9.f);
}
void SystemStub_SDL::prepareGraphics() { void SystemStub_SDL::prepareGraphics() {
_texW = _screenW; _texW = _screenW;
_texH = _screenH; _texH = _screenH;
@ -781,7 +853,15 @@ void SystemStub_SDL::prepareGraphics() {
} else { } else {
flags |= SDL_WINDOW_RESIZABLE; flags |= SDL_WINDOW_RESIZABLE;
} }
if (_widescreen) { if (0 /* && _widescreenMode == kWidescreenDefault */) {
SDL_DisplayMode dm;
if (SDL_GetDesktopDisplayMode(0, &dm) == 0 && is16_9(&dm)) {
_widescreenMode = kWidescreenBlur; // default widescreen mode
} else {
_widescreenMode = kWidescreenNone;
}
}
if (_widescreenMode != kWidescreenNone) {
windowW = windowH * 16 / 9; windowW = windowH * 16 / 9;
} }
_window = SDL_CreateWindow(_caption, SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, windowW, windowH, flags); _window = SDL_CreateWindow(_caption, SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, windowW, windowH, flags);
@ -793,11 +873,13 @@ void SystemStub_SDL::prepareGraphics() {
_renderer = SDL_CreateRenderer(_window, -1, SDL_RENDERER_ACCELERATED); _renderer = SDL_CreateRenderer(_window, -1, SDL_RENDERER_ACCELERATED);
SDL_RenderSetLogicalSize(_renderer, windowW, windowH); SDL_RenderSetLogicalSize(_renderer, windowW, windowH);
_texture = SDL_CreateTexture(_renderer, kPixelFormat, SDL_TEXTUREACCESS_STREAMING, _texW, _texH); _texture = SDL_CreateTexture(_renderer, kPixelFormat, SDL_TEXTUREACCESS_STREAMING, _texW, _texH);
if (_widescreen) { if (_widescreenMode != kWidescreenNone) {
const int w = _screenH * 16 / 9; // in blur mode, the background texture has the same dimensions as the game texture
// SDL stretches the texture to 16:9
const int w = (_widescreenMode == kWidescreenBlur) ? _screenW : _screenH * 16 / 9;
const int h = _screenH; const int h = _screenH;
_wideTexture = SDL_CreateTexture(_renderer, kPixelFormat, SDL_TEXTUREACCESS_STREAMING, w, h); _widescreenTexture = SDL_CreateTexture(_renderer, kPixelFormat, SDL_TEXTUREACCESS_STREAMING, w, h);
clearTexture(_wideTexture, _screenH, _fmt); clearTexture(_widescreenTexture, _screenH, _fmt);
// left and right borders // left and right borders
_wideMargin = (w - _screenW) / 2; _wideMargin = (w - _screenW) / 2;
@ -809,9 +891,9 @@ void SystemStub_SDL::cleanupGraphics() {
SDL_DestroyTexture(_texture); SDL_DestroyTexture(_texture);
_texture = 0; _texture = 0;
} }
if (_wideTexture) { if (_widescreenTexture) {
SDL_DestroyTexture(_wideTexture); SDL_DestroyTexture(_widescreenTexture);
_wideTexture = 0; _widescreenTexture = 0;
} }
if (_renderer) { if (_renderer) {
SDL_DestroyRenderer(_renderer); SDL_DestroyRenderer(_renderer);
@ -859,7 +941,7 @@ void SystemStub_SDL::changeScaler(int scaler) {
break; break;
case 5: case 5:
scalerParameters.type = kScalerTypeInternal; scalerParameters.type = kScalerTypeInternal;
scalerParameters.scaler = &scaler_xbrz; scalerParameters.scaler = &scaler_xbr;
break; break;
#endif #endif
default: default:
@ -868,13 +950,13 @@ void SystemStub_SDL::changeScaler(int scaler) {
if (_scalerType != scalerParameters.type || scalerParameters.scaler != _scaler) { if (_scalerType != scalerParameters.type || scalerParameters.scaler != _scaler) {
_scalerType = scalerParameters.type; _scalerType = scalerParameters.type;
_scaler = scalerParameters.scaler; _scaler = scalerParameters.scaler;
const int scaleFactor = CLIP(_scaleFactor, _scaler->factorMin, _scaler->factorMax); if (_scalerType == kScalerTypeInternal || _scalerType == kScalerTypeExternal) {
// only recreate the window if dimensions actually changed _scaleFactor = CLIP(_scaleFactor, _scaler->factorMin, _scaler->factorMax);
if (scaleFactor != _scaleFactor) { } else {
cleanupGraphics(); _scaleFactor = 1;
_scaleFactor = scaleFactor;
prepareGraphics();
} }
cleanupGraphics();
prepareGraphics();
} }
} }

View File

@ -1,7 +1,7 @@
/* /*
* REminiscence - Flashback interpreter * REminiscence - Flashback interpreter
* Copyright (C) 2005-2018 Gregory Montoir (cyx@users.sourceforge.net) * Copyright (C) 2005-2019 Gregory Montoir (cyx@users.sourceforge.net)
*/ */
#include "unpack.h" #include "unpack.h"
@ -18,7 +18,7 @@ struct UnpackCtx {
static bool nextBit(UnpackCtx *uc) { static bool nextBit(UnpackCtx *uc) {
bool carry = (uc->bits & 1) != 0; bool carry = (uc->bits & 1) != 0;
uc->bits >>= 1; uc->bits >>= 1;
if (uc->bits == 0) { if (uc->bits == 0) { // getnextlwd
uc->bits = READ_BE_UINT32(uc->src); uc->src -= 4; uc->bits = READ_BE_UINT32(uc->src); uc->src -= 4;
uc->crc ^= uc->bits; uc->crc ^= uc->bits;
carry = (uc->bits & 1) != 0; carry = (uc->bits & 1) != 0;
@ -27,7 +27,7 @@ static bool nextBit(UnpackCtx *uc) {
return carry; return carry;
} }
static int getBits(UnpackCtx *uc, int count) { static int getBits(UnpackCtx *uc, int count) { // rdd1bits
int bits = 0; int bits = 0;
for (int i = 0; i < count; ++i) { for (int i = 0; i < count; ++i) {
bits <<= 1; bits <<= 1;
@ -38,7 +38,7 @@ static int getBits(UnpackCtx *uc, int count) {
return bits; return bits;
} }
static void copyLiteral(UnpackCtx *uc, int bitsCount, int len) { static void copyLiteral(UnpackCtx *uc, int bitsCount, int len) { // getd3chr
int count = getBits(uc, bitsCount) + len + 1; int count = getBits(uc, bitsCount) + len + 1;
uc->size -= count; uc->size -= count;
if (uc->size < 0) { if (uc->size < 0) {
@ -51,7 +51,7 @@ static void copyLiteral(UnpackCtx *uc, int bitsCount, int len) {
uc->dst -= count; uc->dst -= count;
} }
static void copyReference(UnpackCtx *uc, int bitsCount, int count) { static void copyReference(UnpackCtx *uc, int bitsCount, int count) { // copyd3bytes
uc->size -= count; uc->size -= count;
if (uc->size < 0) { if (uc->size < 0) {
count += uc->size; count += uc->size;
@ -64,7 +64,7 @@ static void copyReference(UnpackCtx *uc, int bitsCount, int count) {
uc->dst -= count; uc->dst -= count;
} }
bool delphine_unpack(uint8_t *dst, int dstSize, const uint8_t *src, int srcSize) { bool bytekiller_unpack(uint8_t *dst, int dstSize, const uint8_t *src, int srcSize) {
UnpackCtx uc; UnpackCtx uc;
uc.src = src + srcSize - 4; uc.src = src + srcSize - 4;
uc.size = READ_BE_UINT32(uc.src); uc.src -= 4; uc.size = READ_BE_UINT32(uc.src); uc.src -= 4;

View File

@ -1,7 +1,7 @@
/* /*
* REminiscence - Flashback interpreter * REminiscence - Flashback interpreter
* Copyright (C) 2005-2018 Gregory Montoir (cyx@users.sourceforge.net) * Copyright (C) 2005-2019 Gregory Montoir (cyx@users.sourceforge.net)
*/ */
#ifndef UNPACK_H__ #ifndef UNPACK_H__
@ -9,6 +9,6 @@
#include "intern.h" #include "intern.h"
extern bool delphine_unpack(uint8_t *dst, int dstSize, const uint8_t *src, int srcSize); extern bool bytekiller_unpack(uint8_t *dst, int dstSize, const uint8_t *src, int srcSize);
#endif // UNPACK_H__ #endif // UNPACK_H__

View File

@ -1,7 +1,7 @@
/* /*
* REminiscence - Flashback interpreter * REminiscence - Flashback interpreter
* Copyright (C) 2005-2018 Gregory Montoir (cyx@users.sourceforge.net) * Copyright (C) 2005-2019 Gregory Montoir (cyx@users.sourceforge.net)
*/ */
#ifdef _WIN32 #ifdef _WIN32

2
util.h
View File

@ -1,7 +1,7 @@
/* /*
* REminiscence - Flashback interpreter * REminiscence - Flashback interpreter
* Copyright (C) 2005-2018 Gregory Montoir (cyx@users.sourceforge.net) * Copyright (C) 2005-2019 Gregory Montoir (cyx@users.sourceforge.net)
*/ */
#ifndef UTIL_H__ #ifndef UTIL_H__

121
video.cpp
View File

@ -1,7 +1,7 @@
/* /*
* REminiscence - Flashback interpreter * REminiscence - Flashback interpreter
* Copyright (C) 2005-2018 Gregory Montoir (cyx@users.sourceforge.net) * Copyright (C) 2005-2019 Gregory Montoir (cyx@users.sourceforge.net)
*/ */
#include "decode_mac.h" #include "decode_mac.h"
@ -11,8 +11,8 @@
#include "util.h" #include "util.h"
#include "video.h" #include "video.h"
Video::Video(Resource *res, SystemStub *stub) Video::Video(Resource *res, SystemStub *stub, WidescreenMode widescreenMode)
: _res(res), _stub(stub) { : _res(res), _stub(stub), _widescreenMode(widescreenMode) {
_layerScale = (_res->_type == kResourceTypeMac) ? 2 : 1; // Macintosh version is 512x448 _layerScale = (_res->_type == kResourceTypeMac) ? 2 : 1; // Macintosh version is 512x448
_w = GAMESCREEN_W * _layerScale; _w = GAMESCREEN_W * _layerScale;
_h = GAMESCREEN_H * _layerScale; _h = GAMESCREEN_H * _layerScale;
@ -115,6 +115,18 @@ void Video::updateScreen() {
} }
} }
void Video::updateWidescreen() {
if (_stub->hasWidescreen()) {
if (_widescreenMode == kWidescreenMirrorRoom) {
_stub->copyWidescreenMirror(_w, _h, _backLayer);
} else if (_widescreenMode == kWidescreenBlur) {
_stub->copyWidescreenBlur(_w, _h, _backLayer);
} else {
_stub->clearWidescreen();
}
}
}
void Video::fullRefresh() { void Video::fullRefresh() {
debug(DBG_VIDEO, "Video::fullRefresh()"); debug(DBG_VIDEO, "Video::fullRefresh()");
_fullRefresh = true; _fullRefresh = true;
@ -154,20 +166,26 @@ void Video::setPaletteColorBE(int num, int offset) {
void Video::setPaletteSlotBE(int palSlot, int palNum) { void Video::setPaletteSlotBE(int palSlot, int palNum) {
debug(DBG_VIDEO, "Video::setPaletteSlotBE()"); debug(DBG_VIDEO, "Video::setPaletteSlotBE()");
const uint8_t *p = _res->_pal + palNum * 0x20; const uint8_t *p = _res->_pal + palNum * 32;
for (int i = 0; i < 16; ++i) { for (int i = 0; i < 16; ++i) {
const int color = READ_BE_UINT16(p); p += 2; const int color = READ_BE_UINT16(p); p += 2;
Color c = AMIGA_convertColor(color, true); Color c = AMIGA_convertColor(color, true);
_stub->setPaletteEntry(palSlot * 0x10 + i, &c); _stub->setPaletteEntry(palSlot * 16 + i, &c);
} }
} }
void Video::setPaletteSlotLE(int palSlot, const uint8_t *palData) { void Video::setPaletteSlotLE(int palSlot, const uint8_t *palData) {
debug(DBG_VIDEO, "Video::setPaletteSlotLE()"); debug(DBG_VIDEO, "Video::setPaletteSlotLE()");
for (int i = 0; i < 16; ++i) { for (int i = 0; i < 16; ++i) {
uint16_t color = READ_LE_UINT16(palData); palData += 2; const uint16_t color = READ_LE_UINT16(palData + i * 2);
Color c = AMIGA_convertColor(color); Color c = AMIGA_convertColor(color);
_stub->setPaletteEntry(palSlot * 0x10 + i, &c); _stub->setPaletteEntry(palSlot * 16 + i, &c);
}
if (palSlot == 4 && g_options.use_white_tshirt) {
const Color color12 = AMIGA_convertColor(0x888);
const Color color13 = AMIGA_convertColor((palData == _conradPal2) ? 0x888 : 0xCCC);
_stub->setPaletteEntry(palSlot * 16 + 12, &color12);
_stub->setPaletteEntry(palSlot * 16 + 13, &color13);
} }
} }
@ -315,6 +333,33 @@ void Video::PC_decodeSpc(const uint8_t *src, int w, int h, uint8_t *dst) {
} }
} }
void Video::PC_decodeSpm(const uint8_t *dataPtr, uint8_t *dst) {
const int len = 2 * READ_BE_UINT16(dataPtr); dataPtr += 2;
uint8_t *dst2 = dst + 1024;
for (int i = 0; i < len; ++i) {
*dst2++ = dataPtr[i] >> 4;
*dst2++ = dataPtr[i] & 15;
}
const uint8_t *src = dst + 1024;
const uint8_t *end = src + len;
do {
const uint8_t code = *src++;
if (code == 0xF) {
uint8_t color = *src++;
int count = *src++;
if (color == 0xF) {
count = (count << 4) | *src++;
color = *src++;
}
count += 4;
memset(dst, color, count);
dst += count;
} else {
*dst++ = code;
}
} while (src < end);
}
static void AMIGA_planar16(uint8_t *dst, int w, int h, int depth, const uint8_t *src) { static void AMIGA_planar16(uint8_t *dst, int w, int h, int depth, const uint8_t *src) {
const int pitch = w * 16; const int pitch = w * 16;
const int planarSize = w * 2 * h; const int planarSize = w * 2 * h;
@ -629,7 +674,7 @@ static void decodeLevHelper(uint8_t *dst, const uint8_t *src, int offset10, int
void Video::AMIGA_decodeLev(int level, int room) { void Video::AMIGA_decodeLev(int level, int room) {
uint8_t *tmp = _res->_scratchBuffer; uint8_t *tmp = _res->_scratchBuffer;
const int offset = READ_BE_UINT32(_res->_lev + room * 4); const int offset = READ_BE_UINT32(_res->_lev + room * 4);
if (!delphine_unpack(tmp, Resource::kScratchBufferSize, _res->_lev, offset)) { if (!bytekiller_unpack(tmp, Resource::kScratchBufferSize, _res->_lev, offset)) {
warning("Bad CRC for level %d room %d", level, room); warning("Bad CRC for level %d room %d", level, room);
return; return;
} }
@ -912,7 +957,7 @@ void Video::MAC_drawStringChar(uint8_t *dst, int pitch, int x, int y, const uint
buf.h = _h; buf.h = _h;
buf.x = x * _layerScale; buf.x = x * _layerScale;
buf.y = y * _layerScale; buf.y = y * _layerScale;
buf.setPixel = Video::MAC_drawBufferFont; buf.setPixel = Video::MAC_setPixelFont;
_MAC_fontFrontColor = color; _MAC_fontFrontColor = color;
_MAC_fontShadowColor = _charShadowColor; _MAC_fontShadowColor = _charShadowColor;
assert(chr >= 32); assert(chr >= 32);
@ -963,7 +1008,7 @@ void Video::MAC_decodeMap(int level, int room) {
buf.ptr = _frontLayer; buf.ptr = _frontLayer;
buf.w = buf.pitch = _w; buf.w = buf.pitch = _w;
buf.h = _h; buf.h = _h;
buf.setPixel = Video::MAC_drawBuffer; buf.setPixel = Video::MAC_setPixel;
_res->MAC_loadLevelRoom(level, room, &buf); _res->MAC_loadLevelRoom(level, room, &buf);
memcpy(_backLayer, _frontLayer, _layerSize); memcpy(_backLayer, _frontLayer, _layerSize);
Color roomPalette[256]; Color roomPalette[256];
@ -979,45 +1024,27 @@ void Video::MAC_decodeMap(int level, int room) {
} }
} }
void Video::MAC_drawBuffer(DecodeBuffer *buf, int src_x, int src_y, int src_w, int src_h, uint8_t color) { void Video::MAC_setPixel(DecodeBuffer *buf, int x, int y, uint8_t color) {
const int y = buf->y + src_y; const int offset = y * buf->pitch + x;
if (y >= 0 && y < buf->h) { buf->ptr[offset] = color;
const int x = buf->xflip ? (buf->x + (src_w - 1 - src_x)) : (buf->x + src_x); }
if (x >= 0 && x < buf->w) {
const int offset = y * buf->pitch + x; void Video::MAC_setPixelMask(DecodeBuffer *buf, int x, int y, uint8_t color) {
buf->ptr[offset] = color; const int offset = y * buf->pitch + x;
} if ((buf->ptr[offset] & 0x80) == 0) {
buf->ptr[offset] = color;
} }
} }
void Video::MAC_drawBufferMask(DecodeBuffer *buf, int src_x, int src_y, int src_w, int src_h, uint8_t color) { void Video::MAC_setPixelFont(DecodeBuffer *buf, int x, int y, uint8_t color) {
const int y = buf->y + src_y; const int offset = y * buf->pitch + x;
if (y >= 0 && y < buf->h) { switch (color) {
const int x = buf->xflip ? (buf->x + (src_w - 1 - src_x)) : (buf->x + src_x); case 0xC0:
if (x >= 0 && x < buf->w) { buf->ptr[offset] = _MAC_fontShadowColor;
const int offset = y * buf->pitch + x; break;
if ((buf->ptr[offset] & 0x80) == 0) { case 0xC1:
buf->ptr[offset] = color; buf->ptr[offset] = _MAC_fontFrontColor;
} break;
}
}
}
void Video::MAC_drawBufferFont(DecodeBuffer *buf, int src_x, int src_y, int src_w, int src_h, uint8_t color) {
const int y = buf->y + src_y;
if (y >= 0 && y < buf->h) {
const int x = buf->x + src_x;
if (x >= 0 && x < buf->w) {
const int offset = y * buf->pitch + x;
switch (color) {
case 0xC0:
buf->ptr[offset] = _MAC_fontShadowColor;
break;
case 0xC1:
buf->ptr[offset] = _MAC_fontFrontColor;
break;
}
}
} }
} }
@ -1049,7 +1076,7 @@ void Video::MAC_drawSprite(int x, int y, const uint8_t *data, int frame, bool xf
buf.h = _h; buf.h = _h;
buf.x = x * _layerScale; buf.x = x * _layerScale;
buf.y = y * _layerScale; buf.y = y * _layerScale;
buf.setPixel = eraseBackground ? MAC_drawBuffer : MAC_drawBufferMask; buf.setPixel = eraseBackground ? MAC_setPixel : MAC_setPixelMask;
fixOffsetDecodeBuffer(&buf, dataPtr); fixOffsetDecodeBuffer(&buf, dataPtr);
_res->MAC_decodeImageData(data, frame, &buf); _res->MAC_decodeImageData(data, frame, &buf);
markBlockAsDirty(buf.x, buf.y, READ_BE_UINT16(dataPtr), READ_BE_UINT16(dataPtr + 2), 1); markBlockAsDirty(buf.x, buf.y, READ_BE_UINT16(dataPtr), READ_BE_UINT16(dataPtr + 2), 1);

13
video.h
View File

@ -1,7 +1,7 @@
/* /*
* REminiscence - Flashback interpreter * REminiscence - Flashback interpreter
* Copyright (C) 2005-2018 Gregory Montoir (cyx@users.sourceforge.net) * Copyright (C) 2005-2019 Gregory Montoir (cyx@users.sourceforge.net)
*/ */
#ifndef VIDEO_H__ #ifndef VIDEO_H__
@ -32,6 +32,7 @@ struct Video {
Resource *_res; Resource *_res;
SystemStub *_stub; SystemStub *_stub;
WidescreenMode _widescreenMode;
int _w, _h; int _w, _h;
int _layerSize; int _layerSize;
@ -50,11 +51,12 @@ struct Video {
uint8_t _shakeOffset; uint8_t _shakeOffset;
drawCharFunc _drawChar; drawCharFunc _drawChar;
Video(Resource *res, SystemStub *stub); Video(Resource *res, SystemStub *stub, WidescreenMode widescreenMode);
~Video(); ~Video();
void markBlockAsDirty(int16_t x, int16_t y, uint16_t w, uint16_t h, int scale); void markBlockAsDirty(int16_t x, int16_t y, uint16_t w, uint16_t h, int scale);
void updateScreen(); void updateScreen();
void updateWidescreen();
void fullRefresh(); void fullRefresh();
void fadeOut(); void fadeOut();
void fadeOutPalette(); void fadeOutPalette();
@ -68,6 +70,7 @@ struct Video {
void PC_setLevelPalettes(); void PC_setLevelPalettes();
void PC_decodeIcn(const uint8_t *src, int num, uint8_t *dst); void PC_decodeIcn(const uint8_t *src, int num, uint8_t *dst);
void PC_decodeSpc(const uint8_t *src, int w, int h, uint8_t *dst); void PC_decodeSpc(const uint8_t *src, int w, int h, uint8_t *dst);
void PC_decodeSpm(const uint8_t *dataPtr, uint8_t *dstPtr);
void AMIGA_decodeLev(int level, int room); void AMIGA_decodeLev(int level, int room);
void AMIGA_decodeSpm(const uint8_t *src, uint8_t *dst); void AMIGA_decodeSpm(const uint8_t *src, uint8_t *dst);
void AMIGA_decodeIcn(const uint8_t *src, int num, uint8_t *dst); void AMIGA_decodeIcn(const uint8_t *src, int num, uint8_t *dst);
@ -87,9 +90,9 @@ struct Video {
void drawStringLen(const char *str, int len, int x, int y, uint8_t color); void drawStringLen(const char *str, int len, int x, int y, uint8_t color);
static Color AMIGA_convertColor(const uint16_t color, bool bgr = false); static Color AMIGA_convertColor(const uint16_t color, bool bgr = false);
void MAC_decodeMap(int level, int room); void MAC_decodeMap(int level, int room);
static void MAC_drawBuffer(DecodeBuffer *buf, int src_x, int src_y, int src_w, int src_h, uint8_t color); static void MAC_setPixel(DecodeBuffer *buf, int x, int y, uint8_t color);
static void MAC_drawBufferMask(DecodeBuffer *buf, int src_x, int src_y, int src_w, int src_h, uint8_t color); static void MAC_setPixelMask(DecodeBuffer *buf, int x, int y, uint8_t color);
static void MAC_drawBufferFont(DecodeBuffer *buf, int src_x, int src_y, int src_w, int src_h, uint8_t color); static void MAC_setPixelFont(DecodeBuffer *buf, int x, int y, uint8_t color);
void fillRect(int x, int y, int w, int h, uint8_t color); void fillRect(int x, int y, int w, int h, uint8_t color);
void MAC_drawSprite(int x, int y, const uint8_t *data, int frame, bool xflip, bool eraseBackground); void MAC_drawSprite(int x, int y, const uint8_t *data, int frame, bool xflip, bool eraseBackground);
}; };