diff --git a/Makefile b/Makefile index 480b739..e304ef8 100644 --- a/Makefile +++ b/Makefile @@ -1,12 +1,12 @@ SDL_CFLAGS = `sdl-config --cflags` SDL_LIBS = `sdl-config --libs` -VORBIS_LIBS = -lvorbisidec MODPLUG_LIBS = -lmodplug +# TREMOR_LIBS = -lvorbisidec -logg ZLIB_LIBS = -lz CXX := clang++ -CXXFLAGS := -Wall -MMD $(SDL_CFLAGS) -DUSE_ZLIB # -DUSE_MODPLUG +CXXFLAGS := -Wall -MMD $(SDL_CFLAGS) -DUSE_MODPLUG -DUSE_ZLIB # -DUSE_TREMOR SRCS = collision.cpp cutscene.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 \ @@ -16,7 +16,7 @@ SRCS = collision.cpp cutscene.cpp file.cpp fs.cpp game.cpp graphics.cpp main.cpp OBJS = $(SRCS:.cpp=.o) DEPS = $(SRCS:.cpp=.d) -LIBS = $(SDL_LIBS) $(VORBIS_LIBS) $(MODPLUG_LIBS) $(ZLIB_LIBS) +LIBS = $(SDL_LIBS) $(MODPLUG_LIBS) $(TREMOR_LIBS) $(ZLIB_LIBS) rs: $(OBJS) $(CXX) $(LDFLAGS) -o $@ $(OBJS) $(LIBS) diff --git a/README b/README.txt similarity index 90% rename from README rename to README.txt index 908a8c4..931b0d3 100644 --- a/README +++ b/README.txt @@ -1,6 +1,6 @@ REminiscence README -Release version: 0.3.0 ($date) +Release version: 0.3.1 ------------------------------------------------------------------------------- @@ -15,7 +15,8 @@ game can be found at [1], [2] and [3]. Compiling: ---------- -Update the defines in the Makefile if needed. The SDL and zlib libraries are required. +Update the defines in the Makefile if needed. The SDL, zlib and modplug +libraries are required. Data Files: @@ -64,6 +65,10 @@ directory. These paths can be changed using command line switches : Usage: rs [OPTIONS]... --datapath=PATH Path to data files (default 'DATA') --savepath=PATH Path to save files (default '.') + --levelnum=NUM Level to start from (default '0') + --fullscreen Fullscreen display + --scaler=INDEX Graphics scaler + --language=LANG Language (fr,en,de,sp,it) In-game hotkeys : diff --git a/cutscene.cpp b/cutscene.cpp index ddebc70..a4952ca 100644 --- a/cutscene.cpp +++ b/cutscene.cpp @@ -18,13 +18,16 @@ Cutscene::Cutscene(Resource *res, SystemStub *stub, Video *vid) } void Cutscene::sync() { - // XXX input handling - if (!(_stub->_pi.dbgMask & PlayerInput::DF_FASTMODE)) { - int32_t delay = _stub->getTimeStamp() - _tstamp; - int32_t pause = _frameDelay * TIMER_SLICE - delay; - if (pause > 0) { - _stub->sleep(pause); - } + if (_stub->_pi.quit) { + return; + } + if (_stub->_pi.dbgMask & PlayerInput::DF_FASTMODE) { + return; + } + const int32_t delay = _stub->getTimeStamp() - _tstamp; + const int32_t pause = _frameDelay * TIMER_SLICE - delay; + if (pause > 0) { + _stub->sleep(pause); } _tstamp = _stub->getTimeStamp(); } @@ -90,7 +93,7 @@ uint16_t Cutscene::findTextSeparators(const uint8_t *p) { uint8_t *q = _textSep; uint16_t ret = 0; uint16_t pos = 0; - for (; *p != 0xA; ++p) { + for (; *p != 0xA && *p; ++p) { if (*p == 0x7C) { *q++ = pos; if (pos > ret) { @@ -111,6 +114,7 @@ uint16_t Cutscene::findTextSeparators(const uint8_t *p) { void Cutscene::drawText(int16_t x, int16_t y, const uint8_t *p, uint16_t color, uint8_t *page, uint8_t n) { debug(DBG_CUT, "Cutscene::drawText(x=%d, y=%d, c=%d)", x, y, color); + Video::drawCharFunc dcf = _vid->_drawChar; uint16_t last_sep = 0; if (n != 0) { last_sep = findTextSeparators(p); @@ -126,7 +130,7 @@ void Cutscene::drawText(int16_t x, int16_t y, const uint8_t *p, uint16_t color, if (n != 0) { xx += ((last_sep - *sep++) & 0xFE) * 4; } - for (; *p != 0xA; ++p) { + for (; *p != 0xA && *p; ++p) { if (*p == 0x7C) { yy += 8; xx = x; @@ -135,25 +139,11 @@ void Cutscene::drawText(int16_t x, int16_t y, const uint8_t *p, uint16_t color, } } else if (*p == 0x20) { xx += 8; + } else if (*p == 0x9) { + // ignore tab } else { - uint8_t *dst_char = page + 256 * yy + xx; - const uint8_t *src = _res->_fnt + (*p - 32) * 32; - for (int h = 0; h < 8; ++h) { - for (int w = 0; w < 4; ++w) { - uint8_t c1 = (*src & 0xF0) >> 4; - uint8_t c2 = (*src & 0x0F) >> 0; - ++src; - if (c1 != 0) { - *dst_char = (c1 == 0xF) ? color : (0xE0 + c1); - } - ++dst_char; - if (c2 != 0) { - *dst_char = (c2 == 0xF) ? color : (0xE0 + c2); - } - ++dst_char; - } - dst_char += 256 - 8; - } + uint8_t *dst = page + 256 * yy + xx; + (_vid->*dcf)(dst, 256, _res->_fnt, color, *p); xx += 8; } } @@ -176,11 +166,10 @@ void Cutscene::drawCreditsText() { return; } } - if (_creditsTextCounter <= 0) { // XXX + if (_creditsTextCounter <= 0) { uint8_t code = *_textCurPtr; if (code == 0xFF) { _textBuf[0] = 0xA; -// _cut_status = 0; } else if (code == 0xFE) { ++_textCurPtr; code = *_textCurPtr++; @@ -198,10 +187,11 @@ void Cutscene::drawCreditsText() { } } else { *_textCurBuf++ = code; - *_textCurBuf++ = 0xA; + *_textCurBuf = 0xA; + ++_textCurPtr; } } else { - _creditsTextCounter -= 10; // XXX adjust + _creditsTextCounter -= 10; } drawText((_creditsTextPosX - 1) * 8, _creditsTextPosY * 8, _textBuf, 0xEF, _page1, 0); } @@ -270,7 +260,7 @@ void Cutscene::op_waitForSync() { _varText = 0xFF; _frameDelay = 3; if (_textBuf == _textCurBuf) { - _creditsTextCounter = 20; + _creditsTextCounter = _res->isAmiga() ? 60 : 20; } memcpy(_page1, _page0, _vid->_layerSize); drawCreditsText(); @@ -385,6 +375,16 @@ void Cutscene::op_drawStringAtBottom() { debug(DBG_CUT, "Cutscene::op_drawStringAtBottom()"); uint16_t strId = fetchNextCmdWord(); if (!_creditsSequence) { + + // 'espions' - ignore last call, allows caption to be displayed longer on the screen + if (_id == 0x39 && strId == 0xFFFF) { + if ((_res->isDOS() && (_cmdPtr - _cmdPtrBak) == 0x10) || (_res->isAmiga() && (_cmdPtr - _res->_cmd) == 0x9F3)) { + _frameDelay = 100; + setPalette(); + return; + } + } + memset(_pageC + 179 * 256, 0xC0, 45 * 256); memset(_page1 + 179 * 256, 0xC0, 45 * 256); memset(_page0 + 179 * 256, 0xC0, 45 * 256); @@ -839,7 +839,7 @@ void Cutscene::op_drawStringAtPos() { uint8_t color = 0xD0 + (strId >> 0xC); drawText(x, y, str, color, _page1, 2); } - // workaround for buggy cutscene script + // 'voyage' - cutscene script redraws the string to refresh the screen if (_id == 0x34 && (strId & 0xFFF) == 0x45) { if ((_cmdPtr - _cmdPtrBak) == 0xA) { _stub->copyRect(0, 0, _vid->_w, _vid->_h, _page1, 256); @@ -964,13 +964,30 @@ void Cutscene::load(uint16_t cutName) { name = "INTRO"; } _res->load(name, Resource::OT_CMP); + if (_id == 0x39 && _res->_lang != LANG_FR) { + // + // 'espions' - '... the power which we need' caption is missing in Amiga English. + // fixed in DOS version, opcodes order is wrong + // + // opcode 0 pos 0x323 + // opcode 6 pos 0x324 + // str 0x3a + // + uint8_t *p = _res->_cmd + 0x322; + if (memcmp(p, "\x00\x18\x00\x3a", 4) == 0) { + p[0] = 0x06 << 2; // op_drawStringAtBottom + p[1] = 0x00; + p[2] = 0x3a; + p[3] = 0x00; // op_markCurPos + } + } break; case kResourceTypeDOS: _res->load(name, Resource::OT_CMD); _res->load(name, Resource::OT_POL); - _res->load_CINE(); break; } + _res->load_CINE(); } void Cutscene::prepare() { @@ -986,12 +1003,11 @@ void Cutscene::prepare() { _gfx.setClippingRect(8, 50, 240, 128); } -void Cutscene::startCredits() { - _textCurPtr = _creditsData; +void Cutscene::playCredits() { + _textCurPtr = _res->isAmiga() ? _creditsDataAmiga : _creditsDataDOS; _textBuf[0] = 0xA; _textCurBuf = _textBuf; _creditsSequence = true; -// _cut_status = 1; _varText = 0; _textUnk2 = 0; _creditsTextCounter = 0; @@ -1011,6 +1027,37 @@ void Cutscene::startCredits() { _creditsSequence = false; } +void Cutscene::playText(const char *str) { + Color c; + // background + c.r = c.g = c.b = 0; + _stub->setPaletteEntry(0xC0, &c); + // text + c.r = c.g = c.b = 255; + _stub->setPaletteEntry(0xC1, &c); + + int lines = 0; + for (int i = 0; str[i]; ++i) { + if (str[i] == '|') { + ++lines; + } + } + const int y = (128 - lines * 8) / 2; + memset(_page1, 0xC0, _vid->_layerSize); + drawText(0, y, (const uint8_t *)str, 0xC1, _page1, 1); + _stub->copyRect(0, 0, _vid->_w, _vid->_h, _page1, 256); + _stub->updateScreen(0); + + while (!_stub->_pi.quit) { + _stub->processEvents(); + if (_stub->_pi.backspace) { + _stub->_pi.backspace = false; + break; + } + _stub->sleep(TIMER_SLICE); + } +} + void Cutscene::play() { if (_id != 0xFFFF) { _textCurBuf = NULL; @@ -1044,7 +1091,14 @@ void Cutscene::play() { } } } - if (cutName != 0xFFFF) { + if (g_options.use_text_cutscenes && _res->_lang == LANG_FR) { + for (int i = 0; _frTextsTable[i].str; ++i) { + if (_id == _frTextsTable[i].num) { + playText(_frTextsTable[i].str); + break; + } + } + } else if (cutName != 0xFFFF) { load(cutName); mainLoop(cutOff); } diff --git a/cutscene.h b/cutscene.h index 95a1776..1658df1 100644 --- a/cutscene.h +++ b/cutscene.h @@ -22,16 +22,23 @@ struct Cutscene { TIMER_SLICE = 15 }; + struct Text { + int num; + const char *str; + }; + static const OpcodeStub _opcodeTable[]; static const char *_namesTable[]; static const uint16_t _offsetsTable[]; static const uint8_t _amigaDemoOffsetsTable[]; static const uint16_t _cosTable[]; static const uint16_t _sinTable[]; - static const uint8_t _creditsData[]; + static const uint8_t _creditsDataDOS[]; + static const uint8_t _creditsDataAmiga[]; static const uint16_t _creditsCutSeq[]; static const uint8_t _musicTable[]; static const uint8_t _protectionShapeData[]; + static const Text _frTextsTable[]; Graphics _gfx; Resource *_res; @@ -118,7 +125,8 @@ struct Cutscene { void mainLoop(uint16_t offset); void load(uint16_t cutName); void prepare(); - void startCredits(); + void playCredits(); + void playText(const char *str); void play(); }; diff --git a/file.cpp b/file.cpp index c56bfa7..0962a10 100644 --- a/file.cpp +++ b/file.cpp @@ -24,9 +24,9 @@ struct File_impl { virtual uint32_t write(void *ptr, uint32_t len) = 0; }; -struct stdFile : File_impl { +struct StdioFile : File_impl { FILE *_fp; - stdFile() : _fp(0) {} + StdioFile() : _fp(0) {} bool open(const char *path, const char *mode) { _ioErr = false; _fp = fopen(path, mode); @@ -76,9 +76,9 @@ struct stdFile : File_impl { }; #ifdef USE_ZLIB -struct zlibFile : File_impl { +struct GzipFile : File_impl { gzFile _fp; - zlibFile() : _fp(0) {} + GzipFile() : _fp(0) {} bool open(const char *path, const char *mode) { _ioErr = false; _fp = gzopen(path, mode); @@ -147,7 +147,7 @@ bool File::open(const char *filename, const char *mode, FileSystem *fs) { _impl = 0; } assert(mode[0] != 'z'); - _impl = new stdFile; + _impl = new StdioFile; char *path = fs->findPath(filename); if (path) { debug(DBG_FILE, "Open file name '%s' mode '%s' path '%s'", filename, mode, path); @@ -166,12 +166,12 @@ bool File::open(const char *filename, const char *mode, const char *directory) { } #ifdef USE_ZLIB if (mode[0] == 'z') { - _impl = new zlibFile; + _impl = new GzipFile; ++mode; } #endif if (!_impl) { - _impl = new stdFile; + _impl = new StdioFile; } char path[MAXPATHLEN]; snprintf(path, sizeof(path), "%s/%s", directory, filename); diff --git a/game.cpp b/game.cpp index d5e19b1..5ab7bb1 100644 --- a/game.cpp +++ b/game.cpp @@ -69,21 +69,26 @@ void Game::run() { break; } - if (_res.isAmiga()) { - displayTitleScreenAmiga(); - _stub->setScreenSize(Video::GAMESCREEN_W, Video::GAMESCREEN_H); - } - while (!_stub->_pi.quit) { - if (_res.isDOS()) { + switch (_res._type) { + case kResourceTypeDOS: _mix.playMusic(1); _menu.handleTitleScreen(); - if (_menu._selectedOption == Menu::MENU_OPTION_ITEM_QUIT) { + if (_menu._selectedOption == Menu::MENU_OPTION_ITEM_QUIT || _stub->_pi.quit) { + _stub->_pi.quit = true; break; } _skillLevel = _menu._skill; _currentLevel = _menu._level; _mix.stopMusic(); + break; + case kResourceTypeAmiga: + displayTitleScreenAmiga(); + _stub->setScreenSize(Video::GAMESCREEN_W, Video::GAMESCREEN_H); + break; + } + if (_stub->_pi.quit) { + break; } if (_currentLevel == 7) { _vid.fadeOut(); @@ -328,7 +333,7 @@ void Game::playCutscene(int id) { _cut.play(); } if (id == 0x3D) { - _cut.startCredits(); + _cut.playCredits(); } _mix.stopMusic(); } diff --git a/intern.h b/intern.h index 4c8dae6..0387fb1 100644 --- a/intern.h +++ b/intern.h @@ -46,6 +46,26 @@ inline uint32_t READ_LE_UINT32(const void *ptr) { return (b[3] << 24) | (b[2] << 16) | (b[1] << 8) | b[0]; } +inline int8_t ADDC_S8(int a, int b) { + a += b; + if (a < -128) { + a = -128; + } else if (a > 127) { + a = 127; + } + return a; +} + +inline int16_t ADDC_S16(int a, int b) { + a += b; + if (a < -32768) { + a = -32768; + } else if (a > 32767) { + a = 32767; + } + return a; +} + template inline void SWAP(T &a, T &b) { T tmp = a; @@ -72,6 +92,7 @@ struct Options { bool enable_password_menu; bool fade_out_palette; bool use_tiledata; + bool use_text_cutscenes; }; struct Color { diff --git a/main.cpp b/main.cpp index 5543b4e..afe7576 100644 --- a/main.cpp +++ b/main.cpp @@ -19,9 +19,10 @@ static const char *USAGE = "Usage: %s [OPTIONS]...\n" " --datapath=PATH Path to data files (default 'DATA')\n" " --savepath=PATH Path to save files (default '.')\n" - " --levelnum=NUM Start level (default '0')\n" - " --fullscreen Start fullscreen\n" + " --levelnum=NUM Level to start from (default '0')\n" + " --fullscreen Fullscreen display\n" " --scaler=INDEX Graphics scaler\n" + " --language=LANG Language (fr,en,de,sp,it)\n" ; static int detectVersion(FileSystem *fs) { @@ -80,6 +81,7 @@ static void initOptions() { g_options.play_disabled_cutscenes = false; g_options.enable_password_menu = false; g_options.fade_out_palette = true; + g_options.use_text_cutscenes = false; // read configuration file struct { const char *name; @@ -90,6 +92,7 @@ static void initOptions() { { "enable_password_menu", &g_options.enable_password_menu }, { "fade_out_palette", &g_options.fade_out_palette }, { "use_tiledata", &g_options.use_tiledata }, + { "use_text_cutscenes", &g_options.use_text_cutscenes }, { 0, 0 } }; static const char *filename = "rs.cfg"; @@ -130,6 +133,7 @@ int main(int argc, char *argv[]) { int levelNum = 0; int scaler = DEFAULT_SCALER; bool fullscreen = false; + int forcedLanguage = -1; if (argc == 2) { // data path as the only command line argument struct stat st; @@ -144,6 +148,7 @@ int main(int argc, char *argv[]) { { "levelnum", required_argument, 0, 3 }, { "fullscreen", no_argument, 0, 4 }, { "scaler", required_argument, 0, 5 }, + { "language", required_argument, 0, 6 }, { 0, 0, 0, 0 } }; int index; @@ -170,6 +175,26 @@ int main(int argc, char *argv[]) { scaler = DEFAULT_SCALER; } break; + case 6: { + static const struct { + int lang; + const char *str; + } languages[] = { + { LANG_FR, "FR" }, + { LANG_EN, "EN" }, + { LANG_DE, "DE" }, + { LANG_SP, "SP" }, + { LANG_IT, "IT" }, + { -1, 0 } + }; + for (int i = 0; languages[i].str; ++i) { + if (strcasecmp(languages[i].str, optarg) == 0) { + forcedLanguage = languages[i].lang; + break; + } + } + } + break; default: printf(USAGE, argv[0]); return 0; @@ -183,7 +208,7 @@ int main(int argc, char *argv[]) { error("Unable to find data files, check that all required files are present"); return -1; } - Language language = detectLanguage(&fs); + 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); stub->init(g_caption, Video::GAMESCREEN_W, Video::GAMESCREEN_H, scaler, fullscreen); diff --git a/mixer.cpp b/mixer.cpp index 23a28ba..ead0224 100644 --- a/mixer.cpp +++ b/mixer.cpp @@ -101,12 +101,19 @@ void Mixer::playMusic(int num) { return; } } + if (_musicType == MT_OGG && isMusicSfx(num)) { // do not play level action music with background music + return; + } if (isMusicSfx(num)) { // level action sequence _sfx.play(num); - _musicType = MT_SFX; + if (_sfx._playing) { + _musicType = MT_SFX; + } } else { // cutscene _mod.play(num); - _musicType = MT_MOD; + if (_mod._playing) { + _musicType = MT_MOD; + } } } @@ -119,8 +126,7 @@ void Mixer::stopMusic() { _mod.stop(); break; case MT_OGG: - _ogg.stopTrack(); - _musicTrack = -1; + _ogg.pauseTrack(); break; case MT_SFX: _sfx.stop(); @@ -133,20 +139,9 @@ void Mixer::stopMusic() { } } -static void nr(const int8_t *in, int len, int8_t *out) { - static int prev = 0; - for (int i = 0; i < len; ++i) { - const int vnr = in[i] >> 1; - out[i] = vnr + prev; - prev = vnr; - } -} - -void Mixer::mix(int8_t *out, int len) { - int8_t buf[len]; - memset(buf, 0, len); +void Mixer::mix(int16_t *out, int len) { if (_premixHook) { - if (!_premixHook(_premixHookData, buf, len)) { + if (!_premixHook(_premixHookData, out, len)) { _premixHook = 0; _premixHookData = 0; } @@ -160,24 +155,13 @@ void Mixer::mix(int8_t *out, int len) { break; } const int sample = ch->chunk.getPCM(ch->chunkPos >> FRAC_BITS); - addclamp(buf[pos], sample * ch->volume / Mixer::MAX_VOLUME); + out[pos] = ADDC_S16(out[pos], (sample * ch->volume / Mixer::MAX_VOLUME) << 8); ch->chunkPos += ch->chunkInc; } } } - nr(buf, len, out); } -void Mixer::addclamp(int8_t& a, int b) { - int add = a + b; - if (add < -128) { - add = -128; - } else if (add > 127) { - add = 127; - } - a = add; -} - -void Mixer::mixCallback(void *param, int8_t *buf, int len) { +void Mixer::mixCallback(void *param, int16_t *buf, int len) { ((Mixer *)param)->mix(buf, len); } diff --git a/mixer.h b/mixer.h index e6771e7..de72779 100644 --- a/mixer.h +++ b/mixer.h @@ -42,7 +42,7 @@ struct FileSystem; struct SystemStub; struct Mixer { - typedef bool (*PremixHook)(void *userData, int8_t *buf, int len); + typedef bool (*PremixHook)(void *userData, int16_t *buf, int len); enum MusicType { MT_NONE, @@ -79,10 +79,9 @@ struct Mixer { void stopAll(); void playMusic(int num); void stopMusic(); - void mix(int8_t *buf, int len); + void mix(int16_t *buf, int len); - static void addclamp(int8_t &a, int b); - static void mixCallback(void *param, int8_t *buf, int len); + static void mixCallback(void *param, int16_t *buf, int len); }; #endif // MIXER_H__ diff --git a/mod_player.cpp b/mod_player.cpp index af6682f..dc1eb7f 100644 --- a/mod_player.cpp +++ b/mod_player.cpp @@ -27,7 +27,7 @@ struct ModPlayer_impl { ModPlug_GetSettings(&_settings); _settings.mFlags = MODPLUG_ENABLE_OVERSAMPLING | MODPLUG_ENABLE_NOISE_REDUCTION; _settings.mChannels = 1; - _settings.mBits = 8; + _settings.mBits = 16; _settings.mFrequency = rate; _settings.mResamplingMode = MODPLUG_RESAMPLE_FIR; ModPlug_SetSettings(&_settings); @@ -50,18 +50,14 @@ struct ModPlayer_impl { } } - bool mix(int8_t *buf, int len) { - memset(buf, 0, len); + bool mix(int16_t *buf, int len) { if (_mf) { const int order = ModPlug_GetCurrentOrder(_mf); if (order == 3 && _repeatIntro) { ModPlug_SeekOrder(_mf, 1); _repeatIntro = false; } - const int count = ModPlug_Read(_mf, buf, len); - for (int i = 0; i < count; ++i) { - buf[i] ^= 0x80; - } + const int count = ModPlug_Read(_mf, buf, len * sizeof(int16_t)); return count > 0; } return false; @@ -152,7 +148,8 @@ struct ModPlayer_impl { void applyPortamento(int trackNum); void handleEffect(int trackNum, bool tick); void mixSamples(int8_t *buf, int len); - bool mix(int8_t *buf, int len); + bool mixS8(int8_t *buf, int len); + bool mix(int16_t *buf, int len); }; ModPlayer_impl::ModPlayer_impl() @@ -607,7 +604,8 @@ void ModPlayer_impl::mixSamples(int8_t *buf, int samplesLen) { } while (count--) { const int out = si->getPCM(pos >> FRAC_BITS); - Mixer::addclamp(*mixbuf++, out * tk->volume / 64); + *mixbuf = ADDC_S8(*mixbuf, out * tk->volume / 64); + ++mixbuf; pos += deltaPos; } } @@ -616,9 +614,8 @@ void ModPlayer_impl::mixSamples(int8_t *buf, int samplesLen) { } } -bool ModPlayer_impl::mix(int8_t *buf, int len) { +bool ModPlayer_impl::mixS8(int8_t *buf, int len) { if (_playing) { - memset(buf, 0, len); const int samplesPerTick = _mixingRate / (50 * _songTempo / 125); while (len != 0) { if (_samplesLeft == 0) { @@ -637,6 +634,16 @@ bool ModPlayer_impl::mix(int8_t *buf, int len) { } return _playing; } + +bool ModPlayer_impl::mix(int16_t *samples, int len) { + int8_t buf[len]; + memset(buf, 0, sizeof(buf)); + const bool ret = mixS8(buf, len); + for (int i = 0; i < len; ++i) { + samples[i] = buf[i] << 8; + } + return ret; +} #endif ModPlayer::ModPlayer(Mixer *mixer, FileSystem *fs) @@ -673,6 +680,6 @@ void ModPlayer::stop() { } } -bool ModPlayer::mixCallback(void *param, int8_t *buf, int len) { +bool ModPlayer::mixCallback(void *param, int16_t *buf, int len) { return ((ModPlayer_impl *)param)->mix(buf, len); } diff --git a/mod_player.h b/mod_player.h index d12ee7a..195d1e9 100644 --- a/mod_player.h +++ b/mod_player.h @@ -31,7 +31,7 @@ struct ModPlayer { void play(int num); void stop(); - static bool mixCallback(void *param, int8_t *buf, int len); + static bool mixCallback(void *param, int16_t *buf, int len); }; #endif // MOD_PLAYER_H__ diff --git a/ogg_player.cpp b/ogg_player.cpp index f37bb0a..dc6a0a5 100644 --- a/ogg_player.cpp +++ b/ogg_player.cpp @@ -10,6 +10,7 @@ #include "file.h" #include "mixer.h" #include "ogg_player.h" +#include "util.h" #ifdef USE_TREMOR struct VorbisFile: File { @@ -85,7 +86,7 @@ struct OggDecoder_impl { _channels = vi->channels; return true; } - int read(int8_t *dst, int samples) { + int read(int16_t *dst, int samples) { int size = samples * _channels * sizeof(int16_t); if (size > _readBufSize) { _readBufSize = size; @@ -100,22 +101,26 @@ struct OggDecoder_impl { const int len = ov_read(&_ovf, (char *)_readBuf, size, 0); if (len < 0) { // error in decoder - return 0; + return count; } else if (len == 0) { // loop ov_raw_seek(&_ovf, 0); continue; } + assert((len & 1) == 0); switch (_channels) { case 2: - assert((len & 1) == 0); + assert((len & 3) == 0); for (int i = 0; i < len / 2; i += 2) { - Mixer::addclamp(*dst++, ((_readBuf[i] + _readBuf[i + 1]) / 2) >> 8); + const int16_t s16 = (_readBuf[i] + _readBuf[i + 1]) / 2; + *dst = ADDC_S16(*dst, s16); + ++dst; } break; case 1: for (int i = 0; i < len / 2; ++i) { - Mixer::addclamp(*dst++, _readBuf[i] >> 8); + *dst = ADDC_S16(*dst, _readBuf[i]); + ++dst; } break; } @@ -188,7 +193,7 @@ void OggPlayer::resumeTrack() { #endif } -bool OggPlayer::mix(int8_t *buf, int len) { +bool OggPlayer::mix(int16_t *buf, int len) { #ifdef USE_TREMOR if (_impl) { return _impl->read(buf, len) != 0; @@ -197,7 +202,7 @@ bool OggPlayer::mix(int8_t *buf, int len) { return false; } -bool OggPlayer::mixCallback(void *param, int8_t *buf, int len) { +bool OggPlayer::mixCallback(void *param, int16_t *buf, int len) { return ((OggPlayer *)param)->mix(buf, len); } diff --git a/ogg_player.h b/ogg_player.h index 9af3b5b..6e5e2a6 100644 --- a/ogg_player.h +++ b/ogg_player.h @@ -22,8 +22,8 @@ struct OggPlayer { void pauseTrack(); void resumeTrack(); bool isPlaying() const { return _impl != 0; } - bool mix(int8_t *buf, int len); - static bool mixCallback(void *param, int8_t *buf, int len); + bool mix(int16_t *buf, int len); + static bool mixCallback(void *param, int16_t *buf, int len); Mixer *_mix; FileSystem *_fs; diff --git a/resource.cpp b/resource.cpp index 697d1c1..7c0c002 100644 --- a/resource.cpp +++ b/resource.cpp @@ -269,28 +269,65 @@ void Resource::load_SPR_OFF(const char *fileName, uint8_t *sprData) { error("Cannot load '%s'", _entryName); } -void Resource::load_CINE() { - const char *baseName = 0; - switch (_lang) { +static const char *getCineName(Language lang, ResourceType type) { + switch (lang) { case LANG_FR: - baseName = "FR_CINE"; - break; - case LANG_EN: - baseName = "ENGCINE"; - break; + if (type == kResourceTypeAmiga) { + return "FR"; + } + return "FR_"; case LANG_DE: - baseName = "GERCINE"; - break; + return "GER"; case LANG_SP: - baseName = "SPACINE"; - break; + return "SPA"; case LANG_IT: - baseName = "ITACINE"; - break; + return "ITA"; + case LANG_EN: + default: + return "ENG"; + } +} + +void Resource::load_CINE() { + const char *prefix = getCineName(_lang, _type); + debug(DBG_RES, "Resource::load_CINE('%s')", prefix); + if (_type == kResourceTypeAmiga) { + if (_isDemo) { + // file not present in demo data files + return; + } + if (_cine_txt == 0) { + snprintf(_entryName, sizeof(_entryName), "%sCINE.TXT", prefix); + File f; + if (f.open(_entryName, "rb", _fs)) { + const int len = f.size(); + _cine_txt = (uint8_t *)malloc(len + 1); + if (!_cine_txt) { + error("Unable to allocate cinematics text data"); + } + f.read(_cine_txt, len); + if (f.ioErr()) { + error("I/O error when reading '%s'", _entryName); + } + _cine_txt[len] = 0; + uint8_t *p = _cine_txt; + for (int i = 0; i < NUM_CUTSCENE_TEXTS; ++i) { + _cineStrings[i] = p; + uint8_t *sep = (uint8_t *)memchr(p, '\n', &_cine_txt[len] - p); + if (!sep) { + break; + } + p = sep + 1; + } + } + if (!_cine_txt) { + error("Cannot load '%s'", _entryName); + } + } + return; } - debug(DBG_RES, "Resource::load_CINE('%s')", baseName); if (_cine_off == 0) { - snprintf(_entryName, sizeof(_entryName), "%s.BIN", baseName); + snprintf(_entryName, sizeof(_entryName), "%sCINE.BIN", prefix); File f; if (f.open(_entryName, "rb", _fs)) { int len = f.size(); @@ -310,7 +347,7 @@ void Resource::load_CINE() { } } if (_cine_txt == 0) { - snprintf(_entryName, sizeof(_entryName), "%s.TXT", baseName); + snprintf(_entryName, sizeof(_entryName), "%sCINE.TXT", prefix); File f; if (f.open(_entryName, "rb", _fs)) { int len = f.size(); diff --git a/resource.h b/resource.h index 5997a1e..1d7c09a 100644 --- a/resource.h +++ b/resource.h @@ -90,6 +90,7 @@ struct Resource { enum { NUM_SFXS = 66, NUM_BANK_BUFFERS = 50, + NUM_CUTSCENE_TEXTS = 117, NUM_SPRITES = 1287 }; @@ -135,6 +136,7 @@ struct Resource { uint8_t _numSfx; uint8_t *_cmd; uint8_t *_pol; + uint8_t *_cineStrings[NUM_CUTSCENE_TEXTS]; uint8_t *_cine_off; uint8_t *_cine_txt; char **_extTextsTable; @@ -209,7 +211,7 @@ struct Resource { const int offset = READ_BE_UINT16(_cine_off + num * 2); return _cine_txt + offset; } - return 0; + return (num >= 0 && num < NUM_CUTSCENE_TEXTS) ? _cineStrings[num] : 0; } const char *getMenuString(int num) { return (num >= 0 && num < LocaleData::LI_NUM) ? _textsTable[num] : ""; diff --git a/rs.cfg b/rs.cfg index 4401265..0d04e7f 100644 --- a/rs.cfg +++ b/rs.cfg @@ -12,3 +12,6 @@ fade_out_palette=true # use .BNQ & .LEV datafiles (tile based rendering) for backgrounds (instead of .MAP) use_tiledata=false + +# display text instead of playing the polygon cutscenes (french only) +use_text_cutscenes=false diff --git a/seq_player.cpp b/seq_player.cpp index d9619b1..18216e3 100644 --- a/seq_player.cpp +++ b/seq_player.cpp @@ -102,10 +102,10 @@ void SeqDemuxer::readPalette(uint8_t *dst) { _f->read(dst, 256 * 3); } -void SeqDemuxer::readAudioS8(uint8_t *dst) { +void SeqDemuxer::readAudio(int16_t *dst) { _f->seek(_frameOffset + _audioDataOffset); for (int i = 0; i < kAudioBufferSize; ++i) { - dst[i] = _f->readUint16BE() >> 8; + dst[i] = _f->readUint16BE(); } } @@ -246,9 +246,9 @@ void SeqPlayer::play(File *f) { if (_demux._audioDataSize != 0) { SoundBufferQueue *sbq = (SoundBufferQueue *)malloc(sizeof(SoundBufferQueue)); if (sbq) { - sbq->data = (uint8_t *)malloc(SeqDemuxer::kAudioBufferSize); + sbq->data = (int16_t *)calloc(SeqDemuxer::kAudioBufferSize, sizeof(int16_t)); if (sbq->data) { - _demux.readAudioS8(sbq->data); + _demux.readAudio(sbq->data); sbq->size = SeqDemuxer::kAudioBufferSize; sbq->read = 0; sbq->next = 0; @@ -332,7 +332,7 @@ void SeqPlayer::play(File *f) { } } -bool SeqPlayer::mix(int8_t *buf, int samples) { +bool SeqPlayer::mix(int16_t *buf, int samples) { if (_soundQueuePreloadSize < kSoundPreloadSize) { return true; } @@ -350,7 +350,7 @@ bool SeqPlayer::mix(int8_t *buf, int samples) { return true; } -bool SeqPlayer::mixCallback(void *param, int8_t *buf, int len) { +bool SeqPlayer::mixCallback(void *param, int16_t *buf, int len) { return ((SeqPlayer *)param)->mix(buf, len); } diff --git a/seq_player.h b/seq_player.h index 1ddaba7..b20dfdf 100644 --- a/seq_player.h +++ b/seq_player.h @@ -28,7 +28,7 @@ struct SeqDemuxer { void fillBuffer(int num, int offset, int size); void clearBuffer(int num); void readPalette(uint8_t *dst); - void readAudioS8(uint8_t *dst); + void readAudio(int16_t *dst); int _frameOffset; int _audioDataOffset; @@ -55,7 +55,7 @@ struct SeqPlayer { static const char *_namesTable[]; struct SoundBufferQueue { - uint8_t *data; + int16_t *data; int size; int read; SoundBufferQueue *next; @@ -66,8 +66,8 @@ struct SeqPlayer { void setBackBuffer(uint8_t *buf) { _buf = buf; } void play(File *f); - bool mix(int8_t *buf, int len); - static bool mixCallback(void *param, int8_t *buf, int len); + bool mix(int16_t *buf, int len); + static bool mixCallback(void *param, int16_t *buf, int len); SystemStub *_stub; uint8_t *_buf; diff --git a/sfx_player.cpp b/sfx_player.cpp index d01622a..5188082 100644 --- a/sfx_player.cpp +++ b/sfx_player.cpp @@ -127,7 +127,8 @@ void SfxPlayer::mixSamples(int8_t *buf, int samplesLen) { } while (count--) { const int out = si->getPCM(pos >> FRAC_BITS); - Mixer::addclamp(*mixbuf++, out * si->vol / 64); + *mixbuf = ADDC_S8(*mixbuf, out * si->vol / 64); + ++mixbuf; pos += deltaPos; } } @@ -138,7 +139,6 @@ void SfxPlayer::mixSamples(int8_t *buf, int samplesLen) { bool SfxPlayer::mix(int8_t *buf, int len) { if (_playing) { - memset(buf, 0, len); const int samplesPerTick = _mix->getSampleRate() / 50; while (len != 0) { if (_samplesLeft == 0) { @@ -158,6 +158,12 @@ bool SfxPlayer::mix(int8_t *buf, int len) { return _playing; } -bool SfxPlayer::mixCallback(void *param, int8_t *buf, int len) { - return ((SfxPlayer *)param)->mix(buf, len); +bool SfxPlayer::mixCallback(void *param, int16_t *samples, int len) { + int8_t buf[len]; + memset(buf, 0, sizeof(buf)); + const bool ret = ((SfxPlayer *)param)->mix(buf, len); + for (int i = 0; i < len; ++i) { + samples[i] = buf[i] << 8; + } + return ret; } diff --git a/sfx_player.h b/sfx_player.h index 91eb17d..d88e8f3 100644 --- a/sfx_player.h +++ b/sfx_player.h @@ -81,10 +81,10 @@ struct SfxPlayer { void stop(); void playSample(int channel, const uint8_t *sampleData, uint16_t period); void handleTick(); - bool mix(int8_t *buf, int len); - void mixSamples(int8_t *buf, int samplesLen); + void mixSamples(int8_t *samples, int samplesLen); - static bool mixCallback(void *param, int8_t *buf, int len); + bool mix(int8_t *buf, int len); + static bool mixCallback(void *param, int16_t *buf, int len); }; #endif // SFX_PLAYER_H__ diff --git a/staticres.cpp b/staticres.cpp index 044f1af..db5aff5 100644 --- a/staticres.cpp +++ b/staticres.cpp @@ -188,7 +188,7 @@ const uint16_t Cutscene::_sinTable[] = { 0xFFDD, 0xFFE1, 0xFFE6, 0xFFEA, 0xFFEF, 0xFFF3, 0xFFF8, 0xFFFC }; -const uint8_t Cutscene::_creditsData[] = { +const uint8_t Cutscene::_creditsDataDOS[] = { 0xFE, 0x14, 0x00, 0x01, 0x00, 0x04, 0x20, 0x20, 0x54, 0x68, 0x65, 0x20, 0x46, 0x6C, 0x61, 0x73, 0x68, 0x42, 0x61, 0x63, 0x6B, 0x20, 0x54, 0x65, 0x61, 0x6D, 0x20, 0x69, 0x73, 0x2E, 0x2E, 0x2E, 0xFE, 0x32, 0xFE, 0x14, 0x00, 0x01, 0x00, 0x07, 0x00, 0x01, 0x00, 0x05, 0x20, 0x20, 0x49, 0x6E, @@ -356,6 +356,164 @@ const uint8_t Cutscene::_creditsData[] = { 0x20, 0x20, 0x20, 0x20, 0x20, 0x7C, 0xFE, 0x50, 0xFE, 0xFF, 0x00, 0xFF }; +const uint8_t Cutscene::_creditsDataAmiga[] = { + 0xFE, 0x14, 0x00, 0x01, 0x00, 0x08, 0x20, 0x20, 0x20, 0x20, 0x20, 0x54, 0x68, 0x65, 0x20, 0x46, + 0x6C, 0x61, 0x73, 0x68, 0x42, 0x61, 0x63, 0x6B, 0x20, 0x54, 0x65, 0x61, 0x6D, 0x20, 0x69, 0x73, + 0x2E, 0x2E, 0x2E, 0xFE, 0x32, 0x00, 0x01, 0x00, 0x08, 0x20, 0x20, 0x41, 0x6D, 0x69, 0x67, 0x61, + 0x20, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6F, 0x6E, 0x3A, 0x7C, 0xFE, 0x14, 0x7C, 0x7C, 0xFE, 0x14, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x42, + 0x65, 0x6E, 0x6F, 0x69, 0x73, 0x74, 0x20, 0x41, 0x72, 0x6F, 0x6E, 0x20, 0x20, 0x20, 0x20, 0x7C, + 0xFE, 0x14, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x54, 0x68, 0x69, 0x65, 0x72, 0x72, 0x79, 0x20, 0x4C, 0x65, 0x76, 0x61, 0x73, 0x74, 0x72, + 0x65, 0x20, 0x7C, 0xFE, 0x14, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x54, 0x68, 0x69, 0x65, 0x72, 0x72, 0x79, 0x20, 0x50, 0x65, 0x72, 0x72, + 0x65, 0x61, 0x75, 0x20, 0x20, 0x7C, 0xFE, 0x14, 0xFE, 0x14, 0x00, 0x01, 0x00, 0x07, 0x00, 0x01, + 0x00, 0x09, 0x20, 0x20, 0x50, 0x72, 0x6F, 0x67, 0x72, 0x61, 0x6D, 0x6D, 0x65, 0x72, 0x73, 0x3A, + 0x7C, 0x7C, 0xFE, 0x14, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x42, 0x65, 0x6E, 0x6F, 0x69, 0x73, 0x74, 0x20, 0x41, 0x72, 0x6F, 0x6E, + 0x20, 0x20, 0x20, 0x20, 0x7C, 0xFE, 0x14, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x50, 0x68, 0x69, 0x6C, 0x69, 0x70, 0x70, 0x65, 0x20, 0x43, + 0x68, 0x61, 0x73, 0x74, 0x65, 0x6C, 0x20, 0x7C, 0xFE, 0x14, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x50, 0x61, 0x75, + 0x6C, 0x20, 0x43, 0x75, 0x69, 0x73, 0x73, 0x65, 0x74, 0x20, 0x7C, 0xFE, 0x14, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x46, 0x72, 0x65, 0x64, + 0x65, 0x72, 0x69, 0x63, 0x20, 0x53, 0x61, 0x76, 0x6F, 0x69, 0x72, 0x20, 0x20, 0x7C, 0xFE, 0x14, + 0xFE, 0x14, 0x00, 0x01, 0x00, 0x07, 0x20, 0x20, 0x47, 0x72, 0x61, 0x70, 0x68, 0x69, 0x63, 0x20, + 0x41, 0x72, 0x74, 0x69, 0x73, 0x74, 0x73, 0x3A, 0x7C, 0x7C, 0xFE, 0x14, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x50, 0x61, 0x74, 0x72, 0x69, + 0x63, 0x6B, 0x20, 0x44, 0x61, 0x68, 0x65, 0x72, 0x20, 0x20, 0x20, 0x20, 0x7C, 0xFE, 0x14, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x54, 0x68, + 0x69, 0x65, 0x72, 0x72, 0x79, 0x20, 0x4C, 0x65, 0x76, 0x61, 0x73, 0x74, 0x72, 0x65, 0x20, 0x7C, + 0xFE, 0x14, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x44, 0x65, 0x6E, 0x69, 0x73, 0x20, 0x4D, 0x65, 0x72, 0x63, 0x69, 0x65, 0x72, + 0x20, 0x20, 0x7C, 0xFE, 0x14, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x54, 0x68, 0x69, 0x65, 0x72, 0x72, 0x79, 0x20, 0x50, 0x65, 0x72, 0x72, + 0x65, 0x61, 0x75, 0x20, 0x20, 0x7C, 0xFE, 0x14, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x43, 0x68, 0x72, 0x69, 0x73, 0x74, 0x69, 0x61, 0x6E, 0x20, 0x52, + 0x6F, 0x62, 0x65, 0x72, 0x74, 0x20, 0x20, 0x20, 0x7C, 0xFE, 0x14, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x46, 0x61, 0x62, 0x72, 0x69, 0x63, + 0x65, 0x20, 0x56, 0x69, 0x73, 0x73, 0x65, 0x72, 0x6F, 0x74, 0x20, 0x7C, 0xFE, 0x14, 0xFE, 0x14, + 0x00, 0x01, 0x00, 0x07, 0x20, 0x20, 0x48, 0x61, 0x72, 0x64, 0x77, 0x61, 0x72, 0x65, 0x20, 0x45, + 0x6E, 0x67, 0x69, 0x6E, 0x65, 0x65, 0x72, 0x3A, 0x7C, 0x7C, 0xFE, 0x14, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x54, 0x68, 0x69, 0x65, 0x72, 0x72, + 0x79, 0x20, 0x47, 0x61, 0x65, 0x72, 0x74, 0x68, 0x6E, 0x65, 0x72, 0x20, 0x7C, 0xFE, 0x14, 0x00, + 0x01, 0x00, 0x07, 0x20, 0x20, 0x53, 0x74, 0x6F, 0x72, 0x79, 0x3A, 0x7C, 0x7C, 0xFE, 0x14, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x50, 0x61, 0x75, 0x6C, 0x20, 0x43, 0x75, 0x69, 0x73, 0x73, 0x65, 0x74, 0x20, 0x20, 0x7C, + 0xFE, 0x14, 0x00, 0x01, 0x00, 0x08, 0x20, 0x20, 0x4C, 0x65, 0x76, 0x65, 0x6C, 0x20, 0x44, 0x65, + 0x73, 0x69, 0x67, 0x6E, 0x3A, 0x7C, 0x7C, 0xFE, 0x14, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x50, 0x61, 0x75, 0x6C, 0x20, + 0x43, 0x75, 0x69, 0x73, 0x73, 0x65, 0x74, 0x20, 0x20, 0x7C, 0xFE, 0x14, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x50, 0x61, 0x74, 0x72, 0x69, + 0x63, 0x6B, 0x20, 0x44, 0x61, 0x68, 0x65, 0x72, 0x20, 0x20, 0x20, 0x20, 0x7C, 0xFE, 0x14, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x44, 0x65, 0x6E, 0x69, 0x73, 0x20, 0x4D, 0x65, 0x72, 0x63, 0x69, 0x65, 0x72, 0x20, 0x20, 0x7C, + 0xFE, 0x14, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x46, 0x72, 0x65, 0x64, 0x65, 0x72, 0x69, 0x63, 0x20, 0x53, 0x61, 0x76, 0x6F, 0x69, 0x72, 0x20, + 0x20, 0x20, 0x7C, 0xFE, 0x14, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x46, 0x61, 0x62, 0x72, 0x69, 0x63, 0x65, 0x20, 0x56, 0x69, 0x73, 0x73, + 0x65, 0x72, 0x6F, 0x74, 0x20, 0x7C, 0xFE, 0x14, 0xFE, 0x14, 0x00, 0x01, 0x00, 0x08, 0x20, 0x20, + 0x4D, 0x75, 0x73, 0x69, 0x63, 0x3A, 0x7C, 0x7C, 0xFE, 0x14, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x4A, 0x65, 0x61, 0x6E, + 0x20, 0x42, 0x61, 0x75, 0x64, 0x6C, 0x6F, 0x74, 0x20, 0x20, 0x7C, 0xFE, 0x14, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x52, 0x61, 0x70, 0x68, + 0x61, 0x65, 0x6C, 0x20, 0x47, 0x65, 0x73, 0x71, 0x75, 0x61, 0x20, 0x20, 0x20, 0x7C, 0xFE, 0x14, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x46, + 0x61, 0x62, 0x72, 0x69, 0x63, 0x65, 0x20, 0x56, 0x69, 0x73, 0x73, 0x65, 0x72, 0x6F, 0x74, 0x20, + 0x7C, 0xFE, 0x14, 0xFE, 0x14, 0x00, 0x01, 0x00, 0x08, 0x20, 0x20, 0x53, 0x6F, 0x75, 0x6E, 0x64, + 0x20, 0x66, 0x78, 0x3A, 0x7C, 0x7C, 0xFE, 0x14, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x42, 0x65, 0x6E, 0x6F, 0x69, 0x73, 0x74, 0x20, 0x41, + 0x72, 0x6F, 0x6E, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7C, 0xFE, 0x14, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x50, 0x68, 0x69, 0x6C, 0x69, 0x70, 0x70, + 0x65, 0x20, 0x43, 0x68, 0x61, 0x73, 0x74, 0x65, 0x6C, 0x20, 0x20, 0x7C, 0xFE, 0x14, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x50, 0x61, 0x75, 0x6C, 0x20, 0x43, 0x75, 0x69, 0x73, 0x73, 0x65, 0x74, 0x20, 0x20, 0x7C, 0xFE, + 0x14, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x46, 0x61, 0x62, 0x72, 0x69, 0x63, 0x65, 0x20, 0x56, 0x69, 0x73, 0x73, 0x65, 0x72, 0x6F, 0x74, + 0x20, 0x7C, 0xFE, 0x14, 0xFE, 0x14, 0x00, 0x01, 0x00, 0x06, 0x20, 0x20, 0x41, 0x63, 0x74, 0x6F, + 0x72, 0x73, 0x3A, 0x7C, 0x7C, 0xFE, 0x14, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x42, 0x65, 0x6E, 0x6F, 0x69, 0x73, 0x74, 0x20, 0x41, 0x72, + 0x6F, 0x6E, 0x20, 0x20, 0x20, 0x20, 0x7C, 0xFE, 0x14, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x50, 0x61, 0x74, 0x72, 0x69, 0x63, 0x6B, 0x20, + 0x44, 0x61, 0x68, 0x65, 0x72, 0x20, 0x20, 0x20, 0x20, 0x7C, 0xFE, 0x14, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x54, 0x68, 0x69, 0x65, 0x72, + 0x72, 0x79, 0x20, 0x4C, 0x65, 0x76, 0x61, 0x73, 0x74, 0x72, 0x65, 0x20, 0x7C, 0xFE, 0x14, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x44, 0x65, 0x6E, 0x69, 0x73, 0x20, 0x4D, 0x65, 0x72, 0x63, 0x69, 0x65, 0x72, 0x20, 0x20, 0x7C, + 0xFE, 0x14, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x54, 0x68, 0x69, 0x65, 0x72, 0x72, 0x79, 0x20, 0x50, 0x65, 0x72, 0x72, 0x65, 0x61, 0x75, + 0x20, 0x20, 0x7C, 0xFE, 0x14, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x43, 0x68, 0x72, 0x69, 0x73, 0x74, 0x69, 0x61, 0x6E, 0x20, 0x52, 0x6F, 0x62, 0x65, + 0x72, 0x74, 0x20, 0x20, 0x20, 0x7C, 0xFE, 0x14, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x46, 0x61, 0x62, 0x72, 0x69, 0x63, 0x65, 0x20, 0x56, + 0x69, 0x73, 0x73, 0x65, 0x72, 0x6F, 0x74, 0x20, 0x7C, 0xFE, 0x14, 0xFE, 0x14, 0x00, 0x01, 0x00, + 0x0A, 0x20, 0x20, 0x56, 0x69, 0x64, 0x65, 0x6F, 0x20, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x6F, + 0x72, 0x73, 0x3A, 0x7C, 0x7C, 0xFE, 0x14, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x09, 0x20, 0x20, 0x20, 0x50, 0x61, 0x74, 0x72, 0x69, 0x63, 0x6B, 0x20, 0x44, + 0x61, 0x68, 0x65, 0x72, 0x20, 0x20, 0x20, 0x20, 0x7C, 0xFE, 0x14, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x54, 0x68, 0x69, 0x65, 0x72, 0x72, + 0x79, 0x20, 0x50, 0x65, 0x72, 0x72, 0x65, 0x61, 0x75, 0x20, 0x20, 0x7C, 0xFE, 0x14, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x46, 0x61, 0x62, + 0x72, 0x69, 0x63, 0x65, 0x20, 0x56, 0x69, 0x73, 0x73, 0x65, 0x72, 0x6F, 0x74, 0x20, 0x7C, 0xFE, + 0x14, 0xFE, 0x14, 0x00, 0x01, 0x00, 0x0A, 0x20, 0x20, 0x56, 0x69, 0x64, 0x65, 0x6F, 0x20, 0x43, + 0x6F, 0x2D, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x6F, 0x72, 0x73, 0x3A, 0x7C, 0x7C, 0xFE, 0x14, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x54, + 0x68, 0x69, 0x65, 0x72, 0x72, 0x79, 0x20, 0x4C, 0x65, 0x76, 0x61, 0x73, 0x74, 0x72, 0x65, 0x20, + 0x7C, 0xFE, 0x14, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x44, 0x65, 0x6E, 0x69, 0x73, 0x20, 0x4D, 0x65, 0x72, 0x63, 0x69, 0x65, + 0x72, 0x20, 0x20, 0x7C, 0xFE, 0x14, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x43, 0x68, 0x72, 0x69, 0x73, 0x74, 0x69, 0x61, 0x6E, 0x20, 0x52, 0x6F, 0x62, + 0x65, 0x72, 0x74, 0x20, 0x20, 0x20, 0x7C, 0xFE, 0x14, 0xFE, 0x14, 0x00, 0x01, 0x00, 0x0A, 0x20, + 0x20, 0x56, 0x69, 0x64, 0x65, 0x6F, 0x20, 0x73, 0x66, 0x78, 0x3A, 0x7C, 0x7C, 0xFE, 0x14, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x50, 0x61, 0x75, 0x6C, 0x20, 0x43, 0x75, 0x69, 0x73, 0x73, 0x65, 0x74, 0x20, 0x20, 0x7C, + 0xFE, 0x14, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x54, 0x68, 0x69, 0x65, 0x72, 0x72, 0x79, 0x20, 0x50, 0x65, 0x72, 0x72, 0x65, 0x61, 0x75, + 0x20, 0x20, 0x7C, 0xFE, 0x14, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x46, 0x61, 0x62, 0x72, 0x69, 0x63, 0x65, 0x20, 0x56, 0x69, 0x73, 0x73, + 0x65, 0x72, 0x6F, 0x74, 0x20, 0x7C, 0xFE, 0x14, 0xFE, 0x14, 0x00, 0x01, 0x00, 0x07, 0x20, 0x20, + 0x54, 0x65, 0x73, 0x74, 0x65, 0x72, 0x73, 0x3A, 0x7C, 0x7C, 0xFE, 0x14, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x50, 0x68, 0x69, 0x6C, + 0x20, 0x42, 0x72, 0x61, 0x64, 0x6C, 0x65, 0x79, 0x20, 0x20, 0x20, 0x20, 0x7C, 0xFE, 0x14, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x50, 0x61, 0x74, 0x72, 0x69, + 0x63, 0x69, 0x61, 0x20, 0x43, 0x75, 0x69, 0x73, 0x73, 0x65, 0x74, 0x20, 0x20, 0x20, 0x20, 0x7C, + 0xFE, 0x14, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x53, 0x69, 0x6D, 0x6F, 0x6E, 0x20, 0x48, 0x61, 0x64, 0x6C, 0x69, 0x6E, 0x67, 0x74, 0x6F, + 0x6E, 0x20, 0x7C, 0xFE, 0x14, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x44, 0x61, 0x6E, 0x69, 0x65, 0x6C, 0x20, 0x4C, 0x6C, 0x65, 0x77, 0x65, 0x6C, + 0x6C, 0x79, 0x6E, 0x20, 0x20, 0x7C, 0xFE, 0x14, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x4A, 0x65, 0x61, 0x6E, 0x2D, 0x50, 0x69, 0x65, 0x72, 0x72, 0x65, 0x20, 0x4C, 0x75, 0x63, + 0x6B, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7C, 0xFE, 0x14, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x4D, 0x61, 0x72, 0x74, 0x69, 0x6E, 0x20, + 0x53, 0x6D, 0x69, 0x74, 0x68, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7C, 0xFE, 0x14, 0xFE, 0x14, + 0x00, 0x01, 0x00, 0x08, 0x20, 0x20, 0x4D, 0x61, 0x6E, 0x79, 0x20, 0x74, 0x68, 0x61, 0x6E, 0x6B, + 0x73, 0x20, 0x74, 0x6F, 0x2E, 0x2E, 0x2E, 0x7C, 0x7C, 0xFE, 0x14, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x4C, 0x6F, 0x72, 0x69, 0x20, 0x43, + 0x68, 0x72, 0x69, 0x73, 0x74, 0x65, 0x6E, 0x73, 0x65, 0x6E, 0x20, 0x7C, 0xFE, 0x14, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x50, 0x61, 0x74, 0x72, 0x69, 0x63, 0x69, + 0x61, 0x20, 0x43, 0x75, 0x69, 0x73, 0x73, 0x65, 0x74, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7C, 0xFE, + 0x14, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x41, 0x6E, 0x6E, 0x65, 0x2D, 0x4D, + 0x61, 0x72, 0x69, 0x65, 0x20, 0x4A, 0x6F, 0x61, 0x73, 0x73, 0x69, 0x6D, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x7C, 0xFE, 0x14, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x4A, 0x65, 0x61, 0x6E, + 0x2D, 0x50, 0x69, 0x65, 0x72, 0x72, 0x65, 0x20, 0x4C, 0x75, 0x63, 0x6B, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x7C, 0xFE, 0x14, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x4D, 0x61, 0x72, 0x63, 0x20, 0x4D, 0x69, 0x6E, 0x69, 0x65, + 0x72, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7C, 0xFE, 0x14, 0xFE, 0x14, 0x00, 0x01, 0x00, 0x05, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x44, 0x69, 0x72, 0x65, 0x63, + 0x74, 0x65, 0x64, 0x20, 0x62, 0x79, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x7C, 0x7C, 0xFE, 0x14, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x50, 0x61, + 0x75, 0x6C, 0x20, 0x43, 0x75, 0x69, 0x73, 0x73, 0x65, 0x74, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x7C, 0xFE, 0x14, 0xFE, 0x14, 0x00, 0x01, 0x00, 0x05, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x50, 0x72, 0x6F, 0x64, 0x75, 0x63, 0x65, 0x64, 0x20, + 0x62, 0x79, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7C, 0x7C, 0xFE, + 0x14, 0x20, 0x20, 0x20, 0x44, 0x65, 0x6C, 0x70, 0x68, 0x69, 0x6E, 0x65, 0x20, 0x53, 0x6F, 0x66, + 0x74, 0x77, 0x61, 0x72, 0x65, 0x20, 0x2F, 0x20, 0x55, 0x53, 0x20, 0x47, 0x4F, 0x4C, 0x44, 0x20, + 0x20, 0x7C, 0xFE, 0x14, 0x20, 0x7C, 0x20, 0x7C, 0x20, 0x7C, 0x20, 0x7C, 0x20, 0x7C, 0x20, 0x7C, + 0x20, 0x7C, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x28, 0x63, 0x29, 0x4D, + 0x43, 0x4D, 0x58, 0x43, 0x49, 0x49, 0x49, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x7C, 0xFE, 0x14, 0xFE, 0x28, 0x00, 0xFF +}; + const uint16_t Cutscene::_creditsCutSeq[] = { 0x00, 0x05, 0x2F, 0x32, 0x36, 0x3E, 0x30, 0x39, 0x3F, 0x14, 0x34, 0xFFFF }; @@ -649,6 +807,29 @@ const uint8_t Cutscene::_protectionShapeData[] = { 0x03, 0xFD, 0x06, 0xFC, 0x00, 0x19 }; +const Cutscene::Text Cutscene::_frTextsTable[] = { + { 1, "VOUS RAMASSEZ L'HOLOCUBE" }, + { 2, "VOUS RAMASSEZ LA CLE" }, + { 4, "VOUS RAMASSEZ LE PISTOLET" }, + { 5, "VOTRE BOUCLIER EST RECHARGE" }, + { 10, "VOUS RAMASSEZ||LA CARTE DE CREDITS" }, + { 14, "LA PILE EST RECHARGEE" }, + { 15, "VOUS RAMASSEZ LA PILE" }, + { 16, "VOUS RAMASSEZ LE TELEPORTEUR" }, + { 18, "VOUS RAMASSEZ LA CARTE ID" }, + { 21, "VOUS LUI DONNEZ LE TELEPORTEUR" }, + { 32, "LA RECEPTIONNISTE VOUS DONNE||UN PAQUET" }, + { 33, "VOUS DONNEZ LE COLIS" }, + { 34, "LE GOUVERNEUR VOUS DONNE||LA CARTE DE TRAVAIL" }, + { 35, "LE FAUSSAIRE VOUS DONNE||LA FAUSSE CARTE ID" }, + { 36, "VOUS RAMASSEZ LE FUSIBLE" }, + { 43, "VOUS LUI TENDEZ||VOS PAPIERS" }, + { 44, "VOUS LUI DONNEZ L'ARGENT" }, + { 49, "IL VOUS DONNE||UNE CEINTURE ANTI-G" }, + { 60, "VOUS RAMASSEZ LE TELE RECEPTEUR" }, + { -1, 0 } +}; + const Level Game::_gameLevels[] = { { "level1", "level1", "level1", 0x00, 1, 3 }, { "level2", "level2", "level2", 0x2F, 1, 4 }, diff --git a/systemstub.h b/systemstub.h index 09fe49f..ed7e5ad 100644 --- a/systemstub.h +++ b/systemstub.h @@ -45,7 +45,7 @@ struct PlayerInput { }; struct SystemStub { - typedef void (*AudioCallback)(void *param, int8_t *stream, int len); + typedef void (*AudioCallback)(void *param, int16_t *stream, int len); PlayerInput _pi; diff --git a/systemstub_sdl.cpp b/systemstub_sdl.cpp index 00e61f4..4438abe 100644 --- a/systemstub_sdl.cpp +++ b/systemstub_sdl.cpp @@ -9,26 +9,31 @@ #include "systemstub.h" #include "util.h" -struct SystemStub_SDL : SystemStub { - enum { - MAX_BLIT_RECTS = 200, - SOUND_SAMPLE_RATE = 22050, - JOYSTICK_COMMIT_VALUE = 3200 - }; +static const int kAudioHz = 22050; +static const int kJoystickCommitValue = 3200; +struct SystemStub_SDL : SystemStub { +#if SDL_VERSION_ATLEAST(2, 0, 0) + SDL_Window *_window; + SDL_Renderer *_renderer; + SDL_Texture *_texture; +#else + SDL_Surface *_surface; +#endif + SDL_PixelFormat *_fmt; + const char *_caption; uint16_t *_screenBuffer; uint16_t *_fadeScreenBuffer; - SDL_Surface *_screenSurface; bool _fullscreen; - int _currentScaler; + int _scaler; uint8_t _overscanColor; uint16_t _pal[256]; int _screenW, _screenH; SDL_Joystick *_joystick; - SDL_Rect _blitRects[MAX_BLIT_RECTS]; + SDL_Rect _blitRects[200]; int _numBlitRects; bool _fadeOnUpdateScreen; - void (*_audioCbProc)(void *, int8_t *, int); + void (*_audioCbProc)(void *, int16_t *, int); void *_audioCbData; int _screenshot; @@ -53,11 +58,11 @@ struct SystemStub_SDL : SystemStub { virtual void unlockAudio(); void processEvent(const SDL_Event &ev, bool &paused); - void prepareGfxMode(); - void cleanupGfxMode(); - void switchGfxMode(bool fullscreen, uint8_t scaler); - void flipGfx(); - void forceGfxRedraw(); + void prepareGraphics(); + void cleanupGraphics(); + void changeGraphics(bool fullscreen, uint8_t scaler); + void flipGraphics(); + void forceGraphicsRedraw(); void drawRect(SDL_Rect *rect, uint8_t color, uint16_t *dst, uint16_t dstPitch); }; @@ -68,14 +73,15 @@ SystemStub *SystemStub_SDL_create() { void SystemStub_SDL::init(const char *title, int w, int h, int scaler, bool fullscreen) { SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_JOYSTICK); SDL_ShowCursor(SDL_DISABLE); - SDL_WM_SetCaption(title, NULL); + _caption = title; memset(&_pi, 0, sizeof(_pi)); _screenBuffer = 0; _fadeScreenBuffer = 0; _fadeOnUpdateScreen = false; _fullscreen = fullscreen; - _currentScaler = scaler; + _scaler = scaler; memset(_pal, 0, sizeof(_pal)); + _screenW = _screenH = 0; setScreenSize(w, h); _joystick = NULL; if (SDL_NumJoysticks() > 0) { @@ -85,9 +91,10 @@ void SystemStub_SDL::init(const char *title, int w, int h, int scaler, bool full } void SystemStub_SDL::destroy() { - cleanupGfxMode(); - if (SDL_JoystickOpened(0)) { + cleanupGraphics(); + if (_joystick) { SDL_JoystickClose(_joystick); + _joystick = 0; } SDL_Quit(); } @@ -108,7 +115,7 @@ void SystemStub_SDL::setScreenSize(int w, int h) { } _screenW = w; _screenH = h; - prepareGfxMode(); + prepareGraphics(); } void SystemStub_SDL::setPalette(const uint8_t *pal, int n) { @@ -117,16 +124,16 @@ void SystemStub_SDL::setPalette(const uint8_t *pal, int n) { uint8_t r = pal[i * 3 + 0]; uint8_t g = pal[i * 3 + 1]; uint8_t b = pal[i * 3 + 2]; - _pal[i] = SDL_MapRGB(_screenSurface->format, r, g, b); + _pal[i] = SDL_MapRGB(_fmt, r, g, b); } } void SystemStub_SDL::setPaletteEntry(int i, const Color *c) { - _pal[i] = SDL_MapRGB(_screenSurface->format, c->r, c->g, c->b); + _pal[i] = SDL_MapRGB(_fmt, c->r, c->g, c->b); } void SystemStub_SDL::getPaletteEntry(int i, Color *c) { - SDL_GetRGB(_pal[i], _screenSurface->format, &c->r, &c->g, &c->b); + SDL_GetRGB(_pal[i], _fmt, &c->r, &c->g, &c->b); } void SystemStub_SDL::setOverscanColor(int i) { @@ -134,7 +141,7 @@ void SystemStub_SDL::setOverscanColor(int i) { } void SystemStub_SDL::copyRect(int x, int y, int w, int h, const uint8_t *buf, int pitch) { - if (_numBlitRects >= MAX_BLIT_RECTS) { + if (_numBlitRects >= ARRAYSIZE(_blitRects)) { warning("SystemStub_SDL::copyRect() Too many blit rects, you may experience graphical glitches"); } else { // extend the dirty region by 1 pixel for scalers accessing 'outer' pixels @@ -211,24 +218,39 @@ static uint16_t blendPixel16(uint16_t colorSrc, uint16_t colorDst, uint32_t mask } void SystemStub_SDL::updateScreen(int shakeOffset) { - const int mul = _scalers[_currentScaler].factor; +#if SDL_VERSION_ATLEAST(2, 0, 0) + // _fadeOnUpdateScreen + SDL_UpdateTexture(_texture, 0, _screenBuffer + _screenW + 1, _screenW * sizeof(uint16_t)); + SDL_RenderClear(_renderer); + if (shakeOffset != 0) { + SDL_Rect r; + r.x = 0; + r.y = shakeOffset * _scalers[_scaler].factor; + SDL_GetRendererOutputSize(_renderer, &r.w, &r.h); + r.h -= r.y; + SDL_RenderCopy(_renderer, _texture, 0, &r); + } else { + SDL_RenderCopy(_renderer, _texture, 0, 0); + } + SDL_RenderPresent(_renderer); +#else + const int mul = _scalers[_scaler].factor; if (_fadeOnUpdateScreen) { const int tempScreenBufferSize = (_screenH + 2) * (_screenW + 2) * sizeof(uint16_t); uint16_t *tempScreenBuffer = (uint16_t *)calloc(tempScreenBufferSize, 1); assert(tempScreenBuffer); - const SDL_PixelFormat *pf = _screenSurface->format; - const uint32_t colorMask = (pf->Gmask << 16) | (pf->Rmask | pf->Bmask); + const uint32_t colorMask = (_fmt->Gmask << 16) | (_fmt->Rmask | _fmt->Bmask); const uint16_t *screenBuffer = _screenBuffer + _screenW + 1; for (int i = 1; i <= 16; ++i) { for (int x = 0; x < _screenH * _screenW; ++x) { tempScreenBuffer[_screenW + 1 + x] = blendPixel16(_fadeScreenBuffer[x], screenBuffer[x], colorMask, i); } - SDL_LockSurface(_screenSurface); - uint16_t *dst = (uint16_t *)_screenSurface->pixels; + SDL_LockSurface(_surface); + uint16_t *dst = (uint16_t *)_surface->pixels; const uint16_t *src = tempScreenBuffer + _screenW + 1; - (*_scalers[_currentScaler].proc)(dst, _screenSurface->pitch, src, _screenW, _screenW, _screenH); - SDL_UnlockSurface(_screenSurface); - SDL_UpdateRect(_screenSurface, 0, 0, _screenW * mul, _screenH * mul); + (*_scalers[_scaler].proc)(dst, _surface->pitch, src, _screenW, _screenW, _screenH); + SDL_UnlockSurface(_surface); + SDL_UpdateRect(_surface, 0, 0, _screenW * mul, _screenH * mul); SDL_Delay(30); } free(tempScreenBuffer); @@ -240,35 +262,36 @@ void SystemStub_SDL::updateScreen(int shakeOffset) { SDL_Rect *br = &_blitRects[i]; int dx = br->x * mul; int dy = br->y * mul; - SDL_LockSurface(_screenSurface); - uint16_t *dst = (uint16_t *)_screenSurface->pixels + dy * _screenSurface->pitch / 2 + dx; + SDL_LockSurface(_surface); + uint16_t *dst = (uint16_t *)_surface->pixels + dy * _surface->pitch / 2 + dx; const uint16_t *src = _screenBuffer + (br->y + 1) * _screenW + (br->x + 1); - (*_scalers[_currentScaler].proc)(dst, _screenSurface->pitch, src, _screenW, br->w, br->h); - SDL_UnlockSurface(_screenSurface); + (*_scalers[_scaler].proc)(dst, _surface->pitch, src, _screenW, br->w, br->h); + SDL_UnlockSurface(_surface); br->x *= mul; br->y *= mul; br->w *= mul; br->h *= mul; } - SDL_UpdateRects(_screenSurface, _numBlitRects, _blitRects); + SDL_UpdateRects(_surface, _numBlitRects, _blitRects); } else { - SDL_LockSurface(_screenSurface); + SDL_LockSurface(_surface); int w = _screenW; int h = _screenH - shakeOffset; - uint16_t *dst = (uint16_t *)_screenSurface->pixels + shakeOffset * mul * _screenSurface->pitch / 2; + uint16_t *dst = (uint16_t *)_surface->pixels + shakeOffset * mul * _surface->pitch / 2; const uint16_t *src = _screenBuffer + _screenW + 1; - (*_scalers[_currentScaler].proc)(dst, _screenSurface->pitch, src, _screenW, w, h); - SDL_UnlockSurface(_screenSurface); + (*_scalers[_scaler].proc)(dst, _surface->pitch, src, _screenW, w, h); + SDL_UnlockSurface(_surface); SDL_Rect r; r.x = 0; r.y = 0; r.w = _screenW * mul; r.h = shakeOffset * mul; - SDL_FillRect(_screenSurface, &r, _pal[_overscanColor]); + SDL_FillRect(_surface, &r, _pal[_overscanColor]); - SDL_UpdateRect(_screenSurface, 0, 0, _screenW * mul, _screenH * mul); + SDL_UpdateRect(_surface, 0, 0, _screenW * mul, _screenH * mul); } +#endif _numBlitRects = 0; } @@ -294,13 +317,26 @@ void SystemStub_SDL::processEvent(const SDL_Event &ev, bool &paused) { case SDL_QUIT: _pi.quit = true; break; +#if SDL_VERSION_ATLEAST(2, 0, 0) + case SDL_WINDOWEVENT: + switch (ev.window.event) { + case SDL_WINDOWEVENT_FOCUS_GAINED: + case SDL_WINDOWEVENT_FOCUS_LOST: + paused = (ev.window.event == SDL_WINDOWEVENT_FOCUS_LOST); + SDL_PauseAudio(paused); + break; + } + break; +#else case SDL_ACTIVEEVENT: if (ev.active.state & SDL_APPINPUTFOCUS) { paused = ev.active.gain == 0; SDL_PauseAudio(paused ? 1 : 0); } break; - case SDL_JOYHATMOTION: +#endif + case SDL_JOYHATMOTION: + if (_joystick) { _pi.dirMask = 0; if (ev.jhat.value & SDL_HAT_UP) { _pi.dirMask |= PlayerInput::DIR_UP; @@ -314,42 +350,32 @@ void SystemStub_SDL::processEvent(const SDL_Event &ev, bool &paused) { if (ev.jhat.value & SDL_HAT_RIGHT) { _pi.dirMask |= PlayerInput::DIR_RIGHT; } - break; - case SDL_JOYAXISMOTION: + } + break; + case SDL_JOYAXISMOTION: + if (_joystick) { switch (ev.jaxis.axis) { case 0: - if (ev.jaxis.value > JOYSTICK_COMMIT_VALUE) { + _pi.dirMask &= ~(PlayerInput::DIR_RIGHT | PlayerInput::DIR_LEFT); + if (ev.jaxis.value > kJoystickCommitValue) { _pi.dirMask |= PlayerInput::DIR_RIGHT; - if (_pi.dirMask & PlayerInput::DIR_LEFT) { - _pi.dirMask &= ~PlayerInput::DIR_LEFT; - } - } else if (ev.jaxis.value < -JOYSTICK_COMMIT_VALUE) { + } else if (ev.jaxis.value < -kJoystickCommitValue) { _pi.dirMask |= PlayerInput::DIR_LEFT; - if (_pi.dirMask & PlayerInput::DIR_RIGHT) { - _pi.dirMask &= ~PlayerInput::DIR_RIGHT; - } - } else { - _pi.dirMask &= ~(PlayerInput::DIR_RIGHT | PlayerInput::DIR_LEFT); } break; case 1: - if (ev.jaxis.value > JOYSTICK_COMMIT_VALUE) { + _pi.dirMask &= ~(PlayerInput::DIR_UP | PlayerInput::DIR_DOWN); + if (ev.jaxis.value > kJoystickCommitValue) { _pi.dirMask |= PlayerInput::DIR_DOWN; - if (_pi.dirMask & PlayerInput::DIR_UP) { - _pi.dirMask &= ~PlayerInput::DIR_UP; - } - } else if (ev.jaxis.value < -JOYSTICK_COMMIT_VALUE) { + } else if (ev.jaxis.value < -kJoystickCommitValue) { _pi.dirMask |= PlayerInput::DIR_UP; - if (_pi.dirMask & PlayerInput::DIR_DOWN) { - _pi.dirMask &= ~PlayerInput::DIR_DOWN; - } - } else { - _pi.dirMask &= ~(PlayerInput::DIR_UP | PlayerInput::DIR_DOWN); } break; } - break; - case SDL_JOYBUTTONDOWN: + } + break; + case SDL_JOYBUTTONDOWN: + if (_joystick) { switch (ev.jbutton.button) { case 0: _pi.space = true; @@ -364,8 +390,10 @@ void SystemStub_SDL::processEvent(const SDL_Event &ev, bool &paused) { _pi.backspace = true; break; } - break; - case SDL_JOYBUTTONUP: + } + break; + case SDL_JOYBUTTONUP: + if (_joystick) { switch (ev.jbutton.button) { case 0: _pi.space = false; @@ -380,7 +408,8 @@ void SystemStub_SDL::processEvent(const SDL_Event &ev, bool &paused) { _pi.backspace = false; break; } - break; + } + break; case SDL_KEYUP: switch (ev.key.keysym.sym) { case SDLK_LEFT: @@ -415,23 +444,26 @@ void SystemStub_SDL::processEvent(const SDL_Event &ev, bool &paused) { case SDL_KEYDOWN: if (ev.key.keysym.mod & KMOD_ALT) { if (ev.key.keysym.sym == SDLK_RETURN) { - switchGfxMode(!_fullscreen, _currentScaler); + changeGraphics(!_fullscreen, _scaler); } else if (ev.key.keysym.sym == SDLK_KP_PLUS || ev.key.keysym.sym == SDLK_PAGEUP) { - uint8_t s = _currentScaler + 1; + uint8_t s = _scaler + 1; if (s < NUM_SCALERS) { - switchGfxMode(_fullscreen, s); + changeGraphics(_fullscreen, s); } } else if (ev.key.keysym.sym == SDLK_KP_MINUS || ev.key.keysym.sym == SDLK_PAGEDOWN) { - int8_t s = _currentScaler - 1; - if (_currentScaler > 0) { - switchGfxMode(_fullscreen, s); + int8_t s = _scaler - 1; + if (_scaler > 0) { + changeGraphics(_fullscreen, s); } } else if (ev.key.keysym.sym == SDLK_s) { +#if SDL_VERSION_ATLEAST(2, 0, 0) +#else char name[32]; snprintf(name, sizeof(name), "screenshot-%03d.bmp", _screenshot); - SDL_SaveBMP(_screenSurface, name); + SDL_SaveBMP(_surface, name); ++_screenshot; debug(DBG_INFO, "Written '%s'", name); +#endif } break; } else if (ev.key.keysym.mod & KMOD_CTRL) { @@ -443,7 +475,7 @@ void SystemStub_SDL::processEvent(const SDL_Event &ev, bool &paused) { _pi.dbgMask ^= PlayerInput::DF_SETLIFE; } else if (ev.key.keysym.sym == SDLK_m) { _pi.mirrorMode = !_pi.mirrorMode; - flipGfx(); + flipGraphics(); } else if (ev.key.keysym.sym == SDLK_s) { _pi.save = true; } else if (ev.key.keysym.sym == SDLK_l) { @@ -506,22 +538,20 @@ uint32_t SystemStub_SDL::getTimeStamp() { return SDL_GetTicks(); } -static void mixAudioS8ToU8(void *param, uint8_t *buf, int len) { +static void mixAudioS16(void *param, uint8_t *buf, int len) { SystemStub_SDL *stub = (SystemStub_SDL *)param; - stub->_audioCbProc(stub->_audioCbData, (int8_t *)buf, len); - for (int i = 0; i < len; ++i) { - buf[i] ^= 0x80; - } + memset(buf, 0, len); + stub->_audioCbProc(stub->_audioCbData, (int16_t *)buf, len / 2); } void SystemStub_SDL::startAudio(AudioCallback callback, void *param) { SDL_AudioSpec desired, obtained; memset(&desired, 0, sizeof(desired)); - desired.freq = SOUND_SAMPLE_RATE; - desired.format = AUDIO_U8; + desired.freq = kAudioHz; + desired.format = AUDIO_S16SYS; desired.channels = 1; desired.samples = 2048; - desired.callback = mixAudioS8ToU8; + desired.callback = mixAudioS16; desired.userdata = this; if (SDL_OpenAudio(&desired, &obtained) == 0) { _audioCbProc = callback; @@ -537,7 +567,7 @@ void SystemStub_SDL::stopAudio() { } uint32_t SystemStub_SDL::getOutputSampleRate() { - return SOUND_SAMPLE_RATE; + return kAudioHz; } void SystemStub_SDL::lockAudio() { @@ -548,17 +578,49 @@ void SystemStub_SDL::unlockAudio() { SDL_UnlockAudio(); } -void SystemStub_SDL::prepareGfxMode() { - int w = _screenW * _scalers[_currentScaler].factor; - int h = _screenH * _scalers[_currentScaler].factor; - _screenSurface = SDL_SetVideoMode(w, h, 16, _fullscreen ? (SDL_FULLSCREEN | SDL_HWSURFACE) : SDL_HWSURFACE); - if (!_screenSurface) { - error("SystemStub_SDL::prepareGfxMode() Unable to allocate _screen buffer"); +void SystemStub_SDL::prepareGraphics() { +#if SDL_VERSION_ATLEAST(2, 0, 0) + switch (_scaler) { + case SCALER_SCALE_2X: + case SCALER_SCALE_3X: + case SCALER_SCALE_4X: + SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "1"); + break; + default: + SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "0"); // nearest pixel sampling + break; } - forceGfxRedraw(); + const int windowW = _screenW * _scalers[_scaler].factor; + const int windowH = _screenH * _scalers[_scaler].factor; + int flags = 0; + if (_fullscreen) { + flags |= SDL_WINDOW_FULLSCREEN_DESKTOP; + } + _window = SDL_CreateWindow(_caption, SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, windowW, windowH, flags); + _renderer = SDL_CreateRenderer(_window, -1, SDL_RENDERER_ACCELERATED); + SDL_RenderSetLogicalSize(_renderer, windowW, windowH); + static const uint32_t kPixelFormat = SDL_PIXELFORMAT_RGB565; + _texture = SDL_CreateTexture(_renderer, kPixelFormat, SDL_TEXTUREACCESS_STREAMING, _screenW, _screenH); + _fmt = SDL_AllocFormat(kPixelFormat); +#else + SDL_WM_SetCaption(_caption, NULL); + const int w = _screenW * _scalers[_scaler].factor; + const int h = _screenH * _scalers[_scaler].factor; + int flags = SDL_HWSURFACE; + if (_fullscreen) { + flags |= SDL_FULLSCREEN; + } + static const int kBitDepth = 16; + _surface = SDL_SetVideoMode(w, h, kBitDepth, flags); + if (!_surface) { + error("SystemStub_SDL::prepareGraphics() Unable to allocate _screen buffer"); + } + _fmt = _surface->format; +#endif + forceGraphicsRedraw(); } -void SystemStub_SDL::cleanupGfxMode() { +void SystemStub_SDL::cleanupGraphics() { if (_screenBuffer) { free(_screenBuffer); _screenBuffer = 0; @@ -567,21 +629,56 @@ void SystemStub_SDL::cleanupGfxMode() { free(_fadeScreenBuffer); _fadeScreenBuffer = 0; } - if (_screenSurface) { - // freed by SDL_Quit() - _screenSurface = 0; +#if SDL_VERSION_ATLEAST(2, 0, 0) + if (_window) { + SDL_DestroyWindow(_window); + _window = 0; } + if (_renderer) { + SDL_DestroyRenderer(_renderer); + _renderer = 0; + } + if (_fmt) { + SDL_FreeFormat(_fmt); + _fmt = 0; + } +#else + if (_surface) { + // freed by SDL_Quit() + _surface = 0; + } +#endif } -void SystemStub_SDL::switchGfxMode(bool fullscreen, uint8_t scaler) { - SDL_FreeSurface(_screenSurface); +void SystemStub_SDL::changeGraphics(bool fullscreen, uint8_t scaler) { + if (_fadeScreenBuffer) { + free(_fadeScreenBuffer); + _fadeScreenBuffer = 0; + } +#if SDL_VERSION_ATLEAST(2, 0, 0) + if (_window) { + SDL_DestroyWindow(_window); + _window = 0; + } + if (_renderer) { + SDL_DestroyRenderer(_renderer); + _renderer = 0; + } + if (_fmt) { + SDL_FreeFormat(_fmt); + _fmt = 0; + } +#else + SDL_FreeSurface(_surface); + _surface = 0; +#endif _fullscreen = fullscreen; - _currentScaler = scaler; - prepareGfxMode(); - forceGfxRedraw(); + _scaler = scaler; + prepareGraphics(); + forceGraphicsRedraw(); } -void SystemStub_SDL::flipGfx() { +void SystemStub_SDL::flipGraphics() { uint16_t scanline[256]; assert(_screenW <= 256); uint16_t *p = _screenBuffer + _screenW + 1; @@ -593,10 +690,10 @@ void SystemStub_SDL::flipGfx() { memcpy(p, scanline, _screenW * sizeof(uint16_t)); p += _screenW; } - forceGfxRedraw(); + forceGraphicsRedraw(); } -void SystemStub_SDL::forceGfxRedraw() { +void SystemStub_SDL::forceGraphicsRedraw() { _numBlitRects = 1; _blitRects[0].x = 0; _blitRects[0].y = 0; diff --git a/video.cpp b/video.cpp index f2c4319..c2a01a1 100644 --- a/video.cpp +++ b/video.cpp @@ -25,6 +25,15 @@ Video::Video(Resource *res, SystemStub *stub) _charFrontColor = 0; _charTransparentColor = 0; _charShadowColor = 0; + _drawChar = 0; + switch (_res->_type) { + case kResourceTypeAmiga: + _drawChar = &Video::AMIGA_drawStringChar; + break; + case kResourceTypeDOS: + _drawChar = &Video::PC_drawStringChar; + break; + } } Video::~Video() { @@ -150,6 +159,12 @@ void Video::setPaletteSlotLE(int palSlot, const uint8_t *palData) { void Video::setTextPalette() { debug(DBG_VIDEO, "Video::setTextPalette()"); setPaletteSlotLE(0xE, _textPal); + if (_res->isAmiga()) { + Color c; + c.r = c.g = 0xEE; + c.b = 0; + _stub->setPaletteEntry(0xE7, &c); + } } void Video::setPalette0xF() { @@ -313,6 +328,35 @@ static void AMIGA_planar8(uint8_t *dst, int w, int h, const uint8_t *src) { } } +static void AMIGA_planar24(uint8_t *dst, int w, int h, const uint8_t *src) { + assert(w == 24); + for (int y = 0; y < h; ++y) { + for (int i = 0; i < 16; ++i) { + int color = 0; + const int mask = 1 << (15 - i); + for (int bit = 0; bit < 4; ++bit) { + if (READ_BE_UINT16(src + bit * 2) & mask) { + color |= 1 << bit; + } + } + dst[i] = color; + } + src += 8; + for (int i = 0; i < 8; ++i) { + int color = 0; + const int mask = 1 << (7 - i); + for (int bit = 0; bit < 4; ++bit) { + if (src[bit] & mask) { + color |= 1 << bit; + } + } + dst[16 + i] = color; + } + src += 4; + dst += w; + } +} + static void AMIGA_planar_mask(uint8_t *dst, int x0, int y0, int w, int h, uint8_t *src, uint8_t *mask, int size) { dst += y0 * 256 + x0; for (int y = 0; y < h; ++y) { @@ -341,22 +385,23 @@ static void AMIGA_planar_mask(uint8_t *dst, int x0, int y0, int w, int h, uint8_ } static void AMIGA_decodeRle(uint8_t *dst, const uint8_t *src) { - int code = READ_BE_UINT16(src) & 0x7FFF; src += 2; - const uint8_t *end = src + code; - do { - code = *src++; + const int size = READ_BE_UINT16(src) & 0x7FFF; src += 2; + for (int i = 0; i < size; ) { + int code = src[i++]; if ((code & 0x80) == 0) { ++code; - memcpy(dst, src, code); - src += code; + if (i + code > size) { + code = size - i; + } + memcpy(dst, &src[i], code); + i += code; } else { code = 1 - ((int8_t)code); - memset(dst, *src, code); - ++src; + memset(dst, src[i], code); + ++i; } dst += code; - } while (src < end); - assert(src == end); + } } static void PC_drawTileMask(uint8_t *dst, int x0, int y0, int w, int h, uint8_t *m, uint8_t *p, int size) { @@ -615,12 +660,17 @@ void Video::AMIGA_decodeLev(int level, int room) { // done in ::PC_setLevelPalettes return; } + // background setPaletteSlotBE(0x0, _mapPalSlot1); - for (int i = 1; i < 5; ++i) { - setPaletteSlotBE(i, _mapPalSlot3); - } - setPaletteSlotBE(0x6, _mapPalSlot3); + // objects + setPaletteSlotBE(0x1, (level == 0 || level == 1) ? _mapPalSlot3 : _mapPalSlot2); + setPaletteSlotBE(0x2, _mapPalSlot3); + setPaletteSlotBE(0x3, _mapPalSlot3); + // conrad + setPaletteSlotBE(0x4, _mapPalSlot3); + // foreground setPaletteSlotBE(0x8, _mapPalSlot1); + // inventory setPaletteSlotBE(0xA, _mapPalSlot3); } @@ -655,6 +705,9 @@ void Video::AMIGA_decodeSpc(const uint8_t *src, int w, int h, uint8_t *dst) { case 32: AMIGA_planar16(dst, w / 16, h, 4, src); break; + case 24: + AMIGA_planar24(dst, w, h, src); + break; default: warning("AMIGA_decodeSpc w=%d unimplemented", w); break; @@ -788,7 +841,7 @@ void Video::AMIGA_drawStringChar(uint8_t *dst, int pitch, const uint8_t *src, ui for (int y = 0; y < 8; ++y) { for (int x = 0; x < 8; ++x) { if (src[x] != 0) { - dst[x] = 0x1D; + dst[x] = color; } } src += 16; @@ -819,15 +872,7 @@ void Video::PC_drawStringChar(uint8_t *dst, int pitch, const uint8_t *src, uint8 const char *Video::drawString(const char *str, int16_t x, int16_t y, uint8_t col) { debug(DBG_VIDEO, "Video::drawString('%s', %d, %d, 0x%X)", str, x, y, col); - void (Video::*drawCharFunc)(uint8_t *, int, const uint8_t *, uint8_t, uint8_t) = 0; - switch (_res->_type) { - case kResourceTypeAmiga: - drawCharFunc = &Video::AMIGA_drawStringChar; - break; - case kResourceTypeDOS: - drawCharFunc = &Video::PC_drawStringChar; - break; - } + drawCharFunc dcf = _drawChar; int len = 0; uint8_t *dst = _frontLayer + y * 256 + x; while (1) { @@ -835,7 +880,7 @@ const char *Video::drawString(const char *str, int16_t x, int16_t y, uint8_t col if (c == 0 || c == 0xB || c == 0xA) { break; } - (this->*drawCharFunc)(dst, 256, _res->_fnt, col, c); + (this->*dcf)(dst, 256, _res->_fnt, col, c); dst += CHAR_W; ++len; } diff --git a/video.h b/video.h index 9a827ee..81c4a48 100644 --- a/video.h +++ b/video.h @@ -13,6 +13,8 @@ struct Resource; struct SystemStub; struct Video { + typedef void (Video::*drawCharFunc)(uint8_t *, int, const uint8_t *, uint8_t, uint8_t); + enum { GAMESCREEN_W = 256, GAMESCREEN_H = 224, @@ -44,6 +46,7 @@ struct Video { uint8_t *_screenBlocks; bool _fullRefresh; uint8_t _shakeOffset; + drawCharFunc _drawChar; Video(Resource *res, SystemStub *stub); ~Video();