diff --git a/CHANGES.txt b/CHANGES.txt new file mode 100644 index 0000000..9e89886 --- /dev/null +++ b/CHANGES.txt @@ -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 diff --git a/Makefile b/Makefile index 9e5d737..171f44e 100644 --- a/Makefile +++ b/Makefile @@ -9,12 +9,12 @@ ZLIB_LIBS := -lz 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 \ - menu.cpp mixer.cpp mod_player.cpp ogg_player.cpp piege.cpp resource.cpp resource_aba.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 protection.cpp resource.cpp resource_aba.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 -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) DEPS = $(SRCS:.cpp=.d) $(SCALERS:.cpp=.d) @@ -25,6 +25,6 @@ rs: $(OBJS) $(CXX) $(LDFLAGS) -o $@ $(OBJS) $(LIBS) clean: - rm -f *.o *.d + rm -f $(OBJS) $(DEPS) -include $(DEPS) diff --git a/README.txt b/README.txt index ebe078e..661981a 100644 --- a/README.txt +++ b/README.txt @@ -1,6 +1,6 @@ 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 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). 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 '.') --levelnum=NUM Level to start from (default '0') --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') --language=LANG Language (fr,en,de,sp,it,jp) --autosave Save game state automatically diff --git a/collision.cpp b/collision.cpp index 9e542eb..60907a6 100644 --- a/collision.cpp +++ b/collision.cpp @@ -1,7 +1,7 @@ /* * 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" diff --git a/cpc_player.cpp b/cpc_player.cpp new file mode 100644 index 0000000..aa84934 --- /dev/null +++ b/cpc_player.cpp @@ -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); +} diff --git a/cpc_player.h b/cpc_player.h new file mode 100644 index 0000000..3938ca0 --- /dev/null +++ b/cpc_player.h @@ -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 diff --git a/cutscene.cpp b/cutscene.cpp index 8c689d0..3b39fec 100644 --- a/cutscene.cpp +++ b/cutscene.cpp @@ -1,7 +1,7 @@ /* * REminiscence - Flashback interpreter - * Copyright (C) 2005-2018 Gregory Montoir (cyx@users.sourceforge.net) + * Copyright (C) 2005-2019 Gregory Montoir (cyx@users.sourceforge.net) */ #include @@ -94,6 +94,15 @@ void Cutscene::setPalette() { 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 const int16_t sin_a = SIN(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 sin_b = SIN(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[1] /* .y1 */ = ((sin_a * cos_b) >> 8) + ((((cos_c * cos_a) >> 8) * sin_b) >> 8); - _rotMat[2] /* .x2 */ = ( sin_c * sin_a) >> 8; - _rotMat[3] /* .y2 */ = (-sin_c * cos_a) >> 8; + _rotMat[0] = ((cos_a * cos_b) >> 8) - ((((cos_c * sin_a) >> 8) * sin_b) >> 8); + _rotMat[1] = ((sin_a * cos_b) >> 8) + ((((cos_c * cos_a) >> 8) * sin_b) >> 8); + _rotMat[2] = ( sin_c * sin_a) >> 8; + _rotMat[3] = (-sin_c * cos_a) >> 8; } static bool isNewLineChar(uint8_t chr, Resource *res) { @@ -188,21 +197,20 @@ void Cutscene::swapLayers() { void Cutscene::drawCreditsText() { if (_creditsSequence) { - if (_textUnk2 != 0) { - if (_varText == 0) { - _textUnk2 = 0; + if (_creditsKeepText != 0) { + if (_creditsSlowText == 0) { + _creditsKeepText = 0; } else { return; } } if (_creditsTextCounter <= 0) { - uint8_t code = *_textCurPtr; + const uint8_t code = *_textCurPtr; if (code == 0xFF) { _textBuf[0] = 0xA; } else if (code == 0xFE) { ++_textCurPtr; - code = *_textCurPtr++; - _creditsTextCounter = code; + _creditsTextCounter = *_textCurPtr++; } else if (code == 1) { ++_textCurPtr; _creditsTextPosX = *_textCurPtr++; @@ -211,8 +219,8 @@ void Cutscene::drawCreditsText() { _textCurBuf = _textBuf; _textBuf[0] = 0xA; ++_textCurPtr; - if (_varText != 0) { - _textUnk2 = 0xFF; + if (_creditsSlowText != 0) { + _creditsKeepText = 0xFF; } } else { *_textCurBuf++ = code; @@ -235,7 +243,6 @@ void Cutscene::drawProtectionShape(uint8_t shapeNum, int16_t zoom) { int16_t x = 0; int16_t y = 0; zoom += 512; - setRotationTransform(0, 180, 90); const uint8_t *shapeOffsetTable = _protectionShapeData + READ_BE_UINT16(_protectionShapeData + 0x02); 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; _primitiveColor = 0xC0 + *shapeData++; - drawShapeScaleRotate(p, zoom, dx, dy, x, y, 0, 0); + drawShapeScale(p, zoom, dx, dy, x, y, 0, 0); ++_shape_count; } } @@ -269,7 +276,7 @@ void Cutscene::op_markCurPos() { _frameDelay = 5; setPalette(); swapLayers(); - _varText = 0; + _creditsSlowText = 0; } void Cutscene::op_refreshScreen() { @@ -277,7 +284,7 @@ void Cutscene::op_refreshScreen() { _clearScreen = fetchNextCmdByte(); if (_clearScreen != 0) { swapLayers(); - _varText = 0; + _creditsSlowText = 0; } } @@ -286,7 +293,7 @@ void Cutscene::op_waitForSync() { if (_creditsSequence) { uint16_t n = fetchNextCmdByte() * 2; do { - _varText = 0xFF; + _creditsSlowText = 0xFF; _frameDelay = 3; if (_textBuf == _textCurBuf) { _creditsTextCounter = _res->isAmiga() ? 60 : 20; @@ -296,7 +303,7 @@ void Cutscene::op_waitForSync() { setPalette(); } while (--n); swapLayers(); - _varText = 0; + _creditsSlowText = 0; } else { _frameDelay = fetchNextCmdByte() * 4; sync(); // XXX handle input @@ -447,7 +454,7 @@ void Cutscene::op_refreshAll() { _frameDelay = 5; setPalette(); swapLayers(); - _varText = 0xFF; + _creditsSlowText = 0xFF; op_handleKeys(); } @@ -859,7 +866,7 @@ void Cutscene::op_drawShapeScaleRotate() { void Cutscene::op_drawCreditsText() { debug(DBG_CUT, "Cutscene::op_drawCreditsText()"); - _varText = 0xFF; + _creditsSlowText = 0xFF; if (_textCurBuf == _textBuf) { ++_creditsTextCounter; } @@ -1009,11 +1016,13 @@ void Cutscene::mainLoop(uint16_t num) { bool Cutscene::load(uint16_t cutName) { assert(cutName != 0xFFFF); - const char *name = _namesTable[cutName & 0xFF]; + const char *name = _namesTableDOS[cutName & 0xFF]; switch (_res->_type) { case kResourceTypeAmiga: - if (strncmp(name, "INTRO", 5) == 0) { + if (cutName == 7) { name = "INTRO"; + } else if (cutName == 10) { + name = "SERRURE"; } _res->load(name, Resource::OT_CMP); if (_id == 0x39 && _res->_lang != LANG_FR) { @@ -1091,8 +1100,8 @@ void Cutscene::playCredits() { _textBuf[0] = 0xA; _textCurBuf = _textBuf; _creditsSequence = true; - _varText = 0; - _textUnk2 = 0; + _creditsSlowText = 0; + _creditsKeepText = 0; _creditsTextCounter = 0; _interrupted = false; const uint16_t *cut_seq = _creditsCutSeq; @@ -1102,8 +1111,9 @@ void Cutscene::playCredits() { break; } prepare(); - uint16_t cutName = _offsetsTable[cut_id * 2 + 0]; - uint16_t cutOff = _offsetsTable[cut_id * 2 + 1]; + const uint16_t *offsets = _res->isAmiga() ? _offsetsTableAmiga : _offsetsTableDOS; + uint16_t cutName = offsets[cut_id * 2 + 0]; + uint16_t cutOff = offsets[cut_id * 2 + 1]; if (load(cutName)) { mainLoop(cutOff); unload(); @@ -1149,18 +1159,26 @@ void Cutscene::play() { debug(DBG_CUT, "Cutscene::play() _id=0x%X", _id); _creditsSequence = false; prepare(); - uint16_t cutName = _offsetsTable[_id * 2 + 0]; - uint16_t cutOff = _offsetsTable[_id * 2 + 1]; + const uint16_t *offsets = _res->isAmiga() ? _offsetsTableAmiga : _offsetsTableDOS; + uint16_t cutName = offsets[_id * 2 + 0]; + uint16_t cutOff = offsets[_id * 2 + 1]; if (cutName == 0xFFFF) { switch (_id) { + case 3: // keys + if (g_options.play_carte_cutscene) { + cutName = 2; // CARTE + } + break; + case 8: // save checkpoints + break; case 19: if (g_options.play_serrure_cutscene) { cutName = 31; // SERRURE } break; - case 22: - case 23: - case 24: + case 22: // Level 2 fuse repaired + case 23: // switches + case 24: // Level 2 fuse is blown if (g_options.play_asc_cutscene) { cutName = 12; // ASC } @@ -1171,6 +1189,11 @@ void Cutscene::play() { cutName = 14; // METRO } break; + case 46: // Level 2 terminal card mission + break; + default: + warning("Unknown cutscene %d", _id); + break; } } if (_patchedOffsetsTable) { @@ -1284,7 +1307,7 @@ void Cutscene::playSet(const uint8_t *p, int offset) { offset = 10; 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(); 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); _stub->sleep((diff < 16) ? 16 : diff); _stub->processEvents(); + if (_stub->_pi.backspace) { + _stub->_pi.backspace = false; + _interrupted = true; + } } } diff --git a/cutscene.h b/cutscene.h index 04dac55..13c354d 100644 --- a/cutscene.h +++ b/cutscene.h @@ -1,7 +1,7 @@ /* * 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__ @@ -39,8 +39,9 @@ struct Cutscene { }; static const OpcodeStub _opcodeTable[]; - static const char *_namesTable[]; - static const uint16_t _offsetsTable[]; + static const char *_namesTableDOS[]; + static const uint16_t _offsetsTableDOS[]; + static const uint16_t _offsetsTableAmiga[]; static const uint8_t _amigaDemoOffsetsTable[]; static const uint8_t _ssiOffsetsTable[]; static const uint16_t _cosTable[]; @@ -70,7 +71,7 @@ struct Cutscene { uint32_t _tstamp; uint8_t _frameDelay; bool _newPal; - uint8_t _palBuf[0x20 * 2]; + uint8_t _palBuf[16 * sizeof(uint16_t) * 2]; uint16_t _baseOffset; bool _creditsSequence; uint32_t _rotMat[4]; @@ -78,7 +79,6 @@ struct Cutscene { uint8_t _clearScreen; Point _vertices[0x80]; bool _hasAlphaColor; - uint8_t _varText; uint8_t _varKey; int16_t _shape_ix; int16_t _shape_iy; @@ -97,7 +97,8 @@ struct Cutscene { uint8_t _textBuf[500]; const uint8_t *_textCurPtr; uint8_t *_textCurBuf; - uint8_t _textUnk2; + uint8_t _creditsSlowText; + uint8_t _creditsKeepText; uint8_t _creditsTextPosX; uint8_t _creditsTextPosY; int16_t _creditsTextCounter; diff --git a/decode_mac.cpp b/decode_mac.cpp index cf18bf9..205e25e 100644 --- a/decode_mac.cpp +++ b/decode_mac.cpp @@ -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) { - buf->setPixel(buf, x, y, w, h, color); -} - -void decodeC103(const uint8_t *a3, int w, int h, DecodeBuffer *buf) { - uint8_t d0; - int d3 = 0; - int d7 = 1; - int d6 = 0; - 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; + y += buf->y; + if (y >= 0 && y < buf->h) { + if (buf->xflip) { + x = w - 1 - x; + } + x += buf->x; + if (x >= 0 && x < buf->w) { + buf->setPixel(buf, x, y, color); } } } -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 { const uint8_t *ptr; int repeatCount; @@ -90,52 +97,48 @@ void decodeC211(const uint8_t *a3, int w, int h, DecodeBuffer *buf) { int sp = 0; while (1) { - uint8_t d0 = *a3++; - if ((d0 & 0x80) != 0) { + const uint8_t code = *src++; + if ((code & 0x80) != 0) { ++y; x = 0; } - int d1 = d0 & 0x1F; - if (d1 == 0) { - d1 = READ_BE_UINT16(a3); a3 += 2; + int count = code & 0x1F; + if (count == 0) { + count = READ_BE_UINT16(src); src += 2; } - const int carry_set = (d0 & 0x40) != 0; - d0 <<= 2; - if (!carry_set) { - if ((d0 & 0x80) == 0) { - --d1; - if (d1 == 0) { + if ((code & 0x40) == 0) { + if ((code & 0x20) == 0) { + if (count == 1) { assert(sp > 0); --stack[sp - 1].repeatCount; if (stack[sp - 1].repeatCount >= 0) { - a3 = stack[sp - 1].ptr; + src = stack[sp - 1].ptr; } else { --sp; } } else { assert(sp < ARRAYSIZE(stack)); - stack[sp].ptr = a3; - stack[sp].repeatCount = d1; + stack[sp].ptr = src; + stack[sp].repeatCount = count - 1; ++sp; } } else { - x += d1; + x += count; } } else { - if ((d0 & 0x80) == 0) { - if (d1 == 1) { + if ((code & 0x20) == 0) { + if (count == 1) { return; } - const uint8_t color = *a3++; - for (int i = 0; i < d1; ++i) { + const uint8_t color = *src++; + for (int i = 0; i < count; ++i) { setPixel(x++, y, w, h, color, buf); } } else { - for (int i = 0; i < d1; ++i) { - setPixel(x++, y, w, h, *a3++, buf); + for (int i = 0; i < count; ++i) { + setPixel(x++, y, w, h, *src++, buf); } } } } } - diff --git a/decode_mac.h b/decode_mac.h index 280b548..4fa900f 100644 --- a/decode_mac.h +++ b/decode_mac.h @@ -13,7 +13,7 @@ struct DecodeBuffer { int x, y; 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; }; diff --git a/file.cpp b/file.cpp index 19100e6..d9fd404 100644 --- a/file.cpp +++ b/file.cpp @@ -1,7 +1,7 @@ /* * REminiscence - Flashback interpreter - * Copyright (C) 2005-2018 Gregory Montoir (cyx@users.sourceforge.net) + * Copyright (C) 2005-2019 Gregory Montoir (cyx@users.sourceforge.net) */ #include @@ -324,6 +324,16 @@ void File::writeByte(uint8_t b) { 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) { writeByte(n >> 8); writeByte(n & 0xFF); @@ -333,3 +343,16 @@ void File::writeUint32BE(uint32_t n) { writeUint16BE(n >> 16); 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); + } +} diff --git a/file.h b/file.h index a80b996..9f741d7 100644 --- a/file.h +++ b/file.h @@ -1,7 +1,7 @@ /* * 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__ @@ -32,8 +32,12 @@ struct File { uint32_t readUint32BE(); uint32_t write(const void *ptr, uint32_t size); void writeByte(uint8_t b); + void writeUint16LE(uint16_t n); + void writeUint32LE(uint32_t n); void writeUint16BE(uint16_t n); void writeUint32BE(uint32_t n); }; +void dumpFile(const char *filename, const uint8_t *p, int size); + #endif // FILE_H__ diff --git a/fs.cpp b/fs.cpp index 7621424..bdbf5b3 100644 --- a/fs.cpp +++ b/fs.cpp @@ -1,7 +1,7 @@ /* * 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 diff --git a/fs.h b/fs.h index 84c4b1b..a987b34 100644 --- a/fs.h +++ b/fs.h @@ -1,7 +1,7 @@ /* * 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__ diff --git a/game.cpp b/game.cpp index 3260715..759288d 100644 --- a/game.cpp +++ b/game.cpp @@ -1,7 +1,7 @@ /* * REminiscence - Flashback interpreter - * Copyright (C) 2005-2018 Gregory Montoir (cyx@users.sourceforge.net) + * Copyright (C) 2005-2019 Gregory Montoir (cyx@users.sourceforge.net) */ #include @@ -16,7 +16,7 @@ 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), - _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) { _stateSlot = 1; _inp_demPos = 0; @@ -55,10 +55,11 @@ void Game::run() { break; } - if (!g_options.bypass_protection) { - while (!handleProtectionScreen()); - if (_stub->_pi.quit) { - return; + if (!g_options.bypass_protection && !g_options.use_words_protection && !_res.isMac()) { + while (!handleProtectionScreenShape()) { + if (_stub->_pi.quit) { + return; + } } } @@ -94,6 +95,14 @@ void Game::run() { 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")); while (!_stub->_pi.quit) { if (presentMenu) { @@ -132,9 +141,12 @@ void Game::run() { displayTitleScreenMac(Menu::kMacTitleScreen_Flashback); break; } - if (_stub->_pi.quit) { - break; - } + } + if (_stub->_pi.quit) { + break; + } + if (_stub->hasWidescreen()) { + _stub->clearWidescreen(); } if (_currentLevel == 7) { _vid.fadeOut(); @@ -166,11 +178,6 @@ void Game::run() { _stub->_pi.enter = false; _stub->_pi.space = 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]); _stub->setPaletteEntry(i, &c); } + _vid.setTextPalette(); _stub->setScreenSize(kW, kH); // fill with black _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->updateScreen(0); 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(); if (_stub->_pi.quit) { @@ -249,7 +283,7 @@ void Game::displayTitleScreenMac(int num) { buf.h = _vid._h; buf.x = (_vid._w - w) / 2; buf.y = (_vid._h - h) / 2; - buf.setPixel = Video::MAC_drawBuffer; + buf.setPixel = Video::MAC_setPixel; memset(_vid._frontLayer, 0, _vid._layerSize); _res.MAC_loadTitleImage(num, &buf); for (int i = 0; i < 12; ++i) { @@ -444,6 +478,9 @@ void Game::playCutscene(int id) { _cut._id = id; } if (_cut._id != 0xFFFF) { + if (_stub->hasWidescreen()) { + _stub->enableWidescreen(false); + } _mix.stopMusic(); if (_res._hasSeqData) { int num = 0; @@ -520,6 +557,9 @@ void Game::playCutscene(int id) { _cut.playCredits(); } _mix.stopMusic(); + if (_stub->hasWidescreen()) { + _stub->enableWidescreen(true); + } } } @@ -566,8 +606,7 @@ void Game::drawCurrentInventoryItem() { void Game::showFinalScore() { if (_stub->hasWidescreen()) { - _stub->copyRectLeftBorder(_vid._w, _vid._h, 0); - _stub->copyRectRightBorder(_vid._w, _vid._h, 0); + _stub->clearWidescreen(); } playCutscene(0x49); 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_20_LOAD_GAME), y + 6, 9, colors[2]); _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); _menu.drawString(buf, y + 10, 9, 1); @@ -730,8 +770,7 @@ bool Game::handleConfigPanel() { bool Game::handleContinueAbort() { if (_stub->hasWidescreen()) { - _stub->copyRectLeftBorder(_vid._w, _vid._h, 0); - _stub->copyRectRightBorder(_vid._w, _vid._h, 0); + _stub->clearWidescreen(); } playCutscene(0x48); int timeout = 100; @@ -799,87 +838,6 @@ bool Game::handleContinueAbort() { 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() { if (_printLevelCodeCounter != 0) { --_printLevelCodeCounter; @@ -1006,22 +964,23 @@ void Game::drawStoryTexts() { yPos += 8; } } - MixerChunk chunk; - _res.load_VCE(_textToDisplay, textSpeechSegment++, &chunk.data, &chunk.len); - if (chunk.data) { - _mix.play(&chunk, 32000, Mixer::MAX_VOLUME); + uint8_t *voiceSegmentData = 0; + uint32_t voiceSegmentLen = 0; + _res.load_VCE(_textToDisplay, textSpeechSegment++, &voiceSegmentData, &voiceSegmentLen); + if (voiceSegmentData) { + _mix.play(voiceSegmentData, voiceSegmentLen, 32000, Mixer::MAX_VOLUME); } _vid.updateScreen(); while (!_stub->_pi.backspace && !_stub->_pi.quit) { - if (chunk.data && !_mix.isPlaying(&chunk)) { + if (voiceSegmentData && !_mix.isPlaying(voiceSegmentData)) { break; } inp_update(); _stub->sleep(80); } - if (chunk.data) { + if (voiceSegmentData) { _mix.stopAll(); - free(chunk.data); + free(voiceSegmentData); } _stub->_pi.backspace = false; if (_res._type == kResourceTypeMac) { @@ -1220,7 +1179,7 @@ void Game::drawAnimBuffer(uint8_t stateNum, AnimBufferState *state) { break; case kResourceTypeDOS: 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); } else { 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); } -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) { 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 @@ -1604,6 +1530,7 @@ bool Game::hasLevelMap(int level, int room) const { void Game::loadLevelMap() { debug(DBG_GAME, "Game::loadLevelMap() room=%d", _currentRoom); + bool widescreenUpdated = false; _currentIcon = 0xFF; switch (_res._type) { case kResourceTypeAmiga: @@ -1637,17 +1564,18 @@ void Game::loadLevelMap() { const int leftRoom = _res._ctData[CT_LEFT_ROOM + _currentRoom]; if (leftRoom > 0 && hasLevelMap(_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 { - _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]; if (rightRoom > 0 && hasLevelMap(_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 { - _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); break; @@ -1656,23 +1584,24 @@ void Game::loadLevelMap() { const int leftRoom = _res._ctData[CT_LEFT_ROOM + _currentRoom]; if (leftRoom > 0 && hasLevelMap(_currentLevel, leftRoom)) { _vid.MAC_decodeMap(_currentLevel, leftRoom); - _stub->copyRectLeftBorder(_vid._w, _vid._h, _vid._backLayer); + _stub->copyWidescreenLeft(_vid._w, _vid._h, _vid._backLayer); } else { - _stub->copyRectLeftBorder(_vid._w, _vid._h, 0); + _stub->copyWidescreenLeft(_vid._w, _vid._h, 0); } const int rightRoom = _res._ctData[CT_RIGHT_ROOM + _currentRoom]; if (rightRoom > 0 && hasLevelMap(_currentLevel, rightRoom)) { _vid.MAC_decodeMap(_currentLevel, rightRoom); - _stub->copyRectRightBorder(_vid._w, _vid._h, _vid._backLayer); + _stub->copyWidescreenRight(_vid._w, _vid._h, _vid._backLayer); } else { - _stub->copyRectRightBorder(_vid._w, _vid._h, 0); + _stub->copyWidescreenRight(_vid._w, _vid._h, 0); } + widescreenUpdated = true; } _vid.MAC_decodeMap(_currentLevel, _currentRoom); break; } - if (_stub->hasWidescreen() && _widescreenMode == kWidescreenMirrorRoom) { - _stub->copyRectMirrorBorders(_vid._w, _vid._h, _vid._backLayer); + if (!widescreenUpdated) { + _vid.updateWidescreen(); } } @@ -1737,7 +1666,7 @@ void Game::loadLevelData() { _res.load(lvl->name, Resource::OT_CT); _res.load(lvl->name, Resource::OT_PAL); _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) { _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); } -void Game::playSound(uint8_t sfxId, uint8_t softVol) { - if (sfxId < _res._numSfx) { - SoundFx *sfx = &_res._sfxList[sfxId]; +void Game::playSound(uint8_t num, uint8_t softVol) { + if (num < _res._numSfx) { + SoundFx *sfx = &_res._sfxList[num]; if (sfx->data) { - MixerChunk mc; - mc.data = sfx->data; - mc.len = sfx->len; - _mix.play(&mc, sfx->freq, Mixer::MAX_VOLUME >> softVol); + const int volume = Mixer::MAX_VOLUME >> (2 * softVol); + _mix.play(sfx->data, sfx->len, sfx->freq, volume); } - } else { + } else if (num == 66) { + // open/close inventory (DOS) + } else if (num >= 68 && num <= 75) { // 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 kResourceTypeDOS: { // draw inventory background - int icon_h = 5; - int icon_y = 140; int icon_num = 31; - do { - int icon_x = 56; - int icon_w = 9; - do { - drawIcon(icon_num, icon_x, icon_y, 0xF); + 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; - icon_x += icon_spr_w; - } while (--icon_w); - icon_y += icon_spr_h; - } while (--icon_h); + } + } } if (_res._type == kResourceTypeAmiga) { // draw outline rectangle diff --git a/game.h b/game.h index 0987b01..26e5b62 100644 --- a/game.h +++ b/game.h @@ -1,7 +1,7 @@ /* * 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__ @@ -28,7 +28,7 @@ struct Game { enum { kIngameSaveSlot = 0, kAutoSaveSlot = 255, - kAutoSaveIntervalMs = 30 * 1000 + kAutoSaveIntervalMs = 120 * 1000 }; enum { @@ -54,6 +54,9 @@ struct Game { static const pge_OpcodeProc _pge_opcodeTable[]; static const uint8_t _pge_modKeysTable[]; 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[]; Cutscene _cut; @@ -115,7 +118,6 @@ struct Game { void showFinalScore(); bool handleConfigPanel(); bool handleContinueAbort(); - bool handleProtectionScreen(); void printSaveStateCompleted(); void drawLevelTexts(); void drawStoryTexts(); @@ -127,7 +129,6 @@ struct Game { void drawPiege(AnimBufferState *state); 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 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); int loadMonsterSprites(LivePGE *pge); void playSound(uint8_t sfxId, uint8_t softVol); @@ -135,6 +136,9 @@ struct Game { void changeLevel(); void handleInventory(); + // protection + bool handleProtectionScreenShape(); + bool handleProtectionScreenWords(); // pieges bool _pge_playAnimSound; diff --git a/graphics.cpp b/graphics.cpp index cf10620..23ec31a 100644 --- a/graphics.cpp +++ b/graphics.cpp @@ -1,7 +1,7 @@ /* * 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" diff --git a/graphics.h b/graphics.h index 69973ee..a836138 100644 --- a/graphics.h +++ b/graphics.h @@ -1,7 +1,7 @@ /* * 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__ diff --git a/intern.h b/intern.h index 799cedd..458217c 100644 --- a/intern.h +++ b/intern.h @@ -1,7 +1,7 @@ /* * 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__ @@ -46,6 +46,17 @@ inline int16_t ADDC_S16(int a, int b) { 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 inline void SWAP(T &a, T &b) { T tmp = a; @@ -106,6 +117,7 @@ enum WidescreenMode { kWidescreenNone, kWidescreenAdjacentRooms, kWidescreenMirrorRoom, + kWidescreenBlur, }; struct Options { @@ -113,13 +125,17 @@ struct Options { bool enable_password_menu; bool enable_language_selection; bool fade_out_palette; - bool use_tiledata; + bool use_tile_data; bool use_text_cutscenes; bool use_seq_cutscenes; + bool use_words_protection; + bool use_white_tshirt; bool play_asc_cutscene; bool play_caillou_cutscene; bool play_metro_cutscene; bool play_serrure_cutscene; + bool play_carte_cutscene; + bool play_gamesaved_sound; }; struct Color { @@ -155,7 +171,7 @@ struct InitPGE { int16_t pos_y; uint16_t obj_node_number; uint16_t life; - int16_t counter_values[4]; + int16_t counter_values[4]; // messages uint8_t object_type; uint8_t init_room; uint8_t room_location; @@ -177,7 +193,7 @@ struct LivePGE { uint8_t anim_seq; uint8_t room_location; int16_t life; - int16_t counter_value; + int16_t counter_value; // msg uint8_t collision_slot; uint8_t next_inventory_PGE; uint8_t current_inventory_PGE; @@ -267,6 +283,7 @@ struct SoundFx { uint16_t len; uint16_t freq; uint8_t *data; + int8_t peak; }; extern Options g_options; diff --git a/main.cpp b/main.cpp index 34cf66e..02e8b66 100644 --- a/main.cpp +++ b/main.cpp @@ -1,7 +1,7 @@ /* * REminiscence - Flashback interpreter - * Copyright (C) 2005-2018 Gregory Montoir (cyx@users.sourceforge.net) + * Copyright (C) 2005-2019 Gregory Montoir (cyx@users.sourceforge.net) */ #include @@ -22,7 +22,7 @@ static const char *USAGE = " --savepath=PATH Path to save files (default '.')\n" " --levelnum=NUM Start to level, bypass introduction\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" " --language=LANG Language (fr,en,de,sp,it,jp)\n" " --autosave Save game state automatically\n" @@ -36,6 +36,7 @@ static int detectVersion(FileSystem *fs) { } table[] = { { "DEMO_UK.ABA", kResourceTypeDOS, "DOS (Demo)" }, { "INTRO.SEQ", kResourceTypeDOS, "DOS CD" }, + { "MENU1SSI.MAP", kResourceTypeDOS, "DOS SSI" }, { "LEVEL1.MAP", kResourceTypeDOS, "DOS" }, { "LEVEL1.BNQ", kResourceTypeDOS, "DOS (Demo)" }, { "LEVEL1.LEV", kResourceTypeAmiga, "Amiga" }, @@ -90,10 +91,14 @@ static void initOptions() { g_options.fade_out_palette = true; g_options.use_text_cutscenes = false; 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_caillou_cutscene = false; g_options.play_metro_cutscene = false; g_options.play_serrure_cutscene = false; + g_options.play_carte_cutscene = false; + g_options.play_gamesaved_sound = false; // read configuration file struct { const char *name; @@ -103,13 +108,17 @@ static void initOptions() { { "enable_password_menu", &g_options.enable_password_menu }, { "enable_language_selection", &g_options.enable_language_selection }, { "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_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_caillou_cutscene", &g_options.play_caillou_cutscene }, { "play_metro_cutscene", &g_options.play_metro_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 } }; static const char *filename = "rs.cfg"; @@ -147,7 +156,7 @@ static void initOptions() { } static void parseScaler(char *name, ScalerParameters *scalerParameters) { - struct { + static const struct { const char *name; int type; const Scaler *scaler; @@ -158,7 +167,7 @@ static void parseScaler(char *name, ScalerParameters *scalerParameters) { #ifdef USE_STATIC_SCALER { "nearest", kScalerTypeInternal, &scaler_nearest }, { "tv2x", kScalerTypeInternal, &scaler_tv2x }, - { "xbrz", kScalerTypeInternal, &scaler_xbrz }, + { "xbr", kScalerTypeInternal, &scaler_xbr }, #endif { 0, -1 } }; @@ -200,6 +209,7 @@ static WidescreenMode parseWidescreen(const char *mode) { } modes[] = { { "adjacent", kWidescreenAdjacentRooms }, { "mirror", kWidescreenMirrorRoom }, + { "blur", kWidescreenBlur }, { 0, kWidescreenNone }, }; for (int i = 0; modes[i].name; ++i) { @@ -207,8 +217,8 @@ static WidescreenMode parseWidescreen(const char *mode) { return modes[i].mode; } } - warning("Unhandled widecreen mode '%s', defaults to adjacent rooms", mode); - return kWidescreenAdjacentRooms; // default value + warning("Unhandled widecreen mode '%s', defaults to 16:9 blur", mode); + return kWidescreenBlur; } 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; SystemStub *stub = SystemStub_SDL_create(); 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(); delete g; stub->destroy(); diff --git a/menu.cpp b/menu.cpp index 4d6655f..a7c6e31 100644 --- a/menu.cpp +++ b/menu.cpp @@ -1,7 +1,7 @@ /* * 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" @@ -89,17 +89,20 @@ void Menu::drawString2(const char *str, int16_t y, int16_t x) { void Menu::loadPicture(const char *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); for (int i = 0; i < 4; ++i) { - for (int y = 0; y < 224; ++y) { - for (int x = 0; x < 64; ++x) { - _vid->_frontLayer[i + x * 4 + 256 * y] = _res->_scratchBuffer[0x3800 * i + x + 64 * y]; + for (int y = 0; y < kPictureH; ++y) { + for (int x = 0; x < kPictureW / 4; ++x) { + _vid->_frontLayer[i + x * 4 + kPictureW * y] = _res->_scratchBuffer[0x3800 * i + x + 64 * y]; } } } memcpy(_vid->_backLayer, _vid->_frontLayer, _vid->_layerSize); _res->load_PAL_menu(prefix, _res->_scratchBuffer); _stub->setPalette(_res->_scratchBuffer, 256); + _vid->updateWidescreen(); } void Menu::handleInfoScreen() { @@ -386,7 +389,7 @@ void Menu::handleTitleScreen() { } } - while (!quitLoop) { + while (!quitLoop && !_stub->_pi.quit) { int selectedItem = -1; int previousLanguage = currentLanguage; @@ -497,9 +500,6 @@ void Menu::handleTitleScreen() { _vid->updateScreen(); _stub->sleep(EVENTS_DELAY); _stub->processEvents(); - if (_stub->_pi.quit) { - break; - } } } diff --git a/menu.h b/menu.h index 27c236a..beac9a1 100644 --- a/menu.h +++ b/menu.h @@ -1,7 +1,7 @@ /* * 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__ diff --git a/mixer.cpp b/mixer.cpp index 0f06185..2bbd7af 100644 --- a/mixer.cpp +++ b/mixer.cpp @@ -1,7 +1,7 @@ /* * 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" @@ -9,8 +9,9 @@ #include "util.h" 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; + _backgroundMusicType = MT_NONE; } void Mixer::init() { @@ -32,14 +33,14 @@ void Mixer::setPremixHook(PremixHook premixHook, void *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); LockAudioStack las(_stub); MixerChannel *ch = 0; for (int i = 0; i < NUM_CHANNELS; ++i) { MixerChannel *cur = &_channels[i]; if (cur->active) { - if (cur->chunk.data == mc->data) { + if (cur->chunk.data == data) { cur->chunkPos = 0; return; } @@ -51,18 +52,19 @@ void Mixer::play(const MixerChunk *mc, uint16_t freq, uint8_t volume) { if (ch) { ch->active = true; ch->volume = volume; - ch->chunk = *mc; + ch->chunk.data = data; + ch->chunk.len = len; ch->chunkPos = 0; 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"); LockAudioStack las(_stub); for (int i = 0; i < NUM_CHANNELS; ++i) { const MixerChannel *ch = &_channels[i]; - if (ch->active && ch->chunk.data == mc->data) { + if (ch->active && ch->chunk.data == data) { return true; } } @@ -93,15 +95,20 @@ void Mixer::playMusic(int num) { _musicTrack = num; return; } + if (_cpc.playTrack(num - MUSIC_TRACK)) { + _backgroundMusicType = _musicType = MT_CPC; + _musicTrack = num; + return; + } } if (num == 1) { // menu screen - if (_ogg.playTrack(2)) { - _musicType = MT_OGG; + if (_cpc.playTrack(2) || _ogg.playTrack(2)) { + _backgroundMusicType = _musicType = MT_OGG; _musicTrack = 2; 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; } if (isMusicSfx(num)) { // level action sequence @@ -131,14 +138,29 @@ void Mixer::stopMusic() { case MT_SFX: _sfx.stop(); break; + case MT_CPC: + _cpc.pauseTrack(); + break; } _musicType = MT_NONE; if (_musicTrack != -1) { - _ogg.resumeTrack(); - _musicType = MT_OGG; + switch (_backgroundMusicType) { + 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 int prev = 0; for (int i = 0; i < len; ++i) { @@ -163,13 +185,15 @@ void Mixer::mix(int16_t *out, int len) { ch->active = false; break; } - const int sample = ch->chunk.getPCM(ch->chunkPos >> FRAC_BITS); - out[pos] = ADDC_S16(out[pos], (sample * ch->volume / Mixer::MAX_VOLUME) << 8); + const int sample = ch->chunk.getPCM(ch->chunkPos >> FRAC_BITS) * ch->volume / Mixer::MAX_VOLUME; + out[pos] = ADDC_S16(out[pos], S8_to_S16(sample)); ch->chunkPos += ch->chunkInc; } } } - nr(out, len); + if (kUseNr) { + nr(out, len); + } } void Mixer::mixCallback(void *param, int16_t *buf, int len) { diff --git a/mixer.h b/mixer.h index c633659..63d0834 100644 --- a/mixer.h +++ b/mixer.h @@ -1,19 +1,20 @@ /* * 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__ #define MIXER_H__ #include "intern.h" +#include "cpc_player.h" #include "mod_player.h" #include "ogg_player.h" #include "sfx_player.h" struct MixerChunk { - uint8_t *data; + const uint8_t *data; uint32_t len; MixerChunk() @@ -49,6 +50,7 @@ struct Mixer { MT_MOD, MT_OGG, MT_SFX, + MT_CPC, }; enum { @@ -63,7 +65,9 @@ struct Mixer { MixerChannel _channels[NUM_CHANNELS]; PremixHook _premixHook; void *_premixHookData; + MusicType _backgroundMusicType; MusicType _musicType; + CpcPlayer _cpc; ModPlayer _mod; OggPlayer _ogg; SfxPlayer _sfx; @@ -73,8 +77,8 @@ struct Mixer { void init(); void free(); void setPremixHook(PremixHook premixHook, void *userData); - void play(const MixerChunk *mc, uint16_t freq, uint8_t volume); - bool isPlaying(const MixerChunk *mc) const; + void play(const uint8_t *data, uint32_t len, uint16_t freq, uint8_t volume); + bool isPlaying(const uint8_t *data) const; uint32_t getSampleRate() const; void stopAll(); void playMusic(int num); diff --git a/mod_player.cpp b/mod_player.cpp index 189630d..7ee290c 100644 --- a/mod_player.cpp +++ b/mod_player.cpp @@ -1,7 +1,7 @@ /* * 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" @@ -203,17 +203,20 @@ bool ModPlayer_impl::load(File *f) { f->read(_modInfo.patternOrderTable, NUM_PATTERNS); f->readUint32BE(); // 'M.K.', Protracker, 4 channels - uint16_t n = 0; + uint8_t n = 0; for (int i = 0; i < NUM_PATTERNS; ++i) { if (_modInfo.patternOrderTable[i] != 0) { n = MAX(n, _modInfo.patternOrderTable[i]); } } debug(DBG_MOD, "numPatterns=%d",n + 1); - n = (n + 1) * 64 * 4 * 4; // 64 lines of 4 notes per channel - _modInfo.patternsTable = (uint8_t *)malloc(n); - assert(_modInfo.patternsTable); - f->read(_modInfo.patternsTable, n); + const int patternsSize = (n + 1) * 64 * 4 * 4; // 64 lines of 4 notes per channel + _modInfo.patternsTable = (uint8_t *)malloc(patternsSize); + if (!_modInfo.patternsTable) { + 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) { SampleInfo *si = &_modInfo.samples[s]; @@ -612,8 +615,8 @@ void ModPlayer_impl::mixSamples(int16_t *buf, int samplesLen) { curLen = 0; } while (count--) { - const int out = si->getPCM(pos >> FRAC_BITS); - *mixbuf = ADDC_S16(*mixbuf, (out * tk->volume / 64) << 8); + const int out = si->getPCM(pos >> FRAC_BITS) * tk->volume / 64; + *mixbuf = ADDC_S16(*mixbuf, S8_to_S16(out)); ++mixbuf; pos += deltaPos; } @@ -676,7 +679,7 @@ void ModPlayer::stop() { if (_playing) { _mix->setPremixHook(0, 0); _impl->unload(); - _playing = true; + _playing = false; } } diff --git a/mod_player.h b/mod_player.h index 9862b37..19b0a15 100644 --- a/mod_player.h +++ b/mod_player.h @@ -1,7 +1,7 @@ /* * 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__ diff --git a/ogg_player.cpp b/ogg_player.cpp index 45a1f0e..9c47b1d 100644 --- a/ogg_player.cpp +++ b/ogg_player.cpp @@ -1,7 +1,7 @@ /* * 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 diff --git a/ogg_player.h b/ogg_player.h index d06e422..5d38bcb 100644 --- a/ogg_player.h +++ b/ogg_player.h @@ -1,7 +1,7 @@ /* * 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__ diff --git a/piege.cpp b/piege.cpp index 9120b9e..ce5e46f 100644 --- a/piege.cpp +++ b/piege.cpp @@ -1,7 +1,7 @@ /* * 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" @@ -1429,6 +1429,9 @@ int Game::pge_op_setCollisionState2(ObjectOpcodeArgs *args) { int Game::pge_op_saveState(ObjectOpcodeArgs *args) { _saveStateCompleted = true; _validSaveState = saveGameState(kIngameSaveSlot); + if (_validSaveState and g_options.play_gamesaved_sound) { + _mix.play(Resource::_gameSavedSoundData, Resource::_gameSavedSoundLen, 8000, Mixer::MAX_VOLUME); + } return 0xFFFF; } @@ -1877,8 +1880,11 @@ int Game::pge_op_setPiegePosModX(ObjectOpcodeArgs *args) { int Game::pge_op_changeRoom(ObjectOpcodeArgs *args) { InitPGE *init_pge_1 = args->pge->init_PGE; assert(args->a >= 0 && args->a < 3); - int16_t _ax = init_pge_1->counter_values[args->a]; - int16_t _bx = init_pge_1->counter_values[args->a + 1]; + const int16_t _ax = init_pge_1->counter_values[args->a]; + 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_2 = &_pgeLive[_ax]; int8_t pge_room = live_pge_1->room_location; diff --git a/protection.cpp b/protection.cpp new file mode 100644 index 0000000..ff440cc --- /dev/null +++ b/protection.cpp @@ -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; +} + diff --git a/resource.cpp b/resource.cpp index 0768652..0cfbb25 100644 --- a/resource.cpp +++ b/resource.cpp @@ -1,7 +1,7 @@ /* * 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" @@ -53,6 +53,7 @@ Resource::~Resource() { free(_sfxList); free(_bankData); delete _aba; + delete _mac; } void Resource::init() { @@ -138,10 +139,6 @@ void Resource::load_DEM(const char *filename) { void Resource::load_FIB(const char *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); File f; if (f.open(_entryName, "rb", _fs)) { @@ -150,37 +147,48 @@ void Resource::load_FIB(const char *fileName) { if (!_sfxList) { error("Unable to allocate SoundFx table"); } - int i; - for (i = 0; i < _numSfx; ++i) { + for (int i = 0; i < _numSfx; ++i) { SoundFx *sfx = &_sfxList[i]; sfx->offset = f.readUint32LE(); sfx->len = f.readUint16LE(); sfx->freq = 6000; sfx->data = 0; } - for (i = 0; i < _numSfx; ++i) { + for (int i = 0; i < _numSfx; ++i) { SoundFx *sfx = &_sfxList[i]; if (sfx->len == 0) { continue; } 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) { error("Unable to allocate SoundFx data buffer"); } 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; - uint16_t sz = sfx->len - 1; - while (sz--) { - uint8_t d = f.readByte(); - c += fibonacciTable[d >> 4]; - *data++ = c; - c += fibonacciTable[d & 15]; - *data++ = c; + sfx->peak = ABS(c); + + for (int j = 1; j < sfx->len; ++j) { + const uint8_t d = f.readByte(); + + c += codeToDelta[d >> 4]; + *data++ = CLIP(c, -128, 127); + 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()) { 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() { _numSfx = NUM_SFXS; _sfxList = (SoundFx *)calloc(_numSfx, sizeof(SoundFx)); @@ -207,6 +228,7 @@ void Resource::load_SPL_demo() { sfx->offset = 0; sfx->len = size; 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"); } f.read(tmp, size); - if (!delphine_unpack(_scratchBuffer, kScratchBufferSize, tmp, size)) { + if (!bytekiller_unpack(_scratchBuffer, kScratchBufferSize, tmp, size)) { error("Bad CRC for %s", fileName); } free(tmp); @@ -668,7 +690,7 @@ void Resource::load(const char *objName, int objType, const char *ext) { _pal = dat; break; 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); } free(dat); @@ -736,7 +758,7 @@ void Resource::load_CT(File *pf) { error("Unable to allocate CT buffer"); } else { 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"); } free(tmp); @@ -936,7 +958,7 @@ void Resource::load_OBC(File *f) { } f->seek(4); 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"); } free(packedData); @@ -1153,7 +1175,7 @@ void Resource::load_CMP(File *pf) { } if (data[0].packedSize == data[0].size) { 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"); } _cmd = (uint8_t *)malloc(data[1].size); @@ -1162,7 +1184,7 @@ void Resource::load_CMP(File *pf) { } if (data[1].packedSize == data[1].size) { 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"); } free(tmp); @@ -1229,15 +1251,18 @@ void Resource::load_SPL(File *f) { } debug(DBG_RES, "sfx=%d size=%d", i, size); 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].len = size; _sfxList[i].freq = kPaulaFreq / 650; _sfxList[i].data = (uint8_t *)malloc(size); - assert(_sfxList[i].data); - f->read(_sfxList[i].data, size); - } else { - f->seek(offset + size); + if (_sfxList[i].data) { + f->read(_sfxList[i].data, size); + _sfxList[i].len = size; + normalizeSPL(&_sfxList[i]); + } } offset += size; } @@ -1278,7 +1303,7 @@ void Resource::load_SGD(File *f) { if (!_sgd) { 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"); } free(tmp); @@ -1310,12 +1335,12 @@ void Resource::load_SPM(File *f) { if (!_spr1) { 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"); } } else { 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"); } } @@ -1391,7 +1416,7 @@ uint8_t *Resource::loadBankData(uint16_t num) { } else { assert(dataOffset > 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); } } diff --git a/resource.h b/resource.h index de063e4..c11dcee 100644 --- a/resource.h +++ b/resource.h @@ -1,7 +1,7 @@ /* * 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__ @@ -119,6 +119,8 @@ struct Resource { static const uint16_t _voicesOffsetsTable[]; static const uint32_t _spmOffsetsTable[]; static const char *_splNames[]; + static const uint8_t _gameSavedSoundData[]; + static const uint16_t _gameSavedSoundLen; FileSystem *_fs; ResourceType _type; diff --git a/resource_aba.cpp b/resource_aba.cpp index b76ef21..5039589 100644 --- a/resource_aba.cpp +++ b/resource_aba.cpp @@ -74,7 +74,7 @@ uint8_t *ResourceAba::loadEntry(const char *name, uint32_t *size) { free(tmp); 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) { error("Bad CRC for '%s'", name); } diff --git a/rs.cfg b/rs.cfg index 3972c13..42f3cd3 100644 --- a/rs.cfg +++ b/rs.cfg @@ -1,4 +1,4 @@ -# display copy protection shapes +# display copy protection (shapes or words lookup) bypass_protection=true # use original password level selection menu screen @@ -11,22 +11,34 @@ enable_language_selection=true fade_out_palette=false # 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 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 +# 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 -play_asc_cutscene=false +play_asc_cutscene=true # enable playback of 'CAILLOU-F.SET' cutscene play_caillou_cutscene=true +# enable playback of 'CARTE' cutscene +play_carte_cutscene=true + # enable playback of 'METRO' cutscene play_metro_cutscene=false # enable playback of 'SERRURE' cutscene play_serrure_cutscene=true + +# play 'Game saved' sample when saving with level checkpoints (as in the 3DO version) +play_gamesaved_sound=true diff --git a/scaler.cpp b/scaler.cpp index 700190a..8ecb14b 100644 --- a/scaler.cpp +++ b/scaler.cpp @@ -1,7 +1,7 @@ /* * 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" diff --git a/scaler.h b/scaler.h index 46f6603..1a92b38 100644 --- a/scaler.h +++ b/scaler.h @@ -1,7 +1,7 @@ /* * 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__ @@ -34,7 +34,7 @@ const Scaler *findScaler(const char *name); #ifdef USE_STATIC_SCALER extern const Scaler scaler_nearest; extern const Scaler scaler_tv2x; -extern const Scaler scaler_xbrz; +extern const Scaler scaler_xbr; #endif #endif // SCALER_H__ diff --git a/screenshot.cpp b/screenshot.cpp index ba33f36..29d1937 100644 --- a/screenshot.cpp +++ b/screenshot.cpp @@ -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); + } + } + } +} diff --git a/screenshot.h b/screenshot.h index c3257d6..b569b11 100644 --- a/screenshot.h +++ b/screenshot.h @@ -5,5 +5,6 @@ #include 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 diff --git a/seq_player.cpp b/seq_player.cpp index 5349928..4d013a3 100644 --- a/seq_player.cpp +++ b/seq_player.cpp @@ -1,7 +1,7 @@ /* * 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" @@ -226,10 +226,8 @@ SeqPlayer::~SeqPlayer() { void SeqPlayer::play(File *f) { if (_demux.open(f)) { - Color pal[256]; - for (int i = 0; i < 256; ++i) { - _stub->getPaletteEntry(i, &pal[i]); - } + uint8_t palette[256 * 3]; + _stub->getPalette(palette, 256); _mix->setPremixHook(mixCallback, this); memset(_buf, 0, 256 * 224); bool clearScreen = true; @@ -315,9 +313,8 @@ void SeqPlayer::play(File *f) { _stub->sleep(diff); } } - for (int i = 0; i < 256; ++i) { - _stub->setPaletteEntry(i, &pal[i]); - } + // restore level palette + _stub->setPalette(palette, 256); _mix->setPremixHook(0, 0); _demux.close(); // flush sound queue diff --git a/seq_player.h b/seq_player.h index 93eb1a8..dc8f8f8 100644 --- a/seq_player.h +++ b/seq_player.h @@ -1,7 +1,7 @@ /* * 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__ diff --git a/sfx_player.cpp b/sfx_player.cpp index 8bf3ac2..34c7312 100644 --- a/sfx_player.cpp +++ b/sfx_player.cpp @@ -1,13 +1,35 @@ /* * 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 "sfx_player.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) : _mod(0), _playing(false), _mix(mixer) { } @@ -15,20 +37,23 @@ SfxPlayer::SfxPlayer(Mixer *mixer) void SfxPlayer::play(uint8_t num) { debug(DBG_SFX, "SfxPlayer::play(%d)", num); if (!_playing) { - if (num >= 68 && num <= 75) { - static const Module *modTable[] = { - &_module68, &_module68, &_module70, &_module70, - &_module72, &_module73, &_module74, &_module75 - }; - _mod = modTable[num - 68]; - _curOrder = 0; - _numOrders = READ_BE_UINT16(_mod->moduleData); - _orderDelay = 0; - _modData = _mod->moduleData + 0x22; - memset(_samples, 0, sizeof(_samples)); - _samplesLeft = 0; - _mix->setPremixHook(mixCallback, this); - _playing = true; + assert(num >= 68 && num <= 75); + static const Module *modTable[] = { + &_module68, &_module68, &_module70, &_module70, + &_module72, &_module73, &_module74, &_module75 + }; + _mod = modTable[num - 68]; + _curOrder = 0; + _numOrders = READ_BE_UINT16(_mod->moduleData); + _orderDelay = 0; + _modData = _mod->moduleData + 0x22; + memset(_samples, 0, sizeof(_samples)); + _samplesLeft = 0; + _mix->setPremixHook(mixCallback, this); + _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; } while (count--) { - const int out = si->getPCM(pos >> FRAC_BITS); - *mixbuf = ADDC_S16(*mixbuf, (out * si->vol / 64) << 8); + const int out = si->getPCM(pos >> FRAC_BITS) * si->vol / kMasterVolume; + *mixbuf = ADDC_S16(*mixbuf, S8_to_S16(out)); ++mixbuf; pos += deltaPos; } @@ -153,6 +178,9 @@ bool SfxPlayer::mix(int16_t *buf, int len) { _samplesLeft -= count; len -= count; mixSamples(buf, count); + if (kLowPassFilter) { + butterworth(buf, count); + } buf += count; } } diff --git a/sfx_player.h b/sfx_player.h index 5929ff0..1bc8bce 100644 --- a/sfx_player.h +++ b/sfx_player.h @@ -1,7 +1,7 @@ /* * 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__ diff --git a/staticres.cpp b/staticres.cpp index 2c2558f..3b1bd49 100644 --- a/staticres.cpp +++ b/staticres.cpp @@ -1,7 +1,7 @@ /* * 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" @@ -30,7 +30,7 @@ const Cutscene::OpcodeStub Cutscene::_opcodeTable[] = { &Cutscene::op_handleKeys }; -const char *Cutscene::_namesTable[] = { +const char *Cutscene::_namesTableDOS[] = { "DEBUT", "OBJET", "CARTE", @@ -68,7 +68,7 @@ const char *Cutscene::_namesTable[] = { "LOGOSSSI", }; -const uint16_t Cutscene::_offsetsTable[] = { +const uint16_t Cutscene::_offsetsTableDOS[] = { 0x0000, 0x0000, 0x0001, 0x0003, 0x0001, 0x0004, 0xFFFF, 0x0000, 0x0001, 0x0002, 0x0003, 0x0000, 0x0004, 0x0000, 0xFFFF, 0x0100, 0xFFFF, 0x0000, 0x0006, 0x0000, 0x0001, 0x0001, 0xFFFF, 0x0000, 0xFFFF, 0x0200, 0x8007, 0x0000, 0x0003, 0x0001, @@ -87,6 +87,19 @@ const uint16_t Cutscene::_offsetsTable[] = { 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[] = { 1, 32, 0, /* HOLOCUBE */ 6, 33, 0, /* CHUTE2 */ @@ -2928,7 +2941,7 @@ const char *Resource::_splNames[] = { "touche.spl", "coup", "chenille.spl", - "robot.spl", + "robot", "tombe.spl", "porte_ferme.spl", "canon_down", @@ -2972,6 +2985,512 @@ const char *Resource::_splNames[] = { 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[] = { /* 0x00 */ 0, @@ -3214,6 +3733,174 @@ const uint8_t Game::_protectionCodeData[] = { 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[] = { // DOS 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 }; -const char *Menu::_levelNames[] { +const char *Menu::_levelNames[] = { "Titan / The Jungle", "Titan / New Washington", "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, 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, - 0x82, 0x83, 0x84, 0x86, 0x86 + 0x82, 0x83, 0x84, 0x86 }; 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, 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, - 0x08, 0x06, 0x04, 0x04, 0x06, 0x0C, 0x0A, 0x02, 0x02 + 0x08, 0x06, 0x04, 0x04, 0x06, 0x0C, 0x0A, 0x02 }; 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, 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, - 0x16, 0x18, 0x11, 0x08, 0x08 + 0x16, 0x18, 0x11, 0x08 }; 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, 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, 0x0A, 0x0E, 0x14, 0x14, 0x0C, 0x0F, 0x0B, 0x0B, 0x11, 0x13, 0x10, 0x0C, 0x0B, 0x0F, 0x10, - 0x10 + 0x0F, 0x0A, 0x0E, 0x14, 0x14, 0x0C, 0x0F, 0x0B, 0x0B, 0x11, 0x13, 0x10, 0x0C, 0x0B, 0x0F, 0x10 }; 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, 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, - 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[] = { @@ -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, 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, - 0x15, 0x19, 0x1F, 0x27, 0x34, 0x3F, 0x3F + 0x15, 0x19, 0x1F, 0x27, 0x34, 0x3F }; 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, 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, - 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[] = { - 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 = { diff --git a/systemstub.h b/systemstub.h index 0b70c4e..20dd8de 100644 --- a/systemstub.h +++ b/systemstub.h @@ -1,7 +1,7 @@ /* * 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__ @@ -55,20 +55,24 @@ struct 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 bool hasWidescreen() const = 0; virtual void setScreenSize(int w, int h) = 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 getPaletteEntry(int i, Color *c) = 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 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 copyRectRightBorder(int w, int h, const uint8_t *buf) = 0; - virtual void copyRectMirrorBorders(int w, int h, const uint8_t *buf) = 0; + virtual void copyWidescreenLeft(int w, int h, const uint8_t *buf) = 0; + virtual void copyWidescreenRight(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 updateScreen(int shakeOffset) = 0; diff --git a/systemstub_sdl.cpp b/systemstub_sdl.cpp index fab6537..8301a0d 100644 --- a/systemstub_sdl.cpp +++ b/systemstub_sdl.cpp @@ -1,7 +1,7 @@ /* * REminiscence - Flashback interpreter - * Copyright (C) 2005-2018 Gregory Montoir (cyx@users.sourceforge.net) + * Copyright (C) 2005-2019 Gregory Montoir (cyx@users.sourceforge.net) */ #include @@ -49,24 +49,29 @@ struct SystemStub_SDL : SystemStub { ScalerType _scalerType; const Scaler *_scaler; int _scaleFactor; - bool _widescreen; - SDL_Texture *_wideTexture; + int _widescreenMode; + SDL_Texture *_widescreenTexture; int _wideMargin; + bool _enableWidescreen; 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 bool hasWidescreen() const; virtual void setScreenSize(int w, int h); 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 getPaletteEntry(int i, Color *c); virtual void setOverscanColor(int i); 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 copyRectLeftBorder(int w, int h, const uint8_t *buf); - virtual void copyRectRightBorder(int w, int h, const uint8_t *buf); - virtual void copyRectMirrorBorders(int w, int h, const uint8_t *buf); + virtual void copyWidescreenLeft(int w, int h, const uint8_t *buf); + virtual void copyWidescreenRight(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 updateScreen(int shakeOffset); virtual void processEvents(); @@ -91,7 +96,7 @@ SystemStub *SystemStub_SDL_create() { 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_ShowCursor(SDL_DISABLE); _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(_darkPalette, 0, sizeof(_darkPalette)); _screenW = _screenH = 0; - _widescreen = widescreen; - _wideTexture = 0; + _widescreenMode = widescreenMode; + _widescreenTexture = 0; _wideMargin = 0; + _enableWidescreen = false; setScreenSize(w, h); _joystick = 0; _controller = 0; @@ -149,7 +155,7 @@ void SystemStub_SDL::destroy() { } bool SystemStub_SDL::hasWidescreen() const { - return _widescreen; + return _widescreenMode != kWidescreenNone; } 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) { 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); uint32_t *rgb = (uint32_t *)malloc(w * h * sizeof(uint32_t)); if (rgb) { @@ -279,12 +293,12 @@ void SystemStub_SDL::copyRectLeftBorder(int w, int h, const uint8_t *buf) { r.y = 0; r.w = _wideMargin; 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); } } -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); uint32_t *rgb = (uint32_t *)malloc(w * h * sizeof(uint32_t)); if (rgb) { @@ -304,12 +318,12 @@ void SystemStub_SDL::copyRectRightBorder(int w, int h, const uint8_t *buf) { r.y = 0; r.w = _wideMargin; 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); } } -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); uint32_t *rgb = (uint32_t *)malloc(w * h * sizeof(uint32_t)); if (rgb) { @@ -318,7 +332,7 @@ void SystemStub_SDL::copyRectMirrorBorders(int w, int h, const uint8_t *buf) { } void *dst = 0; int pitch = 0; - if (SDL_LockTexture(_wideTexture, 0, &dst, &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) { @@ -332,12 +346,64 @@ void SystemStub_SDL::copyRectMirrorBorders(int w, int h, const uint8_t *buf) { } p += pitch / sizeof(uint32_t); } - SDL_UnlockTexture(_wideTexture); + SDL_UnlockTexture(_widescreenTexture); } 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() { _fadeOnUpdateScreen = true; } @@ -355,9 +421,11 @@ void SystemStub_SDL::updateScreen(int shakeOffset) { SDL_UpdateTexture(_texture, 0, _screenBuffer, _screenW * sizeof(uint32_t)); } SDL_RenderClear(_renderer); - if (_widescreen) { - // borders / background screen - SDL_RenderCopy(_renderer, _wideTexture, 0, 0); + if (_widescreenMode != kWidescreenNone) { + if (_enableWidescreen) { + // borders / background screen + SDL_RenderCopy(_renderer, _widescreenTexture, 0, 0); + } // game screen SDL_Rect r; r.y = shakeOffset * _scaleFactor; @@ -756,6 +824,10 @@ void SystemStub_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() { _texW = _screenW; _texH = _screenH; @@ -781,7 +853,15 @@ void SystemStub_SDL::prepareGraphics() { } else { 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; } _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); SDL_RenderSetLogicalSize(_renderer, windowW, windowH); _texture = SDL_CreateTexture(_renderer, kPixelFormat, SDL_TEXTUREACCESS_STREAMING, _texW, _texH); - if (_widescreen) { - const int w = _screenH * 16 / 9; + if (_widescreenMode != kWidescreenNone) { + // 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; - _wideTexture = SDL_CreateTexture(_renderer, kPixelFormat, SDL_TEXTUREACCESS_STREAMING, w, h); - clearTexture(_wideTexture, _screenH, _fmt); + _widescreenTexture = SDL_CreateTexture(_renderer, kPixelFormat, SDL_TEXTUREACCESS_STREAMING, w, h); + clearTexture(_widescreenTexture, _screenH, _fmt); // left and right borders _wideMargin = (w - _screenW) / 2; @@ -809,9 +891,9 @@ void SystemStub_SDL::cleanupGraphics() { SDL_DestroyTexture(_texture); _texture = 0; } - if (_wideTexture) { - SDL_DestroyTexture(_wideTexture); - _wideTexture = 0; + if (_widescreenTexture) { + SDL_DestroyTexture(_widescreenTexture); + _widescreenTexture = 0; } if (_renderer) { SDL_DestroyRenderer(_renderer); @@ -859,7 +941,7 @@ void SystemStub_SDL::changeScaler(int scaler) { break; case 5: scalerParameters.type = kScalerTypeInternal; - scalerParameters.scaler = &scaler_xbrz; + scalerParameters.scaler = &scaler_xbr; break; #endif default: @@ -868,13 +950,13 @@ void SystemStub_SDL::changeScaler(int scaler) { if (_scalerType != scalerParameters.type || scalerParameters.scaler != _scaler) { _scalerType = scalerParameters.type; _scaler = scalerParameters.scaler; - const int scaleFactor = CLIP(_scaleFactor, _scaler->factorMin, _scaler->factorMax); - // only recreate the window if dimensions actually changed - if (scaleFactor != _scaleFactor) { - cleanupGraphics(); - _scaleFactor = scaleFactor; - prepareGraphics(); + if (_scalerType == kScalerTypeInternal || _scalerType == kScalerTypeExternal) { + _scaleFactor = CLIP(_scaleFactor, _scaler->factorMin, _scaler->factorMax); + } else { + _scaleFactor = 1; } + cleanupGraphics(); + prepareGraphics(); } } diff --git a/unpack.cpp b/unpack.cpp index 44fe0bc..ce93402 100644 --- a/unpack.cpp +++ b/unpack.cpp @@ -1,7 +1,7 @@ /* * 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" @@ -18,7 +18,7 @@ struct UnpackCtx { static bool nextBit(UnpackCtx *uc) { bool carry = (uc->bits & 1) != 0; uc->bits >>= 1; - if (uc->bits == 0) { + if (uc->bits == 0) { // getnextlwd uc->bits = READ_BE_UINT32(uc->src); uc->src -= 4; uc->crc ^= uc->bits; carry = (uc->bits & 1) != 0; @@ -27,7 +27,7 @@ static bool nextBit(UnpackCtx *uc) { return carry; } -static int getBits(UnpackCtx *uc, int count) { +static int getBits(UnpackCtx *uc, int count) { // rdd1bits int bits = 0; for (int i = 0; i < count; ++i) { bits <<= 1; @@ -38,7 +38,7 @@ static int getBits(UnpackCtx *uc, int count) { 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; uc->size -= count; if (uc->size < 0) { @@ -51,7 +51,7 @@ static void copyLiteral(UnpackCtx *uc, int bitsCount, int len) { 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; if (uc->size < 0) { count += uc->size; @@ -64,7 +64,7 @@ static void copyReference(UnpackCtx *uc, int bitsCount, int 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; uc.src = src + srcSize - 4; uc.size = READ_BE_UINT32(uc.src); uc.src -= 4; diff --git a/unpack.h b/unpack.h index 4f2a44d..a81c3aa 100644 --- a/unpack.h +++ b/unpack.h @@ -1,7 +1,7 @@ /* * 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__ @@ -9,6 +9,6 @@ #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__ diff --git a/util.cpp b/util.cpp index a61e91f..51c4f01 100644 --- a/util.cpp +++ b/util.cpp @@ -1,7 +1,7 @@ /* * 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 diff --git a/util.h b/util.h index 06fa1f0..4bb5226 100644 --- a/util.h +++ b/util.h @@ -1,7 +1,7 @@ /* * 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__ diff --git a/video.cpp b/video.cpp index 020112b..c950c83 100644 --- a/video.cpp +++ b/video.cpp @@ -1,7 +1,7 @@ /* * 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" @@ -11,8 +11,8 @@ #include "util.h" #include "video.h" -Video::Video(Resource *res, SystemStub *stub) - : _res(res), _stub(stub) { +Video::Video(Resource *res, SystemStub *stub, WidescreenMode widescreenMode) + : _res(res), _stub(stub), _widescreenMode(widescreenMode) { _layerScale = (_res->_type == kResourceTypeMac) ? 2 : 1; // Macintosh version is 512x448 _w = GAMESCREEN_W * _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() { debug(DBG_VIDEO, "Video::fullRefresh()"); _fullRefresh = true; @@ -154,20 +166,26 @@ void Video::setPaletteColorBE(int num, int offset) { void Video::setPaletteSlotBE(int palSlot, int palNum) { 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) { const int color = READ_BE_UINT16(p); p += 2; 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) { debug(DBG_VIDEO, "Video::setPaletteSlotLE()"); 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); - _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) { const int pitch = w * 16; 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) { uint8_t *tmp = _res->_scratchBuffer; 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); return; } @@ -912,7 +957,7 @@ void Video::MAC_drawStringChar(uint8_t *dst, int pitch, int x, int y, const uint buf.h = _h; buf.x = x * _layerScale; buf.y = y * _layerScale; - buf.setPixel = Video::MAC_drawBufferFont; + buf.setPixel = Video::MAC_setPixelFont; _MAC_fontFrontColor = color; _MAC_fontShadowColor = _charShadowColor; assert(chr >= 32); @@ -963,7 +1008,7 @@ void Video::MAC_decodeMap(int level, int room) { buf.ptr = _frontLayer; buf.w = buf.pitch = _w; buf.h = _h; - buf.setPixel = Video::MAC_drawBuffer; + buf.setPixel = Video::MAC_setPixel; _res->MAC_loadLevelRoom(level, room, &buf); memcpy(_backLayer, _frontLayer, _layerSize); 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) { - const int y = buf->y + src_y; - if (y >= 0 && y < buf->h) { - 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; - buf->ptr[offset] = color; - } +void Video::MAC_setPixel(DecodeBuffer *buf, int x, int y, uint8_t color) { + const int offset = y * buf->pitch + x; + buf->ptr[offset] = color; +} + +void Video::MAC_setPixelMask(DecodeBuffer *buf, int x, int y, uint8_t 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) { - const int y = buf->y + src_y; - if (y >= 0 && y < buf->h) { - 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; - if ((buf->ptr[offset] & 0x80) == 0) { - buf->ptr[offset] = color; - } - } - } -} - -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; - } - } +void Video::MAC_setPixelFont(DecodeBuffer *buf, int x, int y, uint8_t color) { + 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.x = x * _layerScale; buf.y = y * _layerScale; - buf.setPixel = eraseBackground ? MAC_drawBuffer : MAC_drawBufferMask; + buf.setPixel = eraseBackground ? MAC_setPixel : MAC_setPixelMask; fixOffsetDecodeBuffer(&buf, dataPtr); _res->MAC_decodeImageData(data, frame, &buf); markBlockAsDirty(buf.x, buf.y, READ_BE_UINT16(dataPtr), READ_BE_UINT16(dataPtr + 2), 1); diff --git a/video.h b/video.h index 3be685b..c489f51 100644 --- a/video.h +++ b/video.h @@ -1,7 +1,7 @@ /* * 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__ @@ -32,6 +32,7 @@ struct Video { Resource *_res; SystemStub *_stub; + WidescreenMode _widescreenMode; int _w, _h; int _layerSize; @@ -50,11 +51,12 @@ struct Video { uint8_t _shakeOffset; drawCharFunc _drawChar; - Video(Resource *res, SystemStub *stub); + Video(Resource *res, SystemStub *stub, WidescreenMode widescreenMode); ~Video(); void markBlockAsDirty(int16_t x, int16_t y, uint16_t w, uint16_t h, int scale); void updateScreen(); + void updateWidescreen(); void fullRefresh(); void fadeOut(); void fadeOutPalette(); @@ -68,6 +70,7 @@ struct Video { void PC_setLevelPalettes(); 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_decodeSpm(const uint8_t *dataPtr, uint8_t *dstPtr); void AMIGA_decodeLev(int level, int room); void AMIGA_decodeSpm(const uint8_t *src, 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); static Color AMIGA_convertColor(const uint16_t color, bool bgr = false); 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_drawBufferMask(DecodeBuffer *buf, int src_x, int src_y, int src_w, int src_h, 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_setPixel(DecodeBuffer *buf, int x, int y, uint8_t color); + static void MAC_setPixelMask(DecodeBuffer *buf, int x, int y, 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 MAC_drawSprite(int x, int y, const uint8_t *data, int frame, bool xflip, bool eraseBackground); };