Import 0.4.5

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

139
CHANGES.txt Normal file
View File

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

View File

@ -9,12 +9,12 @@ ZLIB_LIBS := -lz
CXXFLAGS += -Wall -Wpedantic -MMD $(SDL_CFLAGS) -DUSE_MODPLUG -DUSE_STATIC_SCALER -DUSE_TREMOR -DUSE_ZLIB
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)

View File

@ -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

View File

@ -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"

129
cpc_player.cpp Normal file
View File

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

37
cpc_player.h Normal file
View File

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

View File

@ -1,7 +1,7 @@
/*
* REminiscence - Flashback interpreter
* Copyright (C) 2005-2018 Gregory Montoir (cyx@users.sourceforge.net)
* Copyright (C) 2005-2019 Gregory Montoir (cyx@users.sourceforge.net)
*/
#include <math.h>
@ -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;
}
}
}

View File

@ -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;

View File

@ -30,57 +30,64 @@ uint8_t *decodeLzss(File &f, uint32_t &decodedSize) {
}
static void setPixel(int x, int y, int w, int h, uint8_t color, DecodeBuffer *buf) {
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);
}
}
}
}
}

View File

@ -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;
};

View File

@ -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 <sys/param.h>
@ -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);
}
}

6
file.h
View File

@ -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__

2
fs.cpp
View File

@ -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

2
fs.h
View File

@ -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__

273
game.cpp
View File

@ -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 <time.h>
@ -16,7 +16,7 @@
Game::Game(SystemStub *stub, FileSystem *fs, const char *savePath, int level, ResourceType ver, Language lang, WidescreenMode widescreenMode, bool autoSave)
: _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

12
game.h
View File

@ -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;

View File

@ -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"

View File

@ -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__

View File

@ -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<typename T>
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;

View File

@ -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 <SDL.h>
@ -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();

View File

@ -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;
}
}
}

2
menu.h
View File

@ -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__

View File

@ -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) {

12
mixer.h
View File

@ -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);

View File

@ -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;
}
}

View File

@ -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__

View File

@ -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

View File

@ -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__

View File

@ -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;

265
protection.cpp Normal file
View File

@ -0,0 +1,265 @@
/*
* REminiscence - Flashback interpreter
* Copyright (C) 2005-2019 Gregory Montoir (cyx@users.sourceforge.net)
*/
#include "game.h"
#include "screenshot.h"
#include "systemstub.h"
static uint8_t reverseBits(uint8_t ch) {
uint8_t r = 0;
for (int b = 0; b < 8; ++b) {
if (ch & (1 << b)) {
r |= (1 << (7 - b));
}
}
return r;
}
static uint8_t decryptChar(uint8_t ch) {
return reverseBits(ch ^ 0x55);
}
static uint8_t encryptChar(uint8_t ch) {
return reverseBits(ch) ^ 0x55;
}
bool Game::handleProtectionScreenShape() {
bool valid = false;
_cut.prepare();
const int palOffset = _res.isAmiga() ? 32 : 0;
_cut.copyPalette(_protectionPal + palOffset, 0);
_cut.updatePalette();
_cut._gfx.setClippingRect(64, 48, 128, 128);
_menu._charVar1 = 0xE0;
_menu._charVar2 = 0xEF;
_menu._charVar4 = 0xE5;
_menu._charVar5 = 0xE2;
// 5 codes per shape (a code is 6 characters long)
if (0) {
for (int shape = 0; shape < 30; ++shape) {
fprintf(stdout, "Shape #%2d\n", shape);
for (int code = 0; code < 5; ++code) {
const int offset = (shape * 5 + code) * 6;
if (_res.isAmiga()) {
fprintf(stdout, "\t ");
for (int i = 0; i < 6; ++i) {
const char chr = _protectionNumberDataAmiga[(shape * 5 + code) * 6 + i] ^ 0xD7;
fprintf(stdout, "%c", chr);
}
fprintf(stdout, " : ");
for (int i = 0; i < 6; ++i) {
fprintf(stdout, "%c", _protectionCodeDataAmiga[offset + i] ^ 0xD7);
}
} else {
fprintf(stdout, "\t code %d : ", code + 1);
for (int i = 0; i < 6; ++i) {
fprintf(stdout, "%c", decryptChar(_protectionCodeData[offset + i]));
}
}
fprintf(stdout, "\n");
}
}
}
if (0) {
uint8_t palette[256 * 3];
_stub->getPalette(palette, 256);
for (int shape = 0; shape < 30; ++shape) {
_cut.drawProtectionShape(shape, 0);
char fname[32];
snprintf(fname, sizeof(fname), "shape_%02d.bmp", shape);
saveBMP(fname, _vid._tempLayer, palette, _vid._w, _vid._h);
}
}
const int shapeNum = getRandomNumber() % 30;
const int codeNum = getRandomNumber() % 5;
for (int16_t zoom = 2000; zoom >= 0; zoom -= 100) {
_cut.drawProtectionShape(shapeNum, zoom);
_stub->copyRect(0, 0, _vid._w, _vid._h, _vid._tempLayer, _vid._w);
_stub->updateScreen(0);
_stub->sleep(30);
}
_vid.setTextPalette();
char codeNumber[8];
if (_res.isAmiga()) {
static const uint8_t kNumberLen = 6;
for (int i = 0; i < kNumberLen; ++i) {
codeNumber[i] = _protectionNumberDataAmiga[(shapeNum * 5 + codeNum) * kNumberLen + i] ^ 0xD7;
}
codeNumber[kNumberLen] = 0;
} else {
snprintf(codeNumber, sizeof(codeNumber), "CODE %d", codeNum + 1);
}
static const int kCodeLen = 6;
char codeText[kCodeLen + 1];
int len = 0;
do {
codeText[len] = '\0';
memcpy(_vid._frontLayer, _vid._tempLayer, _vid._layerSize);
_vid.drawString("PROTECTION", 11 * Video::CHAR_W, 2 * Video::CHAR_H, _menu._charVar2);
char buf[32];
snprintf(buf, sizeof(buf), "%s : %s", codeNumber, codeText);
_vid.drawString(buf, 8 * Video::CHAR_W, 23 * Video::CHAR_H, _menu._charVar2);
_vid.updateScreen();
_stub->sleep(50);
_stub->processEvents();
char c = _stub->_pi.lastChar;
if (c != 0) {
_stub->_pi.lastChar = 0;
if (len < kCodeLen) {
if (c >= 'a' && c <= 'z') {
c &= ~0x20;
}
if ((c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9')) {
codeText[len] = c;
++len;
}
}
}
if (_stub->_pi.backspace) {
_stub->_pi.backspace = false;
if (len > 0) {
--len;
}
}
if (_stub->_pi.enter) {
_stub->_pi.enter = false;
if (len > 0) {
int charsCount = 0;
if (_res.isAmiga()) {
const uint8_t *p = _protectionCodeDataAmiga + (shapeNum * 5 + codeNum) * kCodeLen;
for (int i = 0; i < len && (codeText[i] ^ 0xD7) == p[i]; ++i) {
++charsCount;
}
} else {
const uint8_t *p = _protectionCodeData + (shapeNum * 5 + codeNum) * kCodeLen;
for (int i = 0; i < len && encryptChar(codeText[i]) == p[i]; ++i) {
++charsCount;
}
}
valid = (charsCount == kCodeLen);
break;
}
}
} while (!_stub->_pi.quit);
_vid.fadeOut();
return valid;
}
bool Game::handleProtectionScreenWords() {
bool valid = false;
static const int kWordsCount = 40;
if (0) {
for (int i = 0; i < kWordsCount * 18; i += 18) {
const uint8_t *data = _protectionWordData + i;
fprintf(stdout, "page %d column %d line %2d word %d : ", data[0], data[1], data[2], data[3]);
for (int j = 4; j < 18; ++j) {
const uint8_t ch = decryptChar(data[j]);
if (!(ch >= 'A' && ch <= 'Z')) {
break;
}
fprintf(stdout, "%c", ch);
}
fprintf(stdout, "\n");
}
}
_vid.setTextPalette();
_vid.setPalette0xF();
memset(_vid._frontLayer, 0, _vid._layerSize);
static const char *kText[] = {
"Enter the word found in the",
"following location in your",
"rulebook. (Do not count the",
"title header that appears on",
"all pages. Ignore captions",
"and header).",
0
};
for (int i = 0; kText[i]; ++i) {
_vid.drawString(kText[i], 24, 16 + i * Video::CHAR_H, 0xE5);
}
static const int icon_spr_w = 16;
static const int icon_spr_h = 16;
int icon_num = 31;
for (int y = 140; y < 140 + 5 * icon_spr_h; y += icon_spr_h) {
for (int x = 56; x < 56 + 9 * icon_spr_w; x += icon_spr_w) {
drawIcon(icon_num, x, y, 0xF);
++icon_num;
}
}
const uint8_t code = getRandomNumber() % kWordsCount;
const uint8_t *protectionData = _protectionWordData + code * 18;
const char *kSecurityCodeText = "SECURITY CODE";
_vid.drawString(kSecurityCodeText, 72 + (114 - strlen(kSecurityCodeText) * 8) / 2, 158, 0xE4);
char buf[16];
snprintf(buf, sizeof(buf), "PAGE %d", protectionData[0]);
_vid.drawString(buf, 69, 189, 0xE5);
snprintf(buf, sizeof(buf), "COLUMN %d", protectionData[1]);
_vid.drawString(buf, 69, 197, 0xE5);
snprintf(buf, sizeof(buf), "LINE %d", protectionData[2]);
_vid.drawString(buf, (protectionData[2] < 10) ? 141 : 133, 189, 0xE5);
snprintf(buf, sizeof(buf), "WORD %d", protectionData[3]);
_vid.drawString(buf, 141, 197, 0xE5);
memcpy(_vid._tempLayer, _vid._frontLayer, _vid._layerSize);
static const int kCodeLen = 14;
char codeText[kCodeLen + 1];
int len = 0;
do {
memcpy(_vid._frontLayer, _vid._tempLayer, _vid._layerSize);
codeText[len] = '\0';
_vid.drawString(codeText, 72 + (114 - strlen(codeText) * 8) / 2, 166, 0xE3);
_vid.updateScreen();
_stub->sleep(50);
_stub->processEvents();
char c = _stub->_pi.lastChar;
if (c != 0) {
_stub->_pi.lastChar = 0;
if (len < kCodeLen) {
if (c >= 'a' && c <= 'z') {
c &= ~0x20;
}
if (c >= 'A' && c <= 'Z') {
codeText[len] = c;
++len;
}
}
}
if (_stub->_pi.backspace) {
_stub->_pi.backspace = false;
if (len > 0) {
--len;
}
}
if (_stub->_pi.enter) {
_stub->_pi.enter = false;
if (len > 0) {
int charsCount = 0;
for (int i = 0; i < len; ++i) {
if (encryptChar(codeText[i]) != protectionData[4 + i]) {
break;
}
++charsCount;
}
// words are padded with spaces
valid = decryptChar(protectionData[4 + charsCount]) == 0x20;
}
}
} while (!_stub->_pi.quit);
_vid.fadeOut();
return valid;
}

View File

@ -1,7 +1,7 @@
/*
* REminiscence - Flashback interpreter
* 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);
}
}

View File

@ -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;

View File

@ -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);
}

20
rs.cfg
View File

@ -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

View File

@ -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"

View File

@ -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__

View File

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

View File

@ -5,5 +5,6 @@
#include <stdint.h>
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

View File

@ -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

View File

@ -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__

View File

@ -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;
}
}

View File

@ -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__

View File

@ -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 = {

View File

@ -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;

View File

@ -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 <SDL.h>
@ -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();
}
}

View File

@ -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;

View File

@ -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__

View File

@ -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

2
util.h
View File

@ -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__

121
video.cpp
View File

@ -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);

13
video.h
View File

@ -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);
};