From cb9e4696364cd1ba000f620ce5e39ca09faf73a7 Mon Sep 17 00:00:00 2001 From: Gregory Montoir Date: Mon, 21 Mar 2016 00:00:00 +0800 Subject: [PATCH] Import 0.3.0 --- Makefile | 15 +- README | 3 +- collision.cpp | 21 +-- cutscene.cpp | 135 ++++++++++------- cutscene.h | 25 +--- file.cpp | 26 +--- file.h | 19 +-- fs.cpp | 23 +-- fs.h | 19 +-- game.cpp | 246 +++++++++++++++++++------------ game.h | 21 +-- graphics.cpp | 21 +-- graphics.h | 19 +-- intern.h | 45 +++--- locale.cpp | 19 +-- locale.h | 19 +-- main.cpp | 105 +++++++++++--- menu.cpp | 218 +++++++++++++++------------- menu.h | 48 +++--- mixer.cpp | 38 ++--- mixer.h | 28 +--- mod_player.cpp | 316 ++++++++++++++++++++++++++++++---------- mod_player.h | 107 ++------------ ogg_player.cpp | 19 +-- ogg_player.h | 19 +-- piege.cpp | 50 +++---- resource.cpp | 353 ++++++++++++++++++++++++++++++++------------- resource.h | 45 +++--- resource_aba.cpp | 85 +++++++++++ resource_aba.h | 34 +++++ rs.cfg | 14 ++ scaler.cpp | 21 +-- scaler.h | 19 +-- seq_player.cpp | 21 +-- seq_player.h | 19 +-- sfx_player.cpp | 23 +-- sfx_player.h | 19 +-- staticres.cpp | 37 ++--- systemstub.h | 22 +-- systemstub_sdl.cpp | 81 ++++++----- unpack.cpp | 19 +-- unpack.h | 19 +-- util.cpp | 19 +-- util.h | 19 +-- video.cpp | 331 +++++++++++++++++++++++------------------- video.h | 24 ++- 46 files changed, 1548 insertions(+), 1300 deletions(-) create mode 100644 resource_aba.cpp create mode 100644 resource_aba.h create mode 100644 rs.cfg diff --git a/Makefile b/Makefile index e1aca79..480b739 100644 --- a/Makefile +++ b/Makefile @@ -2,24 +2,21 @@ SDL_CFLAGS = `sdl-config --cflags` SDL_LIBS = `sdl-config --libs` VORBIS_LIBS = -lvorbisidec +MODPLUG_LIBS = -lmodplug ZLIB_LIBS = -lz -DEFINES = -DBYPASS_PROTECTION -#DEFINES = -DBYPASS_PROTECTION -DENABLE_PASSWORD_MENU -DNDEBUG - -CXXFLAGS += -Wall -Wuninitialized -Wshadow -Wundef -Wreorder -Wnon-virtual-dtor -Wno-multichar -CXXFLAGS += -MMD $(SDL_CFLAGS) -DUSE_ZLIB $(DEFINES) +CXX := clang++ +CXXFLAGS := -Wall -MMD $(SDL_CFLAGS) -DUSE_ZLIB # -DUSE_MODPLUG 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 scaler.cpp seq_player.cpp \ + mixer.cpp mod_player.cpp ogg_player.cpp piege.cpp resource.cpp resource_aba.cpp \ + scaler.cpp seq_player.cpp \ sfx_player.cpp staticres.cpp systemstub_sdl.cpp unpack.cpp util.cpp video.cpp OBJS = $(SRCS:.cpp=.o) DEPS = $(SRCS:.cpp=.d) -LIBS = $(SDL_LIBS) $(VORBIS_LIBS) $(ZLIB_LIBS) - --include Makefile.local +LIBS = $(SDL_LIBS) $(VORBIS_LIBS) $(MODPLUG_LIBS) $(ZLIB_LIBS) rs: $(OBJS) $(CXX) $(LDFLAGS) -o $@ $(OBJS) $(LIBS) diff --git a/README b/README index fd9629a..908a8c4 100644 --- a/README +++ b/README @@ -1,6 +1,6 @@ REminiscence README -Release version: 0.2.2 ($date) +Release version: 0.3.0 ($date) ------------------------------------------------------------------------------- @@ -74,6 +74,7 @@ In-game hotkeys : Backspace display the inventory Alt Enter toggle windowed/fullscreen mode Alt + and - change video scaler + Alt S write screenshot as .bmp Ctrl S save game state Ctrl L load game state Ctrl + and - change game state slot diff --git a/collision.cpp b/collision.cpp index ccd4a89..f65a8ba 100644 --- a/collision.cpp +++ b/collision.cpp @@ -1,23 +1,12 @@ -/* REminiscence - Flashback interpreter - * Copyright (C) 2005-2015 Gregory Montoir - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . + +/* + * REminiscence - Flashback interpreter + * Copyright (C) 2005-2015 Gregory Montoir (cyx@users.sourceforge.net) */ #include "game.h" #include "resource.h" - +#include "util.h" void Game::col_prepareRoomState() { memset(_col_activeCollisionSlots, 0xFF, sizeof(_col_activeCollisionSlots)); diff --git a/cutscene.cpp b/cutscene.cpp index c9677c4..ddebc70 100644 --- a/cutscene.cpp +++ b/cutscene.cpp @@ -1,28 +1,19 @@ -/* REminiscence - Flashback interpreter - * Copyright (C) 2005-2015 Gregory Montoir - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . + +/* + * REminiscence - Flashback interpreter + * Copyright (C) 2005-2015 Gregory Montoir (cyx@users.sourceforge.net) */ +#include +#include "cutscene.h" #include "resource.h" #include "systemstub.h" +#include "util.h" #include "video.h" -#include "cutscene.h" - Cutscene::Cutscene(Resource *res, SystemStub *stub, Video *vid) : _res(res), _stub(stub), _vid(vid) { + _patchedOffsetsTable = 0; memset(_palBuf, 0, sizeof(_palBuf)); } @@ -51,12 +42,8 @@ void Cutscene::updatePalette() { if (_newPal) { const uint8_t *p = _palBuf; for (int i = 0; i < 32; ++i) { - uint16_t color = READ_BE_UINT16(p); p += 2; - uint8_t t = (color == 0) ? 0 : 3; - Color c; - c.r = ((color & 0xF00) >> 6) | t; - c.g = ((color & 0x0F0) >> 2) | t; - c.b = ((color & 0x00F) << 2) | t; + const uint16_t color = READ_BE_UINT16(p); p += 2; + Color c = Video::AMIGA_convertColor(color); _stub->setPaletteEntry(0xC0 + i, &c); } _newPal = false; @@ -67,21 +54,36 @@ void Cutscene::setPalette() { sync(); updatePalette(); SWAP(_page0, _page1); - _stub->copyRect(0, 0, Video::GAMESCREEN_W, Video::GAMESCREEN_H, _page0, 256); + _stub->copyRect(0, 0, _vid->_w, _vid->_h, _page0, 256); _stub->updateScreen(0); } -void Cutscene::initRotationData(uint16_t a, uint16_t b, uint16_t c) { - int16_t n1 = _sinTable[a]; - int16_t n2 = _cosTable[a]; - int16_t n3 = _sinTable[c]; - int16_t n4 = _cosTable[c]; - int16_t n5 = _sinTable[b]; - int16_t n6 = _cosTable[b]; - _rotData[0] = ((n2 * n6) >> 8) - ((((n4 * n1) >> 8) * n5) >> 8); - _rotData[1] = ((n1 * n6) >> 8) + ((((n4 * n2) >> 8) * n5) >> 8); - _rotData[2] = ( n3 * n1) >> 8; - _rotData[3] = (-n3 * n2) >> 8; +#if 0 +#define SIN(a) (int16_t)(sin(a * M_PI / 180) * 256) +#define COS(a) (int16_t)(cos(a * M_PI / 180) * 256) +#else +#define SIN(a) _sinTable[a] +#define COS(a) _cosTable[a] +#endif + +/* + cos(60) table: 128, math: 127 + cos(120) table:-127, math:-128 + cos(240) table:-128, math:-127 + sin(330) table: 221, math:-127 +*/ + +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); + const int16_t sin_c = SIN(c); + 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; } uint16_t Cutscene::findTextSeparators(const uint8_t *p) { @@ -159,9 +161,9 @@ void Cutscene::drawText(int16_t x, int16_t y, const uint8_t *p, uint16_t color, void Cutscene::swapLayers() { if (_clearScreen == 0) { - memcpy(_page1, _pageC, Video::GAMESCREEN_W * Video::GAMESCREEN_H); + memcpy(_page1, _pageC, _vid->_layerSize); } else { - memset(_page1, 0xC0, Video::GAMESCREEN_W * Video::GAMESCREEN_H); + memset(_page1, 0xC0, _vid->_layerSize); } } @@ -196,9 +198,7 @@ void Cutscene::drawCreditsText() { } } else { *_textCurBuf++ = code; - _textCurBuf = _textCurBuf; - *_textCurBuf = 0xA; - ++_textCurPtr; + *_textCurBuf++ = 0xA; } } else { _creditsTextCounter -= 10; // XXX adjust @@ -216,7 +216,7 @@ void Cutscene::drawProtectionShape(uint8_t shapeNum, int16_t zoom) { int16_t x = 0; int16_t y = 0; zoom += 512; - initRotationData(0, 180, 90); + setRotationTransform(0, 180, 90); const uint8_t *shapeOffsetTable = _protectionShapeData + READ_BE_UINT16(_protectionShapeData + 0x02); const uint8_t *shapeDataTable = _protectionShapeData + READ_BE_UINT16(_protectionShapeData + 0x0E); @@ -272,7 +272,7 @@ void Cutscene::op_waitForSync() { if (_textBuf == _textCurBuf) { _creditsTextCounter = 20; } - memcpy(_page1, _page0, Video::GAMESCREEN_W * Video::GAMESCREEN_H); + memcpy(_page1, _page0, _vid->_layerSize); drawCreditsText(); setPalette(); } while (--n); @@ -364,7 +364,7 @@ void Cutscene::op_drawShape() { drawShape(primitiveVertices, x + dx, y + dy); } if (_clearScreen != 0) { - memcpy(_pageC, _page1, Video::GAMESCREEN_W * Video::GAMESCREEN_H); + memcpy(_pageC, _page1, _vid->_layerSize); } } @@ -609,8 +609,8 @@ void Cutscene::drawShapeScaleRotate(const uint8_t *data, int16_t zoom, int16_t b y = READ_BE_UINT16(data); data += 2; _shape_cur_x16 = _shape_ix - ix; _shape_cur_y16 = _shape_iy - iy; - _shape_ox = _shape_cur_x = _shape_ix + ((_shape_cur_x16 * _rotData[0] + _shape_cur_y16 * _rotData[1]) >> 8); - _shape_oy = _shape_cur_y = _shape_iy + ((_shape_cur_x16 * _rotData[2] + _shape_cur_y16 * _rotData[3]) >> 8); + _shape_ox = _shape_cur_x = _shape_ix + ((_shape_cur_x16 * _rotMat[0] + _shape_cur_y16 * _rotMat[1]) >> 8); + _shape_oy = _shape_cur_y = _shape_iy + ((_shape_cur_x16 * _rotMat[2] + _shape_cur_y16 * _rotMat[3]) >> 8); pr[0].x = 0; pr[0].y = -y; pr[1].x = -x; @@ -655,8 +655,8 @@ void Cutscene::drawShapeScaleRotate(const uint8_t *data, int16_t zoom, int16_t b pt.y = c + READ_BE_UINT16(data); data += 2; _shape_cur_x16 = _shape_ix - pt.x; _shape_cur_y16 = _shape_iy - pt.y; - _shape_cur_x = _shape_ix + ((_rotData[0] * _shape_cur_x16 + _rotData[1] * _shape_cur_y16) >> 8); - _shape_cur_y = _shape_iy + ((_rotData[2] * _shape_cur_x16 + _rotData[3] * _shape_cur_y16) >> 8); + _shape_cur_x = _shape_ix + ((_rotMat[0] * _shape_cur_x16 + _rotMat[1] * _shape_cur_y16) >> 8); + _shape_cur_y = _shape_iy + ((_rotMat[2] * _shape_cur_x16 + _rotMat[3] * _shape_cur_y16) >> 8); if (_shape_count != 0) { _shape_cur_x16 = _shape_prev_x16 + (_shape_cur_x - _shape_prev_x) * zoom * 128; pt.x = ((_shape_cur_x16 + 0x8000) >> 16) + _shape_ix + d; @@ -685,12 +685,12 @@ void Cutscene::drawShapeScaleRotate(const uint8_t *data, int16_t zoom, int16_t b _shape_cur_x16 = _shape_ix - x; _shape_cur_y16 = _shape_iy - y; - a = _shape_ix + ((_rotData[0] * _shape_cur_x16 + _rotData[1] * _shape_cur_y16) >> 8); + a = _shape_ix + ((_rotMat[0] * _shape_cur_x16 + _rotMat[1] * _shape_cur_y16) >> 8); if (_shape_count == 0) { _shape_ox = a; } _shape_cur_x = shape_last_x = a; - a = _shape_iy + ((_rotData[2] * _shape_cur_x16 + _rotData[3] * _shape_cur_y16) >> 8); + a = _shape_iy + ((_rotMat[2] * _shape_cur_x16 + _rotMat[3] * _shape_cur_y16) >> 8); if (_shape_count == 0) { _shape_oy = a; } @@ -712,10 +712,10 @@ void Cutscene::drawShapeScaleRotate(const uint8_t *data, int16_t zoom, int16_t b sx = 0; _shape_cur_x16 = _shape_ix - ix; _shape_cur_y16 = _shape_iy - iy; - a = _shape_ix + ((_rotData[0] * _shape_cur_x16 + _rotData[1] * _shape_cur_y16) >> 8); + a = _shape_ix + ((_rotMat[0] * _shape_cur_x16 + _rotMat[1] * _shape_cur_y16) >> 8); pt2->x = a - shape_last_x; shape_last_x = a; - a = _shape_iy + ((_rotData[2] * _shape_cur_x16 + _rotData[3] * _shape_cur_y16) >> 8); + a = _shape_iy + ((_rotMat[2] * _shape_cur_x16 + _rotMat[3] * _shape_cur_y16) >> 8); pt2->y = a - shape_last_y; shape_last_y = a; ++pt2; @@ -786,7 +786,7 @@ void Cutscene::op_drawShapeScaleRotate() { if (shapeOffset & 0x1000) { r3 = fetchNextCmdWord(); } - initRotationData(r1, r2, r3); + setRotationTransform(r1, r2, r3); const uint8_t *shapeOffsetTable = _polPtr + READ_BE_UINT16(_polPtr + 0x02); const uint8_t *shapeDataTable = _polPtr + READ_BE_UINT16(_polPtr + 0x0E); @@ -822,7 +822,7 @@ void Cutscene::op_drawCreditsText() { if (_textCurBuf == _textBuf) { ++_creditsTextCounter; } - memcpy(_page1, _page0, Video::GAMESCREEN_W * Video::GAMESCREEN_H); + memcpy(_page1, _page0, _vid->_layerSize); _frameDelay = 10; setPalette(); } @@ -842,7 +842,7 @@ void Cutscene::op_drawStringAtPos() { // workaround for buggy cutscene script if (_id == 0x34 && (strId & 0xFFF) == 0x45) { if ((_cmdPtr - _cmdPtrBak) == 0xA) { - _stub->copyRect(0, 0, Video::GAMESCREEN_W, Video::GAMESCREEN_H, _page1, 256); + _stub->copyRect(0, 0, _vid->_w, _vid->_h, _page1, 256); _stub->updateScreen(0); } else { _stub->sleep(15); @@ -965,7 +965,7 @@ void Cutscene::load(uint16_t cutName) { } _res->load(name, Resource::OT_CMP); break; - case kResourceTypePC: + case kResourceTypeDOS: _res->load(name, Resource::OT_CMD); _res->load(name, Resource::OT_POL); _res->load_CINE(); @@ -1019,6 +1019,31 @@ void Cutscene::play() { prepare(); uint16_t cutName = _offsetsTable[_id * 2 + 0]; uint16_t cutOff = _offsetsTable[_id * 2 + 1]; + if (cutName == 0xFFFF && g_options.play_disabled_cutscenes) { + switch (_id) { + case 19: + cutName = 31; // SERRURE + break; + case 22: + case 23: + case 24: + cutName = 12; // ASC + break; + case 30: + case 31: + cutName = 14; // METRO + break; + } + } + if (_patchedOffsetsTable) { + for (int i = 0; _patchedOffsetsTable[i] != 255; i += 3) { + if (_patchedOffsetsTable[i] == _id) { + cutName = _patchedOffsetsTable[i + 1]; + cutOff = _patchedOffsetsTable[i + 2]; + break; + } + } + } if (cutName != 0xFFFF) { load(cutName); mainLoop(cutOff); diff --git a/cutscene.h b/cutscene.h index 4feea07..95a1776 100644 --- a/cutscene.h +++ b/cutscene.h @@ -1,18 +1,7 @@ -/* REminiscence - Flashback interpreter - * Copyright (C) 2005-2015 Gregory Montoir - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . + +/* + * REminiscence - Flashback interpreter + * Copyright (C) 2005-2015 Gregory Montoir (cyx@users.sourceforge.net) */ #ifndef CUTSCENE_H__ @@ -36,6 +25,7 @@ struct Cutscene { 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[]; @@ -47,6 +37,7 @@ struct Cutscene { Resource *_res; SystemStub *_stub; Video *_vid; + const uint8_t *_patchedOffsetsTable; uint16_t _id; uint16_t _deathCutsceneId; @@ -61,7 +52,7 @@ struct Cutscene { uint8_t _palBuf[0x20 * 2]; uint16_t _startOffset; bool _creditsSequence; - uint32_t _rotData[4]; + uint32_t _rotMat[4]; uint8_t _primitiveColor; uint8_t _clearScreen; Point _vertices[0x80]; @@ -97,7 +88,7 @@ struct Cutscene { void copyPalette(const uint8_t *pal, uint16_t num); void updatePalette(); void setPalette(); - void initRotationData(uint16_t a, uint16_t b, uint16_t c); + void setRotationTransform(uint16_t a, uint16_t b, uint16_t c); uint16_t findTextSeparators(const uint8_t *p); void drawText(int16_t x, int16_t y, const uint8_t *p, uint16_t color, uint8_t *page, uint8_t n); void swapLayers(); diff --git a/file.cpp b/file.cpp index aab81d3..c56bfa7 100644 --- a/file.cpp +++ b/file.cpp @@ -1,26 +1,16 @@ -/* REminiscence - Flashback interpreter - * Copyright (C) 2005-2015 Gregory Montoir - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . + +/* + * REminiscence - Flashback interpreter + * Copyright (C) 2005-2015 Gregory Montoir (cyx@users.sourceforge.net) */ +#include +#include "file.h" #include "fs.h" +#include "util.h" #ifdef USE_ZLIB #include "zlib.h" #endif -#include "file.h" - struct File_impl { bool _ioErr; @@ -183,7 +173,7 @@ bool File::open(const char *filename, const char *mode, const char *directory) { if (!_impl) { _impl = new stdFile; } - char path[512]; + char path[MAXPATHLEN]; snprintf(path, sizeof(path), "%s/%s", directory, filename); debug(DBG_FILE, "Open file name '%s' mode '%s' path '%s'", filename, mode, path); return _impl->open(path, mode); diff --git a/file.h b/file.h index 9fd5335..695ebb1 100644 --- a/file.h +++ b/file.h @@ -1,18 +1,7 @@ -/* REminiscence - Flashback interpreter - * Copyright (C) 2005-2015 Gregory Montoir - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . + +/* + * REminiscence - Flashback interpreter + * Copyright (C) 2005-2015 Gregory Montoir (cyx@users.sourceforge.net) */ #ifndef FILE_H__ diff --git a/fs.cpp b/fs.cpp index fbebbd5..0087f47 100644 --- a/fs.cpp +++ b/fs.cpp @@ -1,18 +1,7 @@ -/* REminiscence - Flashback interpreter - * Copyright (C) 2005-2015 Gregory Montoir - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . + +/* + * REminiscence - Flashback interpreter + * Copyright (C) 2005-2015 Gregory Montoir (cyx@users.sourceforge.net) */ #ifdef _WIN32 @@ -21,8 +10,10 @@ #else #include #include +#include #endif #include "fs.h" +#include "util.h" struct FileName { char *name; @@ -129,7 +120,7 @@ void FileSystem_impl::getPathListFromDirectory(const char *dir) { if (de->d_name[0] == '.') { continue; } - char filePath[512]; + char filePath[MAXPATHLEN]; snprintf(filePath, sizeof(filePath), "%s/%s", dir, de->d_name); struct stat st; if (stat(filePath, &st) == 0) { diff --git a/fs.h b/fs.h index 5a464d8..4af3591 100644 --- a/fs.h +++ b/fs.h @@ -1,18 +1,7 @@ -/* REminiscence - Flashback interpreter - * Copyright (C) 2005-2015 Gregory Montoir - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . + +/* + * REminiscence - Flashback interpreter + * Copyright (C) 2005-2015 Gregory Montoir (cyx@users.sourceforge.net) */ #ifndef FS_H__ diff --git a/game.cpp b/game.cpp index d9cd72d..d5e19b1 100644 --- a/game.cpp +++ b/game.cpp @@ -1,28 +1,17 @@ -/* REminiscence - Flashback interpreter - * Copyright (C) 2005-2015 Gregory Montoir - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . + +/* + * REminiscence - Flashback interpreter + * Copyright (C) 2005-2015 Gregory Montoir (cyx@users.sourceforge.net) */ #include #include "file.h" #include "fs.h" -#include "systemstub.h" -#include "unpack.h" #include "game.h" #include "seq_player.h" - +#include "systemstub.h" +#include "unpack.h" +#include "util.h" Game::Game(SystemStub *stub, FileSystem *fs, const char *savePath, int level, ResourceType ver, Language lang) : _cut(&_res, stub, &_vid), _menu(&_res, stub, &_vid), @@ -32,41 +21,38 @@ Game::Game(SystemStub *stub, FileSystem *fs, const char *savePath, int level, Re _inp_demo = 0; _inp_record = false; _inp_replay = false; - _skillLevel = 1; - _currentLevel = level; + _skillLevel = _menu._skill = 1; + _currentLevel = _menu._level = level; } void Game::run() { - _stub->init(g_caption, Video::GAMESCREEN_W, Video::GAMESCREEN_H); - _randSeed = time(0); + _res.init(); _res.load_TEXT(); switch (_res._type) { case kResourceTypeAmiga: _res.load("FONT8", Resource::OT_FNT, "SPR"); break; - case kResourceTypePC: + case kResourceTypeDOS: _res.load("FB_TXT", Resource::OT_FNT); _res._hasSeqData = _fs->exists("INTRO.SEQ"); break; } -#ifndef BYPASS_PROTECTION - while (!handleProtectionScreen()); - if (_stub->_pi.quit) { - return; + if (!g_options.bypass_protection) { + while (!handleProtectionScreen()); + if (_stub->_pi.quit) { + return; + } } -#endif _mix.init(); + _mix._mod._isAmiga = _res.isAmiga(); playCutscene(0x40); playCutscene(0x0D); - if (!_cut._interrupted && _res._type == kResourceTypePC) { - playCutscene(0x4A); - } switch (_res._type) { case kResourceTypeAmiga: @@ -74,7 +60,7 @@ void Game::run() { _res.load("ICON", Resource::OT_ICN, "SPR"); _res.load("PERSO", Resource::OT_SPM); break; - case kResourceTypePC: + case kResourceTypeDOS: _res.load("GLOBAL", Resource::OT_ICN); _res.load("GLOBAL", Resource::OT_SPC); _res.load("PERSO", Resource::OT_SPR); @@ -83,12 +69,20 @@ void Game::run() { break; } + if (_res.isAmiga()) { + displayTitleScreenAmiga(); + _stub->setScreenSize(Video::GAMESCREEN_W, Video::GAMESCREEN_H); + } + while (!_stub->_pi.quit) { - if (_res._type == kResourceTypePC) { + if (_res.isDOS()) { _mix.playMusic(1); - if (!_menu.handleTitleScreen(_skillLevel, _currentLevel)) { + _menu.handleTitleScreen(); + if (_menu._selectedOption == Menu::MENU_OPTION_ITEM_QUIT) { break; } + _skillLevel = _menu._skill; + _currentLevel = _menu._level; _mix.stopMusic(); } if (_currentLevel == 7) { @@ -105,6 +99,7 @@ void Game::run() { loadLevelData(); resetGameState(); _endLoop = false; + _frameTimestamp = _stub->getTimeStamp(); while (!_stub->_pi.quit && !_endLoop) { mainLoop(); } @@ -112,9 +107,45 @@ void Game::run() { } _res.free_TEXT(); - _mix.free(); - _stub->destroy(); + _res.fini(); +} + +void Game::displayTitleScreenAmiga() { + static const char *FILENAME = "present.cmp"; + _res.load_CMP_menu(FILENAME, _res._memBuf); + static const int kW = 320; + static const int kH = 224; + uint8_t *buf = (uint8_t *)malloc(kW * kH); + if (!buf) { + error("Failed to allocate screen buffer w=%d h=%d", kW, kH); + } + _vid.AMIGA_decodeCmp(_res._memBuf + 6, buf); + static const int kAmigaColors[] = { + 0x000, 0x123, 0x012, 0x134, 0x433, 0x453, 0x046, 0x245, + 0x751, 0x455, 0x665, 0x268, 0x961, 0x478, 0x677, 0x786, + 0x17B, 0x788, 0xB84, 0xC92, 0x49C, 0xF00, 0x9A8, 0x9AA, + 0xCA7, 0xEA3, 0x8BD, 0xBBB, 0xEC7, 0xBCD, 0xDDB, 0xEED + }; + for (int i = 0; i < 32; ++i) { + Color c = Video::AMIGA_convertColor(kAmigaColors[i]); + _stub->setPaletteEntry(i, &c); + } + _stub->setScreenSize(kW, kH); + _stub->copyRect(0, 0, kW, kH, buf, kW); + _stub->updateScreen(0); + free(buf); + while (1) { + _stub->processEvents(); + if (_stub->_pi.quit) { + break; + } + if (_stub->_pi.enter) { + _stub->_pi.enter = false; + break; + } + _stub->sleep(30); + } } void Game::resetGameState() { @@ -166,7 +197,7 @@ void Game::mainLoop() { return; } } - memcpy(_vid._frontLayer, _vid._backLayer, Video::GAMESCREEN_W * Video::GAMESCREEN_H); + memcpy(_vid._frontLayer, _vid._backLayer, _vid._layerSize); pge_getInput(); pge_prepare(); col_prepareRoomState(); @@ -221,14 +252,14 @@ void Game::mainLoop() { } void Game::updateTiming() { - static uint32_t tstamp = 0; - int32_t delay = _stub->getTimeStamp() - tstamp; - int32_t pause = (_stub->_pi.dbgMask & PlayerInput::DF_FASTMODE) ? 20 : 30; + static const int frameHz = 30; + int32_t delay = _stub->getTimeStamp() - _frameTimestamp; + int32_t pause = (_stub->_pi.dbgMask & PlayerInput::DF_FASTMODE) ? 20 : (1000 / frameHz); pause -= delay; if (pause > 0) { _stub->sleep(pause); } - tstamp = _stub->getTimeStamp(); + _frameTimestamp = _stub->getTimeStamp(); } void Game::playCutscene(int id) { @@ -292,12 +323,14 @@ void Game::playCutscene(int id) { _mix.playMusic(Cutscene::_musicTable[_cut._id]); } _cut.play(); + if (id == 0xD && !_cut._interrupted && _res.isDOS()) { + _cut._id = 0x4A; + _cut.play(); + } if (id == 0x3D) { _cut.startCredits(); } - if (_cut._interrupted || id != 0x0D) { - _mix.stopMusic(); - } + _mix.stopMusic(); } } @@ -348,7 +381,8 @@ void Game::inp_handleSpecialKeys() { } else { if (_inp_demo->open(demoFile, "zwb", _savePath)) { debug(DBG_INFO, "Recording input keys"); - _inp_demo->writeUint32BE('FBDM'); + static const uint32_t TAG_FBDM = 0x4642444D; + _inp_demo->writeUint32BE(TAG_FBDM); _inp_demo->writeUint16BE(0); _inp_demo->writeUint32BE(_randSeed); record = true; @@ -395,7 +429,7 @@ void Game::showFinalScore() { strcpy(buf, _menu._passwords[7][_skillLevel]); _vid.drawString(buf, (256 - strlen(buf) * 8) / 2, 16, 0xE7); while (!_stub->_pi.quit) { - _stub->copyRect(0, 0, Video::GAMESCREEN_W, Video::GAMESCREEN_H, _vid._frontLayer, 256); + _stub->copyRect(0, 0, _vid._w, _vid._h, _vid._frontLayer, 256); _stub->updateScreen(0); _stub->processEvents(); if (_stub->_pi.enter) { @@ -407,7 +441,7 @@ void Game::showFinalScore() { } bool Game::handleConfigPanel() { - if (_res._type == kResourceTypeAmiga) { + if (_res.isAmiga()) { return true; } const int x = 7; @@ -499,6 +533,10 @@ bool Game::handleConfigPanel() { } break; } + if (_stub->_pi.escape) { + _stub->_pi.escape = false; + break; + } } _vid.fullRefresh(); return (current == MENU_ITEM_ABORT); @@ -512,7 +550,7 @@ bool Game::handleContinueAbort() { uint8_t color_inc = 0xFF; Color col; _stub->getPaletteEntry(0xE4, &col); - memcpy(_vid._tempLayer, _vid._frontLayer, Video::GAMESCREEN_W * Video::GAMESCREEN_H); + memcpy(_vid._tempLayer, _vid._frontLayer, _vid._layerSize); while (timeout >= 0 && !_stub->_pi.quit) { const char *str; str = _res.getMenuString(LocaleData::LI_01_CONTINUE_OR_ABORT); @@ -545,26 +583,28 @@ bool Game::handleContinueAbort() { _stub->_pi.enter = false; return (current_color == 0); } - _stub->copyRect(0, 0, Video::GAMESCREEN_W, Video::GAMESCREEN_H, _vid._frontLayer, 256); + _stub->copyRect(0, 0, _vid._w, _vid._h, _vid._frontLayer, 256); _stub->updateScreen(0); - if (col.b >= 0x3D) { + static const int COLOR_STEP = 8; + static const int COLOR_MIN = 16; + static const int COLOR_MAX = 256 - 16; + if (col.b >= COLOR_MAX) { color_inc = 0; - } - if (col.b < 2) { + } else if (col.b < COLOR_MIN) { color_inc = 0xFF; } if (color_inc == 0xFF) { - col.b += 2; - col.g += 2; + col.b += COLOR_STEP; + col.g += COLOR_STEP; } else { - col.b -= 2; - col.g -= 2; + col.b -= COLOR_STEP; + col.g -= COLOR_STEP; } _stub->setPaletteEntry(0xE4, &col); _stub->processEvents(); _stub->sleep(100); --timeout; - memcpy(_vid._frontLayer, _vid._tempLayer, Video::GAMESCREEN_W * Video::GAMESCREEN_H); + memcpy(_vid._frontLayer, _vid._tempLayer, _vid._layerSize); } return false; } @@ -584,7 +624,7 @@ bool Game::handleProtectionScreen() { int shapeNum = getRandomNumber() % 30; for (int16_t zoom = 2000; zoom != 0; zoom -= 100) { _cut.drawProtectionShape(shapeNum, zoom); - _stub->copyRect(0, 0, Video::GAMESCREEN_W, Video::GAMESCREEN_H, _vid._tempLayer, 256); + _stub->copyRect(0, 0, _vid._w, _vid._h, _vid._tempLayer, 256); _stub->updateScreen(0); _stub->sleep(30); } @@ -595,7 +635,7 @@ bool Game::handleProtectionScreen() { int len = 0; do { codeText[len] = '\0'; - memcpy(_vid._frontLayer, _vid._tempLayer, Video::GAMESCREEN_W * Video::GAMESCREEN_H); + memcpy(_vid._frontLayer, _vid._tempLayer, _vid._layerSize); _menu.drawString("PROTECTION", 2, 11, 5); char buf[20]; snprintf(buf, sizeof(buf), "CODE %d : %s", codeNum + 1, codeText); @@ -654,7 +694,7 @@ void Game::printLevelCode() { if (_printLevelCodeCounter != 0) { char buf[32]; snprintf(buf, sizeof(buf), "CODE: %s", _menu._passwords[_currentLevel][_skillLevel]); - _vid.drawString(buf, (Video::GAMESCREEN_W - strlen(buf) * 8) / 2, 16, 0xE7); + _vid.drawString(buf, (_vid._w - strlen(buf) * 8) / 2, 16, 0xE7); } } } @@ -678,7 +718,7 @@ void Game::drawLevelTexts() { uint8_t icon_num = obj - 1; drawIcon(icon_num, 80, 8, 0xA); uint8_t txt_num = pge->init_PGE->text_num; - const char *str = (const char *)_res._tbn + READ_LE_UINT16(_res._tbn + txt_num * 2); + const char *str = (const char *)_res.getTextString(txt_num); _vid.drawString(str, (176 - strlen(str) * 8) / 2, 26, 0xE6); if (icon_num == 2) { printSaveStateCompleted(); @@ -695,7 +735,7 @@ void Game::drawStoryTexts() { if (_textToDisplay != 0xFFFF) { uint16_t text_col_mask = 0xE8; const uint8_t *str = _res.getGameString(_textToDisplay); - memcpy(_vid._tempLayer, _vid._frontLayer, Video::GAMESCREEN_W * Video::GAMESCREEN_H); + memcpy(_vid._tempLayer, _vid._frontLayer, _vid._layerSize); int textSpeechSegment = 0; while (!_stub->_pi.quit) { drawIcon(_currentInventoryIconNum, 80, 8, 0xA); @@ -735,7 +775,7 @@ void Game::drawStoryTexts() { break; } ++str; - memcpy(_vid._frontLayer, _vid._tempLayer, Video::GAMESCREEN_W * Video::GAMESCREEN_H); + memcpy(_vid._frontLayer, _vid._tempLayer, _vid._layerSize); } _textToDisplay = 0xFFFF; } @@ -811,7 +851,7 @@ void Game::prepareAnimsHelper(LivePGE *pge, int16_t dx, int16_t dy) { w = ((dataPtr[2] >> 7) + 1) * 16; h = dataPtr[2] & 0x7F; break; - case kResourceTypePC: + case kResourceTypeDOS: w = dataPtr[2]; h = dataPtr[3]; dataPtr += 4; @@ -885,7 +925,7 @@ void Game::drawAnimBuffer(uint8_t stateNum, AnimBufferState *state) { _vid.AMIGA_decodeSpm(state->dataPtr, _res._memBuf); drawCharacter(_res._memBuf, state->x, state->y, state->h, state->w, pge->flags); break; - case kResourceTypePC: + case kResourceTypeDOS: if (!(state->dataPtr[-2] & 0x80)) { decodeCharacterFrame(state->dataPtr, _res._memBuf); drawCharacter(_res._memBuf, state->x, state->y, state->h, state->w, pge->flags); @@ -923,7 +963,7 @@ void Game::drawObject(const uint8_t *dataPtr, int16_t x, int16_t y, uint8_t flag count = dataPtr[8]; dataPtr += 9; break; - case kResourceTypePC: + case kResourceTypeDOS: count = dataPtr[5]; dataPtr += 6; break; @@ -956,13 +996,9 @@ void Game::drawObjectFrame(const uint8_t *bankDataPtr, const uint8_t *dataPtr, i switch (_res._type) { case kResourceTypeAmiga: - if (sprite_w == 24) { - // TODO: fix p24xN - return; - } _vid.AMIGA_decodeSpc(src, sprite_w, sprite_h, _res._memBuf); break; - case kResourceTypePC: + case kResourceTypeDOS: _vid.PC_decodeSpc(src, sprite_w, sprite_h, _res._memBuf); break; } @@ -1197,10 +1233,10 @@ int Game::loadMonsterSprites(LivePGE *pge) { _curMonsterFrame = mList[0]; if (_curMonsterNum != mList[1]) { _curMonsterNum = mList[1]; - if (_res._type == kResourceTypeAmiga) { + if (_res.isAmiga()) { _res.load(_monsterNames[1][_curMonsterNum], Resource::OT_SPM); static const uint8_t tab[4] = { 0, 8, 0, 8 }; - const int offset = _vid._mapPalSlot2 * 16 + tab[_curMonsterNum]; + const int offset = _vid._mapPalSlot3 * 16 + tab[_curMonsterNum]; for (int i = 0; i < 8; ++i) { _vid.setPaletteColorBE(0x50 + i, offset + i); } @@ -1220,15 +1256,23 @@ void Game::loadLevelMap() { switch (_res._type) { case kResourceTypeAmiga: if (_currentLevel == 1) { - static const uint8_t tab[64] = { - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 1, 0, - 0, 0, 0, 1, 0, 0, 0, 0, 2, 0, 0, 2, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 1, 1, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0 - }; - const int num = tab[_currentRoom]; + int num = 0; + switch (_currentRoom) { + case 14: + case 19: + case 52: + case 53: + num = 1; + break; + case 11: + case 24: + case 27: + case 56: + num = 2; + break; + } if (num != 0 && _res._levNum != num) { - char name[8]; + char name[9]; snprintf(name, sizeof(name), "level2_%d", num); _res.load(name, Resource::OT_LEV); _res._levNum = num; @@ -1236,8 +1280,12 @@ void Game::loadLevelMap() { } _vid.AMIGA_decodeLev(_currentLevel, _currentRoom); break; - case kResourceTypePC: - _vid.PC_decodeMap(_currentLevel, _currentRoom); + case kResourceTypeDOS: + if (_res._map) { + _vid.PC_decodeMap(_currentLevel, _currentRoom); + } else if (_res._lev) { + _vid.PC_decodeLev(_currentLevel, _currentRoom); + } _vid.PC_setLevelPalettes(); break; } @@ -1248,9 +1296,8 @@ void Game::loadLevelData() { const Level *lvl = &_gameLevels[_currentLevel]; switch (_res._type) { case kResourceTypeAmiga: - if (_fs->exists("demo.lev")) { // demo data files - Cutscene::_namesTable[1] = "HOLOCUBE"; - Cutscene::_namesTable[4] = "CHUTE2"; + if (_res._isDemo) { + _cut._patchedOffsetsTable = Cutscene::_amigaDemoOffsetsTable; static const char *fname1 = "demo"; static const char *fname2 = "demof"; _res.load(fname1, Resource::OT_MBK); @@ -1301,12 +1348,20 @@ void Game::loadLevelData() { _res.load(lvl->nameAmiga, Resource::OT_SGD); } break; - case kResourceTypePC: + case kResourceTypeDOS: _res.load(lvl->name, Resource::OT_MBK); _res.load(lvl->name, Resource::OT_CT); _res.load(lvl->name, Resource::OT_PAL); _res.load(lvl->name, Resource::OT_RP); - _res.load(lvl->name, Resource::OT_MAP); + if (_res._isDemo || g_options.use_tiledata) { // use .BNQ/.LEV/(.SGD) instead of .MAP (PC demo) + if (_currentLevel == 0) { + _res.load(lvl->name, Resource::OT_SGD); + } + _res.load(lvl->name, Resource::OT_LEV); + _res.load(lvl->name, Resource::OT_BNQ); + } else { + _res.load(lvl->name, Resource::OT_MAP); + } _res.load(lvl->name2, Resource::OT_PGE); _res.load(lvl->name2, Resource::OT_OBJ); _res.load(lvl->name2, Resource::OT_ANI); @@ -1315,6 +1370,9 @@ void Game::loadLevelData() { } _cut._id = lvl->cutscene_id; + if (_res._isDemo && _currentLevel == 5) { // PC demo does not include TELEPORT.* + _cut._id = 0xFFFF; + } _curMonsterNum = 0xFFFF; _curMonsterFrame = 0; @@ -1377,7 +1435,7 @@ void Game::drawIcon(uint8_t iconNum, int16_t x, int16_t y, uint8_t colMask) { _vid.AMIGA_decodeIcn(_res._icn, iconNum, buf); } break; - case kResourceTypePC: + case kResourceTypeDOS: _vid.PC_decodeIcn(_res._icn, iconNum, buf); break; } @@ -1392,7 +1450,7 @@ void Game::playSound(uint8_t sfxId, uint8_t softVol) { MixerChunk mc; mc.data = sfx->data; mc.len = sfx->len; - const int freq = _res._type == kResourceTypeAmiga ? 3546897 / 650 : 6000; + const int freq = _res.isAmiga() ? 3546897 / 650 : 6000; _mix.play(&mc, freq, Mixer::MAX_VOLUME >> softVol); } } else { @@ -1476,7 +1534,7 @@ void Game::handleInventory() { drawIcon(76, icon_x_pos, 157, 0xA); selected_pge = items[item_it].live_pge; uint8_t txt_num = items[item_it].init_pge->text_num; - const char *str = (const char *)_res._tbn + READ_LE_UINT16(_res._tbn + txt_num * 2); + const char *str = (const char *)_res.getTextString(txt_num); _vid.drawString(str, (256 - strlen(str) * 8) / 2, 189, 0xED); if (items[item_it].init_pge->init_flags & 4) { char buf[10]; @@ -1593,6 +1651,8 @@ void Game::makeGameStateName(uint8_t slot, char *buf) { sprintf(buf, "rs-level%d-%02d.state", _currentLevel + 1, slot); } +static const uint32_t TAG_FBSV = 0x46425356; + bool Game::saveGameState(uint8_t slot) { bool success = false; char stateFile[20]; @@ -1602,7 +1662,7 @@ bool Game::saveGameState(uint8_t slot) { warning("Unable to save state file '%s'", stateFile); } else { // header - f.writeUint32BE('FBSV'); + f.writeUint32BE(TAG_FBSV); f.writeUint16BE(2); char buf[32]; memset(buf, 0, sizeof(buf)); @@ -1629,7 +1689,7 @@ bool Game::loadGameState(uint8_t slot) { warning("Unable to open state file '%s'", stateFile); } else { uint32_t id = f.readUint32BE(); - if (id != 'FBSV') { + if (id != TAG_FBSV) { warning("Bad save state format"); } else { uint16_t ver = f.readUint16BE(); diff --git a/game.h b/game.h index d1c1e5f..68b8f96 100644 --- a/game.h +++ b/game.h @@ -1,18 +1,7 @@ -/* REminiscence - Flashback interpreter - * Copyright (C) 2005-2015 Gregory Montoir - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . + +/* + * REminiscence - Flashback interpreter + * Copyright (C) 2005-2015 Gregory Montoir (cyx@users.sourceforge.net) */ #ifndef GAME_H__ @@ -94,10 +83,12 @@ struct Game { uint16_t _deathCutsceneCounter; bool _saveStateCompleted; bool _endLoop; + uint32_t _frameTimestamp; Game(SystemStub *, FileSystem *, const char *savePath, int level, ResourceType ver, Language lang); void run(); + void displayTitleScreenAmiga(); void resetGameState(); void mainLoop(); void updateTiming(); diff --git a/graphics.cpp b/graphics.cpp index 1da5a4d..29a173a 100644 --- a/graphics.cpp +++ b/graphics.cpp @@ -1,22 +1,11 @@ -/* REminiscence - Flashback interpreter - * Copyright (C) 2005-2015 Gregory Montoir - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . + +/* + * REminiscence - Flashback interpreter + * Copyright (C) 2005-2015 Gregory Montoir (cyx@users.sourceforge.net) */ #include "graphics.h" - +#include "util.h" void Graphics::setClippingRect(int16_t rx, int16_t ry, int16_t rw, int16_t rh) { debug(DBG_VIDEO, "Graphics::setClippingRect(%d, %d, %d, %d)", rx, ry, rw, rh); diff --git a/graphics.h b/graphics.h index d28421b..f838855 100644 --- a/graphics.h +++ b/graphics.h @@ -1,18 +1,7 @@ -/* REminiscence - Flashback interpreter - * Copyright (C) 2005-2015 Gregory Montoir - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . + +/* + * REminiscence - Flashback interpreter + * Copyright (C) 2005-2015 Gregory Montoir (cyx@users.sourceforge.net) */ #ifndef GRAPHICS_H__ diff --git a/intern.h b/intern.h index 6e9df9e..4c8dae6 100644 --- a/intern.h +++ b/intern.h @@ -1,18 +1,7 @@ -/* REminiscence - Flashback interpreter - * Copyright (C) 2005-2015 Gregory Montoir - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . + +/* + * REminiscence - Flashback interpreter + * Copyright (C) 2005-2015 Gregory Montoir (cyx@users.sourceforge.net) */ #ifndef INTERN_H__ @@ -24,22 +13,19 @@ #include #include -#include "util.h" - +#ifndef ABS #define ABS(x) ((x)<0?-(x):(x)) +#endif +#ifndef MAX #define MAX(x,y) ((x)>(y)?(x):(y)) +#endif +#ifndef MIN #define MIN(x,y) ((x)<(y)?(x):(y)) +#endif #ifndef ARRAYSIZE #define ARRAYSIZE(a) (sizeof(a)/sizeof(a[0])) #endif - -inline void SWAP_UINT16(uint16_t *ptr) { - const uint8_t hi = *ptr >> 8; - const uint8_t lo = *ptr & 255; - *ptr = (lo << 8) | hi; -} - inline uint16_t READ_BE_UINT16(const void *ptr) { const uint8_t *b = (const uint8_t *)ptr; return (b[0] << 8) | b[1]; @@ -77,7 +63,15 @@ enum Language { enum ResourceType { kResourceTypeAmiga, - kResourceTypePC + kResourceTypeDOS +}; + +struct Options { + bool bypass_protection; + bool play_disabled_cutscenes; + bool enable_password_menu; + bool fade_out_palette; + bool use_tiledata; }; struct Color { @@ -219,6 +213,7 @@ struct SoundFx { uint8_t *data; }; +extern Options g_options; extern const char *g_caption; #endif // INTERN_H__ diff --git a/locale.cpp b/locale.cpp index 6ffbdc8..58708fa 100644 --- a/locale.cpp +++ b/locale.cpp @@ -1,18 +1,7 @@ -/* REminiscence - Flashback interpreter - * Copyright (C) 2005-2015 Gregory Montoir - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . + +/* + * REminiscence - Flashback interpreter + * Copyright (C) 2005-2015 Gregory Montoir (cyx@users.sourceforge.net) */ #include "locale.h" diff --git a/locale.h b/locale.h index 1e2823a..cb0e820 100644 --- a/locale.h +++ b/locale.h @@ -1,18 +1,7 @@ -/* REminiscence - Flashback interpreter - * Copyright (C) 2005-2015 Gregory Montoir - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . + +/* + * REminiscence - Flashback interpreter + * Copyright (C) 2005-2015 Gregory Montoir (cyx@users.sourceforge.net) */ #ifndef LOCALE_H__ diff --git a/main.cpp b/main.cpp index 4dd1543..5543b4e 100644 --- a/main.cpp +++ b/main.cpp @@ -1,33 +1,28 @@ -/* REminiscence - Flashback interpreter - * Copyright (C) 2005-2015 Gregory Montoir - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . + +/* + * REminiscence - Flashback interpreter + * Copyright (C) 2005-2015 Gregory Montoir (cyx@users.sourceforge.net) */ +#include #include #include #include "file.h" #include "fs.h" #include "game.h" +#include "scaler.h" #include "systemstub.h" +#include "util.h" static const char *USAGE = "REminiscence - Flashback Interpreter\n" "Usage: %s [OPTIONS]...\n" " --datapath=PATH Path to data files (default 'DATA')\n" " --savepath=PATH Path to save files (default '.')\n" - " --levelnum=NUM Starting level (default '0')"; + " --levelnum=NUM Start level (default '0')\n" + " --fullscreen Start fullscreen\n" + " --scaler=INDEX Graphics scaler\n" +; static int detectVersion(FileSystem *fs) { static const struct { @@ -35,9 +30,11 @@ static int detectVersion(FileSystem *fs) { int type; const char *name; } table[] = { - { "LEVEL1.MAP", kResourceTypePC, "PC" }, + { "DEMO_UK.ABA", kResourceTypeDOS, "DOS (Demo)" }, + { "INTRO.SEQ", kResourceTypeDOS, "DOS CD" }, + { "LEVEL1.MAP", kResourceTypeDOS, "DOS" }, { "LEVEL1.LEV", kResourceTypeAmiga, "Amiga" }, - { "DEMO.LEV", kResourceTypeAmiga, "Amiga" }, + { "DEMO.LEV", kResourceTypeAmiga, "Amiga (Demo)" }, { 0, -1 } }; for (int i = 0; table[i].filename; ++i) { @@ -74,13 +71,65 @@ static Language detectLanguage(FileSystem *fs) { return LANG_EN; } +Options g_options; const char *g_caption = "REminiscence"; +static void initOptions() { + // defaults + g_options.bypass_protection = true; + g_options.play_disabled_cutscenes = false; + g_options.enable_password_menu = false; + g_options.fade_out_palette = true; + // read configuration file + struct { + const char *name; + bool *value; + } opts[] = { + { "bypass_protection", &g_options.bypass_protection }, + { "play_disabled_cutscenes", &g_options.play_disabled_cutscenes }, + { "enable_password_menu", &g_options.enable_password_menu }, + { "fade_out_palette", &g_options.fade_out_palette }, + { "use_tiledata", &g_options.use_tiledata }, + { 0, 0 } + }; + static const char *filename = "rs.cfg"; + FILE *fp = fopen(filename, "rb"); + if (fp) { + char buf[256]; + while (fgets(buf, sizeof(buf), fp)) { + if (buf[0] == '#') { + continue; + } + const char *p = strchr(buf, '='); + if (p) { + ++p; + while (*p && isspace(*p)) { + ++p; + } + if (*p) { + const bool value = (*p == 't' || *p == 'T' || *p == '1'); + for (int i = 0; opts[i].name; ++i) { + if (strncmp(buf, opts[i].name, strlen(opts[i].name)) == 0) { + *opts[i].value = value; + break; + } + } + } + } + } + fclose(fp); + } +} + +static const int DEFAULT_SCALER = SCALER_SCALE_3X; + #undef main int main(int argc, char *argv[]) { const char *dataPath = "DATA"; const char *savePath = "."; int levelNum = 0; + int scaler = DEFAULT_SCALER; + bool fullscreen = false; if (argc == 2) { // data path as the only command line argument struct stat st; @@ -90,9 +139,11 @@ int main(int argc, char *argv[]) { } while (1) { static struct option options[] = { - { "datapath", required_argument, 0, 1 }, - { "savepath", required_argument, 0, 2 }, - { "levelnum", required_argument, 0, 3 }, + { "datapath", required_argument, 0, 1 }, + { "savepath", required_argument, 0, 2 }, + { "levelnum", required_argument, 0, 3 }, + { "fullscreen", no_argument, 0, 4 }, + { "scaler", required_argument, 0, 5 }, { 0, 0, 0, 0 } }; int index; @@ -110,11 +161,21 @@ int main(int argc, char *argv[]) { case 3: levelNum = atoi(optarg); break; + case 4: + fullscreen = true; + break; + case 5: + scaler = atoi(optarg); + if (scaler < 0 || scaler >= NUM_SCALERS) { + scaler = DEFAULT_SCALER; + } + break; default: printf(USAGE, argv[0]); return 0; } } + initOptions(); g_debugMask = DBG_INFO; // DBG_CUT | DBG_VIDEO | DBG_RES | DBG_MENU | DBG_PGE | DBG_GAME | DBG_UNPACK | DBG_COL | DBG_MOD | DBG_SFX | DBG_FILE; FileSystem fs(dataPath); const int version = detectVersion(&fs); @@ -125,8 +186,10 @@ int main(int argc, char *argv[]) { Language language = detectLanguage(&fs); 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); g->run(); delete g; + stub->destroy(); delete stub; return 0; } diff --git a/menu.cpp b/menu.cpp index e9f8095..3f35338 100644 --- a/menu.cpp +++ b/menu.cpp @@ -1,29 +1,20 @@ -/* REminiscence - Flashback interpreter - * Copyright (C) 2005-2015 Gregory Montoir - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . + +/* + * REminiscence - Flashback interpreter + * Copyright (C) 2005-2015 Gregory Montoir (cyx@users.sourceforge.net) */ #include "game.h" +#include "menu.h" #include "resource.h" #include "systemstub.h" +#include "util.h" #include "video.h" -#include "menu.h" - Menu::Menu(Resource *res, SystemStub *stub, Video *vid) : _res(res), _stub(stub), _vid(vid) { + _skill = 1; + _level = 0; } void Menu::drawString(const char *str, int16_t y, int16_t x, uint8_t color) { @@ -73,13 +64,11 @@ void Menu::drawString(const char *str, int16_t y, int16_t x, uint8_t color) { void Menu::drawString2(const char *str, int16_t y, int16_t x) { debug(DBG_MENU, "Menu::drawString2()"); - int len = 0; - while (*str) { - _vid->PC_drawChar((uint8_t)*str, y, x + len); - ++str; - ++len; + int i = 0; + for (; str[i]; ++i) { + _vid->PC_drawChar((uint8_t)str[i], y, x + i); } - _vid->markBlockAsDirty(x * 8, y * 8, len * 8, 8); + _vid->markBlockAsDirty(x * 8, y * 8, i * 8, 8); } void Menu::loadPicture(const char *prefix) { @@ -99,22 +88,20 @@ void Menu::loadPicture(const char *prefix) { void Menu::handleInfoScreen() { debug(DBG_MENU, "Menu::handleInfoScreen()"); _vid->fadeOut(); - switch (_res->_lang) { - case LANG_FR: + if (_res->_lang == LANG_FR) { loadPicture("instru_f"); - break; - case LANG_EN: - case LANG_DE: - case LANG_SP: - case LANG_IT: + } else { loadPicture("instru_e"); - break; } _vid->fullRefresh(); _vid->updateScreen(); do { _stub->sleep(EVENTS_DELAY); _stub->processEvents(); + if (_stub->_pi.escape) { + _stub->_pi.escape = false; + break; + } if (_stub->_pi.enter) { _stub->_pi.enter = false; break; @@ -122,18 +109,22 @@ void Menu::handleInfoScreen() { } while (!_stub->_pi.quit); } -void Menu::handleSkillScreen(uint8_t &new_skill) { +void Menu::handleSkillScreen() { debug(DBG_MENU, "Menu::handleSkillScreen()"); - static const uint8_t option_colors[3][3] = { { 2, 3, 3 }, { 3, 2, 3}, { 3, 3, 2 } }; + static const uint8_t colors[3][3] = { + { 2, 3, 3 }, // easy + { 3, 2, 3 }, // normal + { 3, 3, 2 } // expert + }; _vid->fadeOut(); loadPicture("menu3"); _vid->fullRefresh(); drawString(_res->getMenuString(LocaleData::LI_12_SKILL_LEVEL), 12, 4, 3); - int skill_level = new_skill; + int skill_level = _skill; do { - drawString(_res->getMenuString(LocaleData::LI_13_EASY), 15, 14, option_colors[skill_level][0]); - drawString(_res->getMenuString(LocaleData::LI_14_NORMAL), 17, 14, option_colors[skill_level][1]); - drawString(_res->getMenuString(LocaleData::LI_15_EXPERT), 19, 14, option_colors[skill_level][2]); + drawString(_res->getMenuString(LocaleData::LI_13_EASY), 15, 14, colors[skill_level][0]); + drawString(_res->getMenuString(LocaleData::LI_14_NORMAL), 17, 14, colors[skill_level][1]); + drawString(_res->getMenuString(LocaleData::LI_15_EXPERT), 19, 14, colors[skill_level][2]); _vid->updateScreen(); _stub->sleep(EVENTS_DELAY); @@ -155,16 +146,20 @@ void Menu::handleSkillScreen(uint8_t &new_skill) { skill_level = 0; } } + if (_stub->_pi.escape) { + _stub->_pi.escape = false; + break; + } if (_stub->_pi.enter) { _stub->_pi.enter = false; - new_skill = skill_level; + _skill = skill_level; return; } } while (!_stub->_pi.quit); - new_skill = 1; + _skill = 1; } -bool Menu::handlePasswordScreen(uint8_t &new_skill, uint8_t &new_level) { +bool Menu::handlePasswordScreen() { debug(DBG_MENU, "Menu::handlePasswordScreen()"); _vid->fadeOut(); _vid->_charShadowColor = _charVar1; @@ -206,14 +201,18 @@ bool Menu::handlePasswordScreen(uint8_t &new_skill, uint8_t &new_level) { --len; } } + if (_stub->_pi.escape) { + _stub->_pi.escape = false; + break; + } if (_stub->_pi.enter) { _stub->_pi.enter = false; password[len] = '\0'; for (int level = 0; level < 8; ++level) { for (int skill = 0; skill < 3; ++skill) { if (strcmp(_passwords[level][skill], password) == 0) { - new_level = level; - new_skill = skill; + _level = level; + _skill = skill; return true; } } @@ -224,13 +223,13 @@ bool Menu::handlePasswordScreen(uint8_t &new_skill, uint8_t &new_level) { return false; } -bool Menu::handleLevelScreen(uint8_t &new_skill, uint8_t &new_level) { +bool Menu::handleLevelScreen() { debug(DBG_MENU, "Menu::handleLevelScreen()"); _vid->fadeOut(); loadPicture("menu2"); _vid->fullRefresh(); - uint8_t currentSkill = new_skill; - uint8_t currentLevel = new_level; + int currentSkill = _skill; + int currentLevel = _level; do { static const char *levelTitles[] = { "Titan / The Jungle", @@ -287,56 +286,77 @@ bool Menu::handleLevelScreen(uint8_t &new_skill, uint8_t &new_level) { currentSkill = 0; } } + if (_stub->_pi.escape) { + _stub->_pi.escape = false; + break; + } if (_stub->_pi.enter) { _stub->_pi.enter = false; - new_skill = currentSkill; - new_level = currentLevel; + _skill = currentSkill; + _level = currentLevel; return true; } } while (!_stub->_pi.quit); return false; } -bool Menu::handleTitleScreen(uint8_t &new_skill, uint8_t &new_level) { +void Menu::handleTitleScreen() { debug(DBG_MENU, "Menu::handleTitleScreen()"); - bool quit_loop = false; - int menu_entry = 0; - bool reinit_screen = true; - bool continue_game = true; + _charVar1 = 0; _charVar2 = 0; _charVar3 = 0; _charVar4 = 0; _charVar5 = 0; - static const struct { - int str; - int opt; - } menu_items[] = { - { LocaleData::LI_07_START, MENU_OPTION_ITEM_START }, -#ifdef ENABLE_PASSWORD_MENU - { LocaleData::LI_08_SKILL, MENU_OPTION_ITEM_SKILL }, - { LocaleData::LI_09_PASSWORD, MENU_OPTION_ITEM_PASSWORD }, -#else - { LocaleData::LI_06_LEVEL, MENU_OPTION_ITEM_LEVEL }, -#endif - { LocaleData::LI_10_INFO, MENU_OPTION_ITEM_INFO }, - { LocaleData::LI_11_QUIT, MENU_OPTION_ITEM_QUIT } - }; - static const int menu_items_count = ARRAYSIZE(menu_items); - while (!quit_loop) { - if (reinit_screen) { + + static const int MAX_MENU_ITEMS = 5; + Item menuItems[MAX_MENU_ITEMS]; + int menuItemsCount = 0; + + menuItems[menuItemsCount].str = LocaleData::LI_07_START; + menuItems[menuItemsCount].opt = MENU_OPTION_ITEM_START; + ++menuItemsCount; + if (g_options.enable_password_menu) { + menuItems[menuItemsCount].str = LocaleData::LI_08_SKILL; + menuItems[menuItemsCount].opt = MENU_OPTION_ITEM_SKILL; + ++menuItemsCount; + menuItems[menuItemsCount].str = LocaleData::LI_09_PASSWORD; + menuItems[menuItemsCount].opt = MENU_OPTION_ITEM_PASSWORD; + ++menuItemsCount; + } else { + menuItems[menuItemsCount].str = LocaleData::LI_06_LEVEL; + menuItems[menuItemsCount].opt = MENU_OPTION_ITEM_LEVEL; + ++menuItemsCount; + } + menuItems[menuItemsCount].str = LocaleData::LI_10_INFO; + menuItems[menuItemsCount].opt = MENU_OPTION_ITEM_INFO; + ++menuItemsCount; + menuItems[menuItemsCount].str = LocaleData::LI_11_QUIT; + menuItems[menuItemsCount].opt = MENU_OPTION_ITEM_QUIT; + ++menuItemsCount; + + _selectedOption = -1; + _currentScreen = -1; + _nextScreen = SCREEN_TITLE; + + bool quitLoop = false; + int currentEntry = 0; + + while (!quitLoop) { + if (_nextScreen == SCREEN_TITLE) { _vid->fadeOut(); loadPicture("menu1"); _vid->fullRefresh(); _charVar3 = 1; _charVar4 = 2; - menu_entry = 0; - reinit_screen = false; + currentEntry = 0; + _currentScreen = _nextScreen; + _nextScreen = -1; } - int selected_menu_entry = -1; - const int y_start = 26 - menu_items_count * 2; - for (int i = 0; i < menu_items_count; ++i) { - drawString(_res->getMenuString(menu_items[i].str), y_start + i * 2, 20, (i == menu_entry) ? 2 : 3); + int selectedItem = -1; + const int yPos = 26 - menuItemsCount * 2; + for (int i = 0; i < menuItemsCount; ++i) { + drawString(_res->getMenuString(menuItems[i].str), yPos + i * 2, 20, (i == currentEntry) ? 2 : 3); } _vid->updateScreen(); @@ -345,63 +365,55 @@ bool Menu::handleTitleScreen(uint8_t &new_skill, uint8_t &new_level) { if (_stub->_pi.dirMask & PlayerInput::DIR_UP) { _stub->_pi.dirMask &= ~PlayerInput::DIR_UP; - if (menu_entry != 0) { - --menu_entry; + if (currentEntry != 0) { + --currentEntry; } else { - menu_entry = menu_items_count - 1; + currentEntry = menuItemsCount - 1; } } if (_stub->_pi.dirMask & PlayerInput::DIR_DOWN) { _stub->_pi.dirMask &= ~PlayerInput::DIR_DOWN; - if (menu_entry != menu_items_count - 1) { - ++menu_entry; + if (currentEntry != menuItemsCount - 1) { + ++currentEntry; } else { - menu_entry = 0; + currentEntry = 0; } } if (_stub->_pi.enter) { _stub->_pi.enter = false; - selected_menu_entry = menu_entry; + selectedItem = currentEntry; } - if (selected_menu_entry != -1) { - switch (menu_items[selected_menu_entry].opt) { + if (selectedItem != -1) { + _selectedOption = menuItems[selectedItem].opt; + switch (_selectedOption) { case MENU_OPTION_ITEM_START: - quit_loop = true; + quitLoop = true; break; case MENU_OPTION_ITEM_SKILL: - handleSkillScreen(new_skill); - reinit_screen = true; + _currentScreen = SCREEN_SKILL; + handleSkillScreen(); break; case MENU_OPTION_ITEM_PASSWORD: - if (handlePasswordScreen(new_skill, new_level)) { - quit_loop = true; - } else { - reinit_screen = true; - } + _currentScreen = SCREEN_PASSWORD; + quitLoop = handlePasswordScreen(); break; case MENU_OPTION_ITEM_LEVEL: - if (handleLevelScreen(new_skill, new_level)) { - quit_loop = true; - } else { - reinit_screen = true; - } + _currentScreen = SCREEN_LEVEL; + quitLoop = handleLevelScreen(); break; case MENU_OPTION_ITEM_INFO: + _currentScreen = SCREEN_INFO; handleInfoScreen(); - reinit_screen = true; break; case MENU_OPTION_ITEM_QUIT: - continue_game = false; - quit_loop = true; + quitLoop = true; break; } + _nextScreen = SCREEN_TITLE; } if (_stub->_pi.quit) { - continue_game = false; - quit_loop = true; break; } } - return continue_game; } diff --git a/menu.h b/menu.h index f52f8ec..45b4372 100644 --- a/menu.h +++ b/menu.h @@ -1,18 +1,7 @@ -/* REminiscence - Flashback interpreter - * Copyright (C) 2005-2015 Gregory Montoir - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . + +/* + * REminiscence - Flashback interpreter + * Copyright (C) 2005-2015 Gregory Montoir (cyx@users.sourceforge.net) */ #ifndef MENU_H__ @@ -33,18 +22,36 @@ struct Menu { MENU_OPTION_ITEM_INFO, MENU_OPTION_ITEM_QUIT }; + enum { + SCREEN_TITLE, + SCREEN_SKILL, + SCREEN_PASSWORD, + SCREEN_LEVEL, + SCREEN_INFO + }; enum { EVENTS_DELAY = 80 }; + struct Item { + int str; + int opt; + }; + static const char *_passwords[8][3]; Resource *_res; SystemStub *_stub; Video *_vid; - const char **_textOptions; + int _currentScreen; + int _nextScreen; + int _selectedOption; + + int _skill; + int _level; + uint8_t _charVar1; uint8_t _charVar2; uint8_t _charVar3; @@ -56,11 +63,12 @@ struct Menu { void drawString(const char *str, int16_t y, int16_t x, uint8_t color); void drawString2(const char *str, int16_t y, int16_t x); void loadPicture(const char *prefix); + void handleInfoScreen(); - void handleSkillScreen(uint8_t &new_skill); - bool handlePasswordScreen(uint8_t &new_skill, uint8_t &new_level); - bool handleLevelScreen(uint8_t &new_skill, uint8_t &new_level); - bool handleTitleScreen(uint8_t &new_skill, uint8_t &new_level); + void handleSkillScreen(); + bool handlePasswordScreen(); + bool handleLevelScreen(); + void handleTitleScreen(); }; #endif // MENU_H__ diff --git a/mixer.cpp b/mixer.cpp index 3380a0c..23a28ba 100644 --- a/mixer.cpp +++ b/mixer.cpp @@ -1,23 +1,12 @@ -/* REminiscence - Flashback interpreter - * Copyright (C) 2005-2015 Gregory Montoir - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . + +/* + * REminiscence - Flashback interpreter + * Copyright (C) 2005-2015 Gregory Montoir (cyx@users.sourceforge.net) */ #include "mixer.h" #include "systemstub.h" - +#include "util.h" Mixer::Mixer(FileSystem *fs, SystemStub *stub) : _stub(stub), _musicType(MT_NONE), _mod(this, fs), _ogg(this, fs), _sfx(this) { @@ -144,7 +133,17 @@ void Mixer::stopMusic() { } } -void Mixer::mix(int8_t *buf, int len) { +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); if (_premixHook) { if (!_premixHook(_premixHookData, buf, len)) { @@ -160,12 +159,13 @@ void Mixer::mix(int8_t *buf, int len) { ch->active = false; break; } - int out = resampleLinear(&ch->chunk, ch->chunkPos, ch->chunkInc, FRAC_BITS); - addclamp(buf[pos], out * ch->volume / Mixer::MAX_VOLUME); + const int sample = ch->chunk.getPCM(ch->chunkPos >> FRAC_BITS); + addclamp(buf[pos], sample * ch->volume / Mixer::MAX_VOLUME); ch->chunkPos += ch->chunkInc; } } } + nr(buf, len, out); } void Mixer::addclamp(int8_t& a, int b) { diff --git a/mixer.h b/mixer.h index 4a2397e..e6771e7 100644 --- a/mixer.h +++ b/mixer.h @@ -1,18 +1,7 @@ -/* REminiscence - Flashback interpreter - * Copyright (C) 2005-2015 Gregory Montoir - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . + +/* + * REminiscence - Flashback interpreter + * Copyright (C) 2005-2015 Gregory Montoir (cyx@users.sourceforge.net) */ #ifndef MIXER_H__ @@ -96,13 +85,4 @@ struct Mixer { static void mixCallback(void *param, int8_t *buf, int len); }; -template -int resampleLinear(T *sample, int pos, int step, int fracBits) { - const int inputPos = pos >> fracBits; - const int inputFrac = pos & ((1 << fracBits) - 1); - int out = sample->getPCM(inputPos); - out += (sample->getPCM(inputPos + 1) - out) * inputFrac >> fracBits; - return out; -} - #endif // MIXER_H__ diff --git a/mod_player.cpp b/mod_player.cpp index 3df55bf..af6682f 100644 --- a/mod_player.cpp +++ b/mod_player.cpp @@ -1,33 +1,168 @@ -/* REminiscence - Flashback interpreter - * Copyright (C) 2005-2015 Gregory Montoir - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . + +/* + * REminiscence - Flashback interpreter + * Copyright (C) 2005-2015 Gregory Montoir (cyx@users.sourceforge.net) */ #include "file.h" #include "mixer.h" #include "mod_player.h" +#include "util.h" +#ifdef USE_MODPLUG +#include -ModPlayer::ModPlayer(Mixer *mixer, FileSystem *fs) - : _playing(false), _mix(mixer), _fs(fs) { +struct ModPlayer_impl { + + ModPlugFile *_mf; + ModPlug_Settings _settings; + bool _repeatIntro; + + ModPlayer_impl() + : _mf(0) { + } + + void init(const int rate) { + memset(&_settings, 0, sizeof(_settings)); + ModPlug_GetSettings(&_settings); + _settings.mFlags = MODPLUG_ENABLE_OVERSAMPLING | MODPLUG_ENABLE_NOISE_REDUCTION; + _settings.mChannels = 1; + _settings.mBits = 8; + _settings.mFrequency = rate; + _settings.mResamplingMode = MODPLUG_RESAMPLE_FIR; + ModPlug_SetSettings(&_settings); + } + + bool load(File *f) { + const uint32_t size = f->size(); + uint8_t *data = (uint8_t *)malloc(size); + if (data) { + f->read(data, size); + _mf = ModPlug_Load(data, size); + } + return _mf != 0; + } + + void unload() { + if (_mf) { + ModPlug_Unload(_mf); + _mf = 0; + } + } + + bool mix(int8_t *buf, int len) { + memset(buf, 0, 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; + } + return count > 0; + } + return false; + } +}; + +#else + +struct ModPlayer_impl { + enum { + NUM_SAMPLES = 31, + NUM_TRACKS = 4, + NUM_PATTERNS = 128, + FRAC_BITS = 12, + PAULA_FREQ = 3546897 + }; + + struct SampleInfo { + char name[23]; + uint16_t len; + uint8_t fineTune; + uint8_t volume; + uint16_t repeatPos; + uint16_t repeatLen; + int8_t *data; + + int8_t getPCM(int offset) const { + if (offset < 0) { + offset = 0; + } else if (offset >= (int)len) { + offset = len - 1; + } + return data[offset]; + } + }; + + struct ModuleInfo { + char songName[21]; + SampleInfo samples[NUM_SAMPLES]; + uint8_t numPatterns; + uint8_t patternOrderTable[NUM_PATTERNS]; + uint8_t *patternsTable; + }; + + struct Track { + SampleInfo *sample; + uint8_t volume; + int pos; + int freq; + uint16_t period; + uint16_t periodIndex; + uint16_t effectData; + int vibratoSpeed; + int vibratoAmp; + int vibratoPos; + int portamento; + int portamentoSpeed; + int retriggerCounter; + int delayCounter; + int cutCounter; + }; + + bool _playing; + int _mixingRate; + ModuleInfo _modInfo; + uint8_t _currentPatternOrder; + uint8_t _currentPatternPos; + uint8_t _currentTick; + uint8_t _songSpeed; + uint8_t _songTempo; + int _patternDelay; + int _patternLoopPos; + int _patternLoopCount; + int _samplesLeft; + bool _repeatIntro; + Track _tracks[NUM_TRACKS]; + + ModPlayer_impl(); + + void init(const int rate); + uint16_t findPeriod(uint16_t period, uint8_t fineTune) const; + bool load(File *f); + void unload(); + void handleNote(int trackNum, uint32_t noteData); + void handleTick(); + void applyVolumeSlide(int trackNum, int amount); + void applyVibrato(int trackNum); + void applyPortamento(int trackNum); + void handleEffect(int trackNum, bool tick); + void mixSamples(int8_t *buf, int len); + bool mix(int8_t *buf, int len); +}; + +ModPlayer_impl::ModPlayer_impl() + : _playing(false) { memset(&_modInfo, 0, sizeof(_modInfo)); } -uint16_t ModPlayer::findPeriod(uint16_t period, uint8_t fineTune) const { +uint16_t ModPlayer_impl::findPeriod(uint16_t period, uint8_t fineTune) const { for (int p = 0; p < 36; ++p) { - if (_periodTable[p] == period) { + if (ModPlayer::_periodTable[p] == period) { return fineTune * 36 + p; } } @@ -35,7 +170,11 @@ uint16_t ModPlayer::findPeriod(uint16_t period, uint8_t fineTune) const { return 0; } -void ModPlayer::load(File *f) { +void ModPlayer_impl::init(const int rate) { + _mixingRate = rate; +} + +bool ModPlayer_impl::load(File *f) { f->read(_modInfo.songName, 20); _modInfo.songName[20] = 0; debug(DBG_MOD, "songName = '%s'", _modInfo.songName); @@ -80,9 +219,24 @@ void ModPlayer::load(File *f) { } } } + + _currentPatternOrder = 0; + _currentPatternPos = 0; + _currentTick = 0; + _patternDelay = 0; + _songSpeed = 6; + _songTempo = 125; + _patternLoopPos = 0; + _patternLoopCount = -1; + _samplesLeft = 0; + _repeatIntro = false; + memset(_tracks, 0, sizeof(_tracks)); + _playing = true; + + return true; } -void ModPlayer::unload() { +void ModPlayer_impl::unload() { if (_modInfo.songName[0]) { free(_modInfo.patternsTable); for (int s = 0; s < NUM_SAMPLES; ++s) { @@ -90,49 +244,10 @@ void ModPlayer::unload() { } memset(&_modInfo, 0, sizeof(_modInfo)); } + _playing = false; } -void ModPlayer::play(uint8_t num) { - if (!_playing && num < _modulesFilesCount) { - File f; - bool found = false; - for (uint8_t i = 0; i < ARRAYSIZE(_modulesFiles[num]); ++i) { - if (f.open(_modulesFiles[num][i], "rb", _fs)) { - found = true; - break; - } - } - if (!found) { - warning("Can't find music file %d", num); - } else { - load(&f); - _currentPatternOrder = 0; - _currentPatternPos = 0; - _currentTick = 0; - _patternDelay = 0; - _songSpeed = 6; - _songTempo = 125; - _patternLoopPos = 0; - _patternLoopCount = -1; - _samplesLeft = 0; - _songNum = num; - _introSongHack = false; - memset(_tracks, 0, sizeof(_tracks)); - _mix->setPremixHook(mixCallback, this); - _playing = true; - } - } -} - -void ModPlayer::stop() { - if (_playing) { - _mix->setPremixHook(0, 0); - _playing = false; - } - unload(); -} - -void ModPlayer::handleNote(int trackNum, uint32_t noteData) { +void ModPlayer_impl::handleNote(int trackNum, uint32_t noteData) { Track *tk = &_tracks[trackNum]; uint16_t sampleNum = ((noteData >> 24) & 0xF0) | ((noteData >> 12) & 0xF); uint16_t samplePeriod = (noteData >> 16) & 0xFFF; @@ -146,10 +261,10 @@ void ModPlayer::handleNote(int trackNum, uint32_t noteData) { if (samplePeriod != 0) { tk->periodIndex = findPeriod(samplePeriod, tk->sample->fineTune); if ((effectData >> 8) != 0x3 && (effectData >> 8) != 0x5) { - tk->period = _periodTable[tk->periodIndex]; + tk->period = ModPlayer::_periodTable[tk->periodIndex]; tk->freq = PAULA_FREQ / tk->period; } else { - tk->portamento = _periodTable[tk->periodIndex]; + tk->portamento = ModPlayer::_periodTable[tk->periodIndex]; } tk->vibratoAmp = 0; tk->vibratoSpeed = 0; @@ -158,7 +273,7 @@ void ModPlayer::handleNote(int trackNum, uint32_t noteData) { tk->effectData = effectData; } -void ModPlayer::applyVolumeSlide(int trackNum, int amount) { +void ModPlayer_impl::applyVolumeSlide(int trackNum, int amount) { debug(DBG_MOD, "ModPlayer::applyVolumeSlide(%d, %d)", trackNum, amount); Track *tk = &_tracks[trackNum]; int vol = tk->volume + amount; @@ -170,10 +285,16 @@ void ModPlayer::applyVolumeSlide(int trackNum, int amount) { tk->volume = vol; } -void ModPlayer::applyVibrato(int trackNum) { +void ModPlayer_impl::applyVibrato(int trackNum) { + static const int8_t sineWaveTable[] = { + 0, 24, 49, 74, 97, 120, -115, -95, -76, -59, -44, -32, -21, -12, -6, -3, + -1, -3, -6, -12, -21, -32, -44, -59, -76, -95, -115, 120, 97, 74, 49, 24, + 0, -24, -49, -74, -97, -120, 115, 95, 76, 59, 44, 32, 21, 12, 6, 3, + 1, 3, 6, 12, 21, 32, 44, 59, 76, 95, 115, -120, -97, -74, -49, -24 + }; debug(DBG_MOD, "ModPlayer::applyVibrato(%d)", trackNum); Track *tk = &_tracks[trackNum]; - int vib = tk->vibratoAmp * _sineWaveTable[tk->vibratoPos] / 128; + int vib = tk->vibratoAmp * sineWaveTable[tk->vibratoPos] / 128; if (tk->period + vib != 0) { tk->freq = PAULA_FREQ / (tk->period + vib); } @@ -183,7 +304,7 @@ void ModPlayer::applyVibrato(int trackNum) { } } -void ModPlayer::applyPortamento(int trackNum) { +void ModPlayer_impl::applyPortamento(int trackNum) { debug(DBG_MOD, "ModPlayer::applyPortamento(%d)", trackNum); Track *tk = &_tracks[trackNum]; if (tk->period < tk->portamento) { @@ -196,7 +317,7 @@ void ModPlayer::applyPortamento(int trackNum) { } } -void ModPlayer::handleEffect(int trackNum, bool tick) { +void ModPlayer_impl::handleEffect(int trackNum, bool tick) { Track *tk = &_tracks[trackNum]; uint8_t effectNum = tk->effectData >> 8; uint8_t effectXY = tk->effectData & 0xFF; @@ -209,10 +330,10 @@ void ModPlayer::handleEffect(int trackNum, bool tick) { uint16_t period = tk->period; switch (_currentTick & 3) { case 1: - period = _periodTable[tk->periodIndex + effectX]; + period = ModPlayer::_periodTable[tk->periodIndex + effectX]; break; case 2: - period = _periodTable[tk->periodIndex + effectY]; + period = ModPlayer::_periodTable[tk->periodIndex + effectY]; break; } tk->freq = PAULA_FREQ / period; @@ -411,7 +532,7 @@ void ModPlayer::handleEffect(int trackNum, bool tick) { } } -void ModPlayer::handleTick() { +void ModPlayer_impl::handleTick() { if (!_playing) { return; } @@ -436,9 +557,9 @@ void ModPlayer::handleTick() { // On the amiga version, the introduction cutscene is shorter than the PC version ; // so the music module doesn't synchronize at all with the PC datafiles, here we // add a hack to let the music play longer - if (_songNum == 0 && _currentPatternOrder == 3 && !_introSongHack) { + if (_currentPatternOrder == 3 && _repeatIntro) { _currentPatternOrder = 1; - _introSongHack = true; + _repeatIntro = false; // warning("Introduction module synchronization hack"); } } @@ -456,7 +577,7 @@ void ModPlayer::handleTick() { } } -void ModPlayer::mixSamples(int8_t *buf, int samplesLen) { +void ModPlayer_impl::mixSamples(int8_t *buf, int samplesLen) { for (int i = 0; i < NUM_TRACKS; ++i) { Track *tk = &_tracks[i]; if (tk->sample != 0 && tk->delayCounter == 0) { @@ -465,7 +586,7 @@ void ModPlayer::mixSamples(int8_t *buf, int samplesLen) { int len = si->len << FRAC_BITS; int loopLen = si->repeatLen << FRAC_BITS; int loopPos = si->repeatPos << FRAC_BITS; - int deltaPos = (tk->freq << FRAC_BITS) / _mix->getSampleRate(); + int deltaPos = (tk->freq << FRAC_BITS) / _mixingRate; int curLen = samplesLen; int pos = tk->pos; while (curLen != 0) { @@ -485,7 +606,7 @@ void ModPlayer::mixSamples(int8_t *buf, int samplesLen) { curLen = 0; } while (count--) { - int out = resampleLinear(si, pos, deltaPos, FRAC_BITS); + const int out = si->getPCM(pos >> FRAC_BITS); Mixer::addclamp(*mixbuf++, out * tk->volume / 64); pos += deltaPos; } @@ -495,10 +616,10 @@ void ModPlayer::mixSamples(int8_t *buf, int samplesLen) { } } -bool ModPlayer::mix(int8_t *buf, int len) { +bool ModPlayer_impl::mix(int8_t *buf, int len) { if (_playing) { memset(buf, 0, len); - const int samplesPerTick = _mix->getSampleRate() / (50 * _songTempo / 125); + const int samplesPerTick = _mixingRate / (50 * _songTempo / 125); while (len != 0) { if (_samplesLeft == 0) { handleTick(); @@ -516,7 +637,42 @@ bool ModPlayer::mix(int8_t *buf, int len) { } return _playing; } +#endif + +ModPlayer::ModPlayer(Mixer *mixer, FileSystem *fs) + : _playing(false), _mix(mixer), _fs(fs) { + _impl = new ModPlayer_impl; +} + +ModPlayer::~ModPlayer() { + delete _impl; +} + +void ModPlayer::play(int num) { + if (num < _modulesFilesCount) { + File f; + for (uint8_t i = 0; i < ARRAYSIZE(_modulesFiles[num]); ++i) { + if (f.open(_modulesFiles[num][i], "rb", _fs)) { + _impl->init(_mix->getSampleRate()); + if (_impl->load(&f)) { + _impl->_repeatIntro = (num == 0) && !_isAmiga; + _mix->setPremixHook(mixCallback, _impl); + _playing = true; + } + return; + } + } + } +} + +void ModPlayer::stop() { + if (_playing) { + _mix->setPremixHook(0, 0); + _impl->unload(); + _playing = true; + } +} bool ModPlayer::mixCallback(void *param, int8_t *buf, int len) { - return ((ModPlayer *)param)->mix(buf, len); + return ((ModPlayer_impl *)param)->mix(buf, len); } diff --git a/mod_player.h b/mod_player.h index 4ae6683..d12ee7a 100644 --- a/mod_player.h +++ b/mod_player.h @@ -1,18 +1,7 @@ -/* REminiscence - Flashback interpreter - * Copyright (C) 2005-2015 Gregory Montoir - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . + +/* + * REminiscence - Flashback interpreter + * Copyright (C) 2005-2015 Gregory Montoir (cyx@users.sourceforge.net) */ #ifndef MOD_PLAYER_H__ @@ -20,101 +9,27 @@ #include "intern.h" -struct File; struct FileSystem; struct Mixer; +struct ModPlayer_impl; struct ModPlayer { - enum { - NUM_SAMPLES = 31, - NUM_TRACKS = 4, - NUM_PATTERNS = 128, - FRAC_BITS = 12, - PAULA_FREQ = 3546897 - }; - struct SampleInfo { - char name[23]; - uint16_t len; - uint8_t fineTune; - uint8_t volume; - uint16_t repeatPos; - uint16_t repeatLen; - int8_t *data; - - int8_t getPCM(int offset) const { - if (offset < 0) { - offset = 0; - } else if (offset >= (int)len) { - offset = len - 1; - } - return data[offset]; - } - }; - - struct ModuleInfo { - char songName[21]; - SampleInfo samples[NUM_SAMPLES]; - uint8_t numPatterns; - uint8_t patternOrderTable[NUM_PATTERNS]; - uint8_t *patternsTable; - }; - - struct Track { - SampleInfo *sample; - uint8_t volume; - int pos; - int freq; - uint16_t period; - uint16_t periodIndex; - uint16_t effectData; - int vibratoSpeed; - int vibratoAmp; - int vibratoPos; - int portamento; - int portamentoSpeed; - int retriggerCounter; - int delayCounter; - int cutCounter; - }; - - static const int8_t _sineWaveTable[]; static const uint16_t _periodTable[]; static const char *_modulesFiles[][2]; static const int _modulesFilesCount; - ModuleInfo _modInfo; - uint8_t _currentPatternOrder; - uint8_t _currentPatternPos; - uint8_t _currentTick; - uint8_t _songSpeed; - uint8_t _songTempo; - int _patternDelay; - int _patternLoopPos; - int _patternLoopCount; - int _samplesLeft; - uint8_t _songNum; - bool _introSongHack; + bool _isAmiga; bool _playing; - Track _tracks[NUM_TRACKS]; Mixer *_mix; - FileSystem *_fs; + FileSystem *_fs; + ModPlayer_impl *_impl; - ModPlayer(Mixer *mixer, FileSystem *fs); + ModPlayer(Mixer *mixer, FileSystem *fs); + ~ModPlayer(); - uint16_t findPeriod(uint16_t period, uint8_t fineTune) const; - void load(File *f); - void unload(); - void play(uint8_t num); + void play(int num); void stop(); - void handleNote(int trackNum, uint32_t noteData); - void handleTick(); - void applyVolumeSlide(int trackNum, int amount); - void applyVibrato(int trackNum); - void applyPortamento(int trackNum); - void handleEffect(int trackNum, bool tick); - void mixSamples(int8_t *buf, int len); - bool mix(int8_t *buf, int len); static bool mixCallback(void *param, int8_t *buf, int len); }; diff --git a/ogg_player.cpp b/ogg_player.cpp index 0daf5f7..f37bb0a 100644 --- a/ogg_player.cpp +++ b/ogg_player.cpp @@ -1,18 +1,7 @@ -/* REminiscence - Flashback interpreter - * Copyright (C) 2005-2015 Gregory Montoir - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . + +/* + * REminiscence - Flashback interpreter + * Copyright (C) 2005-2015 Gregory Montoir (cyx@users.sourceforge.net) */ #ifdef USE_TREMOR diff --git a/ogg_player.h b/ogg_player.h index 9d11d7e..9af3b5b 100644 --- a/ogg_player.h +++ b/ogg_player.h @@ -1,18 +1,7 @@ -/* REminiscence - Flashback interpreter - * Copyright (C) 2005-2015 Gregory Montoir - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . + +/* + * REminiscence - Flashback interpreter + * Copyright (C) 2005-2015 Gregory Montoir (cyx@users.sourceforge.net) */ #ifndef OGG_PLAYER_H__ diff --git a/piege.cpp b/piege.cpp index 1ebe781..4ee52e6 100644 --- a/piege.cpp +++ b/piege.cpp @@ -1,25 +1,14 @@ -/* REminiscence - Flashback interpreter - * Copyright (C) 2005-2015 Gregory Montoir - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . + +/* + * REminiscence - Flashback interpreter + * Copyright (C) 2005-2015 Gregory Montoir (cyx@users.sourceforge.net) */ #include "cutscene.h" +#include "game.h" #include "resource.h" #include "systemstub.h" -#include "game.h" - +#include "util.h" void Game::pge_resetGroups() { memset(_pge_groupsTable, 0, sizeof(_pge_groupsTable)); @@ -133,7 +122,7 @@ void Game::pge_process(LivePGE *pge) { pge_setupNextAnimFrame(pge, le); } const uint8_t *anim_data = _res.getAniData(pge->obj_type); - if (READ_LE_UINT16(anim_data) <= pge->anim_seq) { + if (_res._readUint16(anim_data) <= pge->anim_seq) { InitPGE *init_pge = pge->init_PGE; assert(init_pge->obj_node_number < _res._numObjectNodes); ObjectNode *on = _res._objectNodesMap[init_pge->obj_node_number]; @@ -200,7 +189,7 @@ void Game::pge_setupNextAnimFrame(LivePGE *pge, GroupPGE *le) { set_anim: const uint8_t *anim_data = _res.getAniData(pge->obj_type); - uint8_t _dh = READ_LE_UINT16(anim_data); + uint8_t _dh = _res._readUint16(anim_data); uint8_t _dl = pge->anim_seq; const uint8_t *anim_frame = anim_data + 6 + _dl * 4; while (_dh > _dl) { @@ -239,12 +228,12 @@ void Game::pge_playAnimSound(LivePGE *pge, uint16_t arg2) { void Game::pge_setupAnim(LivePGE *pge) { debug(DBG_PGE, "Game::pge_setupAnim() pgeNum=%d", pge - &_pgeLive[0]); const uint8_t *anim_data = _res.getAniData(pge->obj_type); - if (READ_LE_UINT16(anim_data) < pge->anim_seq) { + if (_res._readUint16(anim_data) < pge->anim_seq) { pge->anim_seq = 0; } const uint8_t *anim_frame = anim_data + 6 + pge->anim_seq * 4; - if (READ_LE_UINT16(anim_frame) != 0xFFFF) { - uint16_t fl = READ_LE_UINT16(anim_frame); + if (_res._readUint16(anim_frame) != 0xFFFF) { + uint16_t fl = _res._readUint16(anim_frame); if (pge->flags & 1) { fl ^= 0x8000; pge->pos_x -= (int8_t)anim_frame[2]; @@ -257,10 +246,10 @@ void Game::pge_setupAnim(LivePGE *pge) { pge->flags |= 2; } pge->flags &= ~8; - if (READ_LE_UINT16(anim_data + 4) & 0xFFFF) { + if (_res._readUint16(anim_data + 4) & 0xFFFF) { pge->flags |= 8; } - pge->anim_number = READ_LE_UINT16(anim_frame) & 0x7FFF; + pge->anim_number = _res._readUint16(anim_frame) & 0x7FFF; } } @@ -371,12 +360,12 @@ void Game::pge_prepare() { void Game::pge_setupDefaultAnim(LivePGE *pge) { const uint8_t *anim_data = _res.getAniData(pge->obj_type); - if (pge->anim_seq < READ_LE_UINT16(anim_data)) { + if (pge->anim_seq < _res._readUint16(anim_data)) { pge->anim_seq = 0; } const uint8_t *anim_frame = anim_data + 6 + pge->anim_seq * 4; - if (READ_LE_UINT16(anim_frame) != 0xFFFF) { - uint16_t f = READ_LE_UINT16(anim_data); + if (_res._readUint16(anim_frame) != 0xFFFF) { + uint16_t f = _res._readUint16(anim_data); if (pge->flags & 1) { f ^= 0x8000; } @@ -385,10 +374,10 @@ void Game::pge_setupDefaultAnim(LivePGE *pge) { pge->flags |= 2; } pge->flags &= ~8; - if (READ_LE_UINT16(anim_data + 4) & 0xFFFF) { + if (_res._readUint16(anim_data + 4) & 0xFFFF) { pge->flags |= 8; } - pge->anim_number = READ_LE_UINT16(anim_frame) & 0x7FFF; + pge->anim_number = _res._readUint16(anim_frame) & 0x7FFF; debug(DBG_PGE, "Game::pge_setupDefaultAnim() pgeNum=%d pge->flags=0x%X pge->anim_number=0x%X pge->anim_seq=0x%X", pge - &_pgeLive[0], pge->flags, pge->anim_number, pge->anim_seq); } } @@ -1277,7 +1266,8 @@ int Game::pge_op_setPiegeDefaultAnim(ObjectOpcodeArgs *args) { int16_t r = args->pge->init_PGE->counter_values[args->a]; args->pge->room_location = r; if (r == 1) { - warning("setting _loadMap to true"); + // this happens after death tower, on earth, when Conrad passes + // by the first policeman who's about to shoot him in the back _loadMap = true; } pge_setupDefaultAnim(args->pge); diff --git a/resource.cpp b/resource.cpp index 3d60eed..697d1c1 100644 --- a/resource.cpp +++ b/resource.cpp @@ -1,31 +1,25 @@ -/* REminiscence - Flashback interpreter - * Copyright (C) 2005-2015 Gregory Montoir - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . + +/* + * REminiscence - Flashback interpreter + * Copyright (C) 2005-2015 Gregory Montoir (cyx@users.sourceforge.net) */ #include "file.h" -#include "unpack.h" +#include "fs.h" #include "resource.h" - +#include "unpack.h" +#include "util.h" Resource::Resource(FileSystem *fs, ResourceType ver, Language lang) { memset(this, 0, sizeof(Resource)); + _fs = fs; _type = ver; _lang = lang; - _fs = fs; - _memBuf = (uint8_t *)malloc(256 * 224); + _isDemo = false; + _aba = 0; + _readUint16 = (_type == kResourceTypeDOS) ? READ_LE_UINT16 : READ_BE_UINT16; + _readUint32 = (_type == kResourceTypeDOS) ? READ_LE_UINT32 : READ_BE_UINT32; + _memBuf = (uint8_t *)malloc(320 * 224 + 1024); if (!_memBuf) { error("Unable to allocate temporary memory buffer"); } @@ -56,6 +50,25 @@ Resource::~Resource() { } free(_sfxList); free(_bankData); + delete _aba; +} + +void Resource::init() { + switch (_type) { + case kResourceTypeAmiga: + _isDemo = _fs->exists("demo.lev"); + break; + case kResourceTypeDOS: + if (_fs->exists(ResourceAba::FILENAME)) { + _aba = new ResourceAba(_fs); + _aba->readEntries(); + _isDemo = true; + } + break; + } +} + +void Resource::fini() { } void Resource::clearLevelRes() { @@ -66,6 +79,7 @@ void Resource::clearLevelRes() { free(_lev); _lev = 0; _levNum = -1; free(_sgd); _sgd = 0; + free(_bnq); _bnq = 0; free(_ani); _ani = 0; free_OBJ(); } @@ -119,7 +133,7 @@ void Resource::load_FIB(const char *fileName) { error("I/O error when reading '%s'", _entryName); } } else { - error("Can't open '%s'", _entryName); + error("Cannot open '%s'", _entryName); } } @@ -146,45 +160,86 @@ void Resource::load_SPL_demo() { void Resource::load_MAP_menu(const char *fileName, uint8_t *dstPtr) { debug(DBG_RES, "Resource::load_MAP_menu('%s')", fileName); + static const int kMenuMapSize = 0x3800 * 4; snprintf(_entryName, sizeof(_entryName), "%s.MAP", fileName); File f; if (f.open(_entryName, "rb", _fs)) { - if (f.size() != 0x3800 * 4) { - error("Wrong file size for '%s', %d", _entryName, f.size()); + if (f.read(dstPtr, kMenuMapSize) != kMenuMapSize) { + error("Failed to read '%s'", _entryName); } - f.read(dstPtr, 0x3800 * 4); if (f.ioErr()) { error("I/O error when reading '%s'", _entryName); } - } else { - error("Can't open '%s'", _entryName); + return; + } else if (_aba) { + uint32_t size = 0; + uint8_t *dat = _aba->loadEntry(_entryName, &size); + if (dat) { + if (size != kMenuMapSize) { + error("Unexpected size %d for '%s'", size, _entryName); + } + memcpy(dstPtr, dat, size); + free(dat); + return; + } } + error("Cannot load '%s'", _entryName); } void Resource::load_PAL_menu(const char *fileName, uint8_t *dstPtr) { debug(DBG_RES, "Resource::load_PAL_menu('%s')", fileName); + static const int kMenuPalSize = 768; snprintf(_entryName, sizeof(_entryName), "%s.PAL", fileName); File f; if (f.open(_entryName, "rb", _fs)) { - if (f.size() != 768) { - error("Wrong file size for '%s', %d", _entryName, f.size()); + if (f.read(dstPtr, kMenuPalSize) != kMenuPalSize) { + error("Failed to read '%s'", _entryName); } - f.read(dstPtr, 768); if (f.ioErr()) { error("I/O error when reading '%s'", _entryName); } - } else { - error("Can't open '%s'", _entryName); + return; + } else if (_aba) { + uint32_t size = 0; + uint8_t *dat = _aba->loadEntry(_entryName, &size); + if (dat) { + if (size != kMenuPalSize) { + error("Unexpected size %d for '%s'", size, _entryName); + } + memcpy(dstPtr, dat, size); + free(dat); + return; + } } + error("Cannot load '%s'", _entryName); +} + +void Resource::load_CMP_menu(const char *fileName, uint8_t *dstPtr) { + File f; + if (f.open(fileName, "rb", _fs)) { + const uint32_t size = f.readUint32BE(); + uint8_t *tmp = (uint8_t *)malloc(size); + if (!tmp) { + error("Failed to allocate CMP temporary buffer"); + } + f.read(tmp, size); + if (!delphine_unpack(dstPtr, tmp, size)) { + error("Bad CRC for %s", fileName); + } + free(tmp); + return; + } + error("Cannot load '%s'", fileName); } void Resource::load_SPR_OFF(const char *fileName, uint8_t *sprData) { debug(DBG_RES, "Resource::load_SPR_OFF('%s')", fileName); snprintf(_entryName, sizeof(_entryName), "%s.OFF", fileName); + uint8_t *offData = 0; File f; if (f.open(_entryName, "rb", _fs)) { - int len = f.size(); - uint8_t *offData = (uint8_t *)malloc(len); + const int len = f.size(); + offData = (uint8_t *)malloc(len); if (!offData) { error("Unable to allocate sprite offsets"); } @@ -192,6 +247,10 @@ void Resource::load_SPR_OFF(const char *fileName, uint8_t *sprData) { if (f.ioErr()) { error("I/O error when reading '%s'", _entryName); } + } else if (_aba) { + offData = _aba->loadEntry(_entryName); + } + if (offData) { const uint8_t *p = offData; uint16_t pos; while ((pos = READ_LE_UINT16(p)) != 0xFFFF) { @@ -205,9 +264,9 @@ void Resource::load_SPR_OFF(const char *fileName, uint8_t *sprData) { p += 6; } free(offData); - } else { - error("Can't open '%s'", _entryName); + return; } + error("Cannot load '%s'", _entryName); } void Resource::load_CINE() { @@ -243,8 +302,11 @@ void Resource::load_CINE() { if (f.ioErr()) { error("I/O error when reading '%s'", _entryName); } - } else { - error("Can't open '%s'", _entryName); + } else if (_aba) { + _cine_off = _aba->loadEntry(_entryName); + } + if (!_cine_off) { + error("Cannot load '%s'", _entryName); } } if (_cine_txt == 0) { @@ -260,8 +322,11 @@ void Resource::load_CINE() { if (f.ioErr()) { error("I/O error when reading '%s'", _entryName); } - } else { - error("Can't open '%s'", _entryName); + } else if (_aba) { + _cine_txt = _aba->loadEntry(_entryName); + } + if (!_cine_txt) { + error("Cannot load '%s'", _entryName); } } } @@ -458,6 +523,10 @@ void Resource::load(const char *objName, int objType, const char *ext) { snprintf(_entryName, sizeof(_entryName), "%s.SGD", objName); loadStub = &Resource::load_SGD; break; + case OT_BNQ: + snprintf(_entryName, sizeof(_entryName), "%s.BNQ", objName); + loadStub = &Resource::load_BNQ; + break; case OT_SPM: snprintf(_entryName, sizeof(_entryName), "%s.SPM", objName); loadStub = &Resource::load_SPM; @@ -477,7 +546,70 @@ void Resource::load(const char *objName, int objType, const char *ext) { error("I/O error when reading '%s'", _entryName); } } else { - error("Can't open '%s'", _entryName); + if (_aba) { + uint32_t size; + uint8_t *dat = _aba->loadEntry(_entryName, &size); + if (dat) { + switch (objType) { + case OT_MBK: + _mbk = dat; + break; + case OT_PGE: + decodePGE(dat, size); + break; + case OT_PAL: + _pal = dat; + break; + case OT_CT: + if (!delphine_unpack((uint8_t *)_ctData, dat, size)) { + error("Bad CRC for '%s'", _entryName); + } + free(dat); + break; + case OT_SPC: + _spc = dat; + _numSpc = READ_BE_UINT16(_spc) / 2; + break; + case OT_RP: + if (size != 0x4A) { + error("Unexpected size %d for '%s'", size, _entryName); + } + memcpy(_rp, dat, size); + free(dat); + break; + case OT_ICN: + _icn = dat; + break; + case OT_FNT: + _fnt = dat; + break; + case OT_OBJ: + _numObjectNodes = READ_LE_UINT16(dat); + assert(_numObjectNodes == 230); + decodeOBJ(dat + 2, size - 2); + break; + case OT_ANI: + _ani = dat; + break; + case OT_TBN: + _tbn = dat; + break; + case OT_CMD: + _cmd = dat; + break; + case OT_POL: + _pol = dat; + break; + case OT_BNQ: + _bnq = dat; + break; + default: + error("Cannot load '%s' type %d", _entryName, objType); + } + return; + } + } + error("Cannot open '%s'", _entryName); } } @@ -702,7 +834,7 @@ void Resource::decodeOBJ(const uint8_t *tmp, int size) { int tmpOffset = 0; _numObjectNodes = 230; for (int i = 0; i < _numObjectNodes; ++i) { - offsets[i] = READ_BE_UINT32(tmp + tmpOffset); tmpOffset += 4; + offsets[i] = _readUint32(tmp + tmpOffset); tmpOffset += 4; } offsets[_numObjectNodes] = size; int numObjectsCount = 0; @@ -724,23 +856,23 @@ void Resource::decodeOBJ(const uint8_t *tmp, int size) { error("Unable to allocate ObjectNode num=%d", i); } const uint8_t *objData = tmp + offsets[i]; - on->last_obj_number = READ_BE_UINT16(objData); objData += 2; + on->last_obj_number = _readUint16(objData); objData += 2; on->num_objects = objectsCount[iObj]; on->objects = (Object *)malloc(sizeof(Object) * on->num_objects); for (int j = 0; j < on->num_objects; ++j) { Object *obj = &on->objects[j]; - obj->type = READ_BE_UINT16(objData); objData += 2; + obj->type = _readUint16(objData); objData += 2; obj->dx = *objData++; obj->dy = *objData++; - obj->init_obj_type = READ_BE_UINT16(objData); objData += 2; + obj->init_obj_type = _readUint16(objData); objData += 2; obj->opcode2 = *objData++; obj->opcode1 = *objData++; obj->flags = *objData++; obj->opcode3 = *objData++; - obj->init_obj_number = READ_BE_UINT16(objData); objData += 2; - obj->opcode_arg1 = READ_BE_UINT16(objData); objData += 2; - obj->opcode_arg2 = READ_BE_UINT16(objData); objData += 2; - obj->opcode_arg3 = READ_BE_UINT16(objData); objData += 2; + obj->init_obj_number = _readUint16(objData); objData += 2; + obj->opcode_arg1 = _readUint16(objData); objData += 2; + obj->opcode_arg2 = _readUint16(objData); objData += 2; + obj->opcode_arg3 = _readUint16(objData); objData += 2; debug(DBG_RES, "obj_node=%d obj=%d op1=0x%X op2=0x%X op3=0x%X", i, j, obj->opcode2, obj->opcode1, obj->opcode3); } ++iObj; @@ -753,13 +885,20 @@ void Resource::decodeOBJ(const uint8_t *tmp, int size) { void Resource::load_PGE(File *f) { debug(DBG_RES, "Resource::load_PGE()"); - int len = f->size() - 2; - _pgeNum = f->readUint16LE(); if (_type == kResourceTypeAmiga) { - SWAP_UINT16(&_pgeNum); + const int size = f->size(); + uint8_t *tmp = (uint8_t *)malloc(size); + if (!tmp) { + error("Unable to allocate PGE temporary buffer"); + } + f->read(tmp, size); + decodePGE(tmp, size); + free(tmp); + return; } + _pgeNum = f->readUint16LE(); memset(_pgeInit, 0, sizeof(_pgeInit)); - debug(DBG_RES, "len=%d _pgeNum=%d", len, _pgeNum); + debug(DBG_RES, "_pgeNum=%d", _pgeNum); for (uint16_t i = 0; i < _pgeNum; ++i) { InitPGE *pge = &_pgeInit[i]; pge->type = f->readUint16LE(); @@ -784,56 +923,46 @@ void Resource::load_PGE(File *f) { f->readByte(); pge->text_num = f->readUint16LE(); } - if (_type == kResourceTypeAmiga) { - for (uint16_t i = 0; i < _pgeNum; ++i) { - InitPGE *pge = &_pgeInit[i]; - SWAP_UINT16((uint16_t *)&pge->type); - SWAP_UINT16((uint16_t *)&pge->pos_x); - SWAP_UINT16((uint16_t *)&pge->pos_y); - SWAP_UINT16((uint16_t *)&pge->obj_node_number); - SWAP_UINT16((uint16_t *)&pge->life); - for (int lc = 0; lc < 4; ++lc) { - SWAP_UINT16((uint16_t *)&pge->counter_values[lc]); - } - SWAP_UINT16((uint16_t *)&pge->text_num); +} + +void Resource::decodePGE(const uint8_t *p, int size) { + _pgeNum = _readUint16(p); p += 2; + memset(_pgeInit, 0, sizeof(_pgeInit)); + debug(DBG_RES, "len=%d _pgeNum=%d", size, _pgeNum); + for (uint16_t i = 0; i < _pgeNum; ++i) { + InitPGE *pge = &_pgeInit[i]; + pge->type = _readUint16(p); p += 2; + pge->pos_x = _readUint16(p); p += 2; + pge->pos_y = _readUint16(p); p += 2; + pge->obj_node_number = _readUint16(p); p += 2; + pge->life = _readUint16(p); p += 2; + for (int lc = 0; lc < 4; ++lc) { + pge->counter_values[lc] = _readUint16(p); p += 2; } + pge->object_type = *p++; + pge->init_room = *p++; + pge->room_location = *p++; + pge->init_flags = *p++; + pge->colliding_icon_num = *p++; + pge->icon_num = *p++; + pge->object_id = *p++; + pge->skill = *p++; + pge->mirror_x = *p++; + pge->flags = *p++; + pge->unk1C = *p++; + ++p; + pge->text_num = _readUint16(p); p += 2; } } void Resource::load_ANI(File *f) { debug(DBG_RES, "Resource::load_ANI()"); - int size = f->size() - 2; + const int size = f->size(); _ani = (uint8_t *)malloc(size); if (!_ani) { error("Unable to allocate ANI buffer"); } else { - uint16_t count = f->readUint16LE(); f->read(_ani, size); - if (_type == kResourceTypeAmiga) { - const uint8_t *end = _ani + size; - SWAP_UINT16(&count); - // byte-swap animation data - for (uint16_t i = 0; i < count; ++i) { - uint8_t *p = _ani + READ_BE_UINT16(_ani + 2 * i); - // byte-swap offset - SWAP(_ani[2 * i], _ani[2 * i + 1]); - if (p >= end) { - continue; - } - const int frames = READ_BE_UINT16(p); - if (p[0] != 0) { - // byte-swap only once - continue; - } - // byte-swap anim count - SWAP(p[0], p[1]); - debug(DBG_RES, "ani=%d frames=%d", i, frames); - for (int j = 0; j < frames; ++j) { - // byte-swap next frame - SWAP(p[6 + j * 4], p[6 + j * 4 + 1]); - } - } - } } } @@ -846,13 +975,6 @@ void Resource::load_TBN(File *f) { } else { f->read(_tbn, len); } - if (_type == kResourceTypeAmiga) { - const int firstOffset = READ_BE_UINT16(_tbn); - for (int i = 0; i < firstOffset; i += 2) { - // byte-swap offset - SWAP(_tbn[i], _tbn[i + 1]); - } - } } void Resource::load_CMD(File *pf) { @@ -1011,6 +1133,17 @@ void Resource::load_LEV(File *f) { void Resource::load_SGD(File *f) { const int len = f->size(); + if (_type == kResourceTypeDOS) { + _sgd = (uint8_t *)malloc(len); + if (!_sgd) { + error("Unable to allocate SGD buffer"); + } else { + f->read(_sgd, len); + // first byte == number of entries, clear to fix up 32 bits offset + _sgd[0] = 0; + } + return; + } f->seek(len - 4); int size = f->readUint32BE(); f->seek(0); @@ -1029,6 +1162,16 @@ void Resource::load_SGD(File *f) { free(tmp); } +void Resource::load_BNQ(File *f) { + const int len = f->size(); + _bnq = (uint8_t *)malloc(len); + if (!_bnq) { + error("Unable to allocate BNQ buffer"); + } else { + f->read(_bnq, len); + } +} + void Resource::load_SPM(File *f) { static const int kPersoDatSize = 178647; const int len = f->size(); @@ -1072,19 +1215,23 @@ void Resource::clearBankData() { int Resource::getBankDataSize(uint16_t num) { int len = READ_BE_UINT16(_mbk + num * 6 + 4); - int size = 0; switch (_type) { case kResourceTypeAmiga: if (len & 0x8000) { len = -(int16_t)len; } - size = len * 32; break; - case kResourceTypePC: - size = (len & 0x7FFF) * 32; + case kResourceTypeDOS: + if (len & 0x8000) { + if (_mbk == _bnq) { // demo .bnq use signed int + len = -(int16_t)len; + break; + } + len &= 0x7FFF; + } break; } - return size; + return len * 32; } uint8_t *Resource::findBankData(uint16_t num) { @@ -1099,7 +1246,7 @@ uint8_t *Resource::findBankData(uint16_t num) { uint8_t *Resource::loadBankData(uint16_t num) { const uint8_t *ptr = _mbk + num * 6; int dataOffset = READ_BE_UINT32(ptr); - if (_type == kResourceTypePC) { + if (_type == kResourceTypeDOS) { // first byte of the data buffer corresponds // to the total count of entries dataOffset &= 0xFFFF; diff --git a/resource.h b/resource.h index 56406dc..5997a1e 100644 --- a/resource.h +++ b/resource.h @@ -1,24 +1,14 @@ -/* REminiscence - Flashback interpreter - * Copyright (C) 2005-2015 Gregory Montoir - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . + +/* + * REminiscence - Flashback interpreter + * Copyright (C) 2005-2015 Gregory Montoir (cyx@users.sourceforge.net) */ #ifndef RESOURCE_H__ #define RESOURCE_H__ #include "intern.h" +#include "resource_aba.h" struct File; struct FileSystem; @@ -93,11 +83,13 @@ struct Resource { OT_SPL, OT_LEV, OT_SGD, + OT_BNQ, OT_SPM }; enum { NUM_SFXS = 66, + NUM_BANK_BUFFERS = 50, NUM_SPRITES = 1287 }; @@ -108,6 +100,10 @@ struct Resource { FileSystem *_fs; ResourceType _type; Language _lang; + bool _isDemo; + ResourceAba *_aba; + uint16_t (*_readUint16)(const void *); + uint32_t (*_readUint32)(const void *); bool _hasSeqData; char _entryName[32]; uint8_t *_fnt; @@ -131,6 +127,7 @@ struct Resource { uint8_t *_lev; int _levNum; uint8_t *_sgd; + uint8_t *_bnq; uint16_t _numObjectNodes; ObjectNode *_objectNodesMap[255]; uint8_t *_memBuf; @@ -147,17 +144,24 @@ struct Resource { uint8_t *_bankData; uint8_t *_bankDataHead; uint8_t *_bankDataTail; - BankSlot _bankBuffers[50]; + BankSlot _bankBuffers[NUM_BANK_BUFFERS]; int _bankBuffersCount; Resource(FileSystem *fs, ResourceType type, Language lang); ~Resource(); + void init(); + void fini(); + + bool isDOS() const { return _type == kResourceTypeDOS; } + bool isAmiga() const { return _type == kResourceTypeAmiga; } + void clearLevelRes(); void load_FIB(const char *fileName); void load_SPL_demo(); void load_MAP_menu(const char *fileName, uint8_t *dstPtr); void load_PAL_menu(const char *fileName, uint8_t *dstPtr); + void load_CMP_menu(const char *fileName, uint8_t *dstPtr); void load_SPR_OFF(const char *fileName, uint8_t *sprData); void load_CINE(); void load_TEXT(); @@ -178,6 +182,7 @@ struct Resource { void load_OBC(File *pf); void decodeOBJ(const uint8_t *, int); void load_PGE(File *pf); + void decodePGE(const uint8_t *, int); void load_ANI(File *pf); void load_TBN(File *pf); void load_CMD(File *pf); @@ -187,10 +192,14 @@ struct Resource { void load_SPL(File *pf); void load_LEV(File *pf); void load_SGD(File *pf); + void load_BNQ(File *pf); void load_SPM(File *f); const uint8_t *getAniData(int num) const { - const int offset = READ_LE_UINT16(_ani + num * 2); - return _ani + offset; + const int offset = _readUint16(_ani + 2 + num * 2); + return _ani + 2 + offset; + } + const uint8_t *getTextString(int num) { + return _tbn + _readUint16(_tbn + num * 2); } const uint8_t *getGameString(int num) { return _stringsTable + READ_LE_UINT16(_stringsTable + num * 2); diff --git a/resource_aba.cpp b/resource_aba.cpp new file mode 100644 index 0000000..f1a55bf --- /dev/null +++ b/resource_aba.cpp @@ -0,0 +1,85 @@ + +#include "resource_aba.h" +#include "unpack.h" +#include "util.h" + +const char *ResourceAba::FILENAME = "DEMO_UK.ABA"; + +ResourceAba::ResourceAba(FileSystem *fs) + : _fs(fs) { + _entries = 0; + _entriesCount = 0; +} + +ResourceAba::~ResourceAba() { + free(_entries); +} + +void ResourceAba::readEntries() { + if (_f.open(FILENAME, "rb", _fs)) { + _entriesCount = _f.readUint16BE(); + _entries = (ResourceAbaEntry *)calloc(_entriesCount, sizeof(ResourceAbaEntry)); + if (!_entries) { + error("Failed to allocate %d _entries", _entriesCount); + return; + } + const int entrySize = _f.readUint16BE(); + assert(entrySize == 30); + uint32_t nextOffset = 0; + for (int i = 0; i < _entriesCount; ++i) { + _f.read(_entries[i].name, sizeof(_entries[i].name)); + _entries[i].offset = _f.readUint32BE(); + _entries[i].compressedSize = _f.readUint32BE(); + _entries[i].size = _f.readUint32BE(); + const uint32_t tag = _f.readUint32BE(); + assert(tag == TAG); + debug(DBG_RES, "'%s' offset 0x%X size %d/%d", _entries[i].name, _entries[i].offset, _entries[i].compressedSize, _entries[i].size); + if (i != 0) { + assert(nextOffset == _entries[i].offset); + } + nextOffset = _entries[i].offset + _entries[i].compressedSize; + } + } +} + +const ResourceAbaEntry *ResourceAba::findEntry(const char *name) const { + for (int i = 0; i < _entriesCount; ++i) { + if (strcasecmp(_entries[i].name, name) == 0) { + return &_entries[i]; + } + } + return 0; +} + +uint8_t *ResourceAba::loadEntry(const char *name, uint32_t *size) { + uint8_t *dst = 0; + const ResourceAbaEntry *e = findEntry(name); + if (e) { + if (size) { + *size = e->size; + } + uint8_t *tmp = (uint8_t *)malloc(e->compressedSize); + if (!tmp) { + error("Failed to allocate %d bytes", e->compressedSize); + return 0; + } + _f.seek(e->offset); + _f.read(tmp, e->compressedSize); + if (e->compressedSize == e->size) { + dst = tmp; + } else { + dst = (uint8_t *)malloc(e->size); + if (!dst) { + error("Failed to allocate %d bytes", e->size); + free(tmp); + return 0; + } + const bool ret = delphine_unpack(dst, tmp, e->compressedSize); + if (!ret) { + error("Bad CRC for '%s'", name); + } + free(tmp); + } + } + return dst; +} diff --git a/resource_aba.h b/resource_aba.h new file mode 100644 index 0000000..0736b0f --- /dev/null +++ b/resource_aba.h @@ -0,0 +1,34 @@ + +#ifndef RESOURCE_ABA_H__ +#define RESOURCE_ABA_H__ + +#include "file.h" + +struct FileSystem; + +struct ResourceAbaEntry { + char name[14]; + uint32_t offset; + uint32_t compressedSize; + uint32_t size; +}; + +struct ResourceAba { + + static const char *FILENAME; + static const int TAG = 0x442E4D2E; + + FileSystem *_fs; + File _f; + ResourceAbaEntry *_entries; + int _entriesCount; + + ResourceAba(FileSystem *fs); + ~ResourceAba(); + + void readEntries(); + const ResourceAbaEntry *findEntry(const char *name) const; + uint8_t *loadEntry(const char *name, uint32_t *size = 0); +}; + +#endif // RESOURCE_ABA_H__ diff --git a/rs.cfg b/rs.cfg new file mode 100644 index 0000000..4401265 --- /dev/null +++ b/rs.cfg @@ -0,0 +1,14 @@ +# display copy protection shapes +bypass_protection=true + +# enable playback of 'asc', 'metro' and 'serrure' cutscenes +play_disabled_cutscenes=false + +# use original password level selection menu screen +enable_password_menu=false + +# fade palette to black for screen transition (use blending if false) +fade_out_palette=true + +# use .BNQ & .LEV datafiles (tile based rendering) for backgrounds (instead of .MAP) +use_tiledata=false diff --git a/scaler.cpp b/scaler.cpp index 8edb46c..6af3bbc 100644 --- a/scaler.cpp +++ b/scaler.cpp @@ -1,22 +1,11 @@ -/* REminiscence - Flashback interpreter - * Copyright (C) 2005-2015 Gregory Montoir - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . + +/* + * REminiscence - Flashback interpreter + * Copyright (C) 2005-2015 Gregory Montoir (cyx@users.sourceforge.net) */ #include "scaler.h" - +#include "util.h" static void point1x(uint16_t *dst, int dstPitch, const uint16_t *src, int srcPitch, int w, int h) { dstPitch >>= 1; diff --git a/scaler.h b/scaler.h index f6b4be2..9ab183c 100644 --- a/scaler.h +++ b/scaler.h @@ -1,18 +1,7 @@ -/* REminiscence - Flashback interpreter - * Copyright (C) 2005-2015 Gregory Montoir - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . + +/* + * REminiscence - Flashback interpreter + * Copyright (C) 2005-2015 Gregory Montoir (cyx@users.sourceforge.net) */ #ifndef SCALER_H__ diff --git a/seq_player.cpp b/seq_player.cpp index 77c6c10..d9619b1 100644 --- a/seq_player.cpp +++ b/seq_player.cpp @@ -1,18 +1,7 @@ -/* REminiscence - Flashback interpreter - * Copyright (C) 2005-2015 Gregory Montoir - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . + +/* + * REminiscence - Flashback interpreter + * Copyright (C) 2005-2015 Gregory Montoir (cyx@users.sourceforge.net) */ #include "file.h" @@ -20,7 +9,7 @@ #include "mixer.h" #include "seq_player.h" #include "systemstub.h" - +#include "util.h" bool SeqDemuxer::open(File *f) { _f = f; diff --git a/seq_player.h b/seq_player.h index 2bd42bc..1ddaba7 100644 --- a/seq_player.h +++ b/seq_player.h @@ -1,18 +1,7 @@ -/* REminiscence - Flashback interpreter - * Copyright (C) 2005-2015 Gregory Montoir - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . + +/* + * REminiscence - Flashback interpreter + * Copyright (C) 2005-2015 Gregory Montoir (cyx@users.sourceforge.net) */ #ifndef SEQ_PLAYER_H__ diff --git a/sfx_player.cpp b/sfx_player.cpp index bee8a2b..d01622a 100644 --- a/sfx_player.cpp +++ b/sfx_player.cpp @@ -1,23 +1,12 @@ -/* REminiscence - Flashback interpreter - * Copyright (C) 2005-2015 Gregory Montoir - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . + +/* + * REminiscence - Flashback interpreter + * Copyright (C) 2005-2015 Gregory Montoir (cyx@users.sourceforge.net) */ #include "mixer.h" #include "sfx_player.h" - +#include "util.h" SfxPlayer::SfxPlayer(Mixer *mixer) : _mod(0), _playing(false), _mix(mixer) { @@ -137,7 +126,7 @@ void SfxPlayer::mixSamples(int8_t *buf, int samplesLen) { curLen = 0; } while (count--) { - int out = resampleLinear(si, pos, deltaPos, FRAC_BITS); + const int out = si->getPCM(pos >> FRAC_BITS); Mixer::addclamp(*mixbuf++, out * si->vol / 64); pos += deltaPos; } diff --git a/sfx_player.h b/sfx_player.h index 588368f..91eb17d 100644 --- a/sfx_player.h +++ b/sfx_player.h @@ -1,18 +1,7 @@ -/* REminiscence - Flashback interpreter - * Copyright (C) 2005-2015 Gregory Montoir - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . + +/* + * REminiscence - Flashback interpreter + * Copyright (C) 2005-2015 Gregory Montoir (cyx@users.sourceforge.net) */ #ifndef SFX_PLAYER_H__ diff --git a/staticres.cpp b/staticres.cpp index adfc41d..044f1af 100644 --- a/staticres.cpp +++ b/staticres.cpp @@ -1,18 +1,7 @@ -/* REminiscence - Flashback interpreter - * Copyright (C) 2005-2015 Gregory Montoir - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . + +/* + * REminiscence - Flashback interpreter + * Copyright (C) 2005-2015 Gregory Montoir (cyx@users.sourceforge.net) */ #include "game.h" @@ -72,7 +61,10 @@ const char *Cutscene::_namesTable[] = { "LOGOS", "OVER", "SCORE", - "INTRO2" + "INTRO2", + "SERRURE", + "HOLOCUBE", + "CHUTE2" }; const uint16_t Cutscene::_offsetsTable[] = { @@ -94,6 +86,12 @@ const uint16_t Cutscene::_offsetsTable[] = { 0xFFFF, 0x0000 }; +const uint8_t Cutscene::_amigaDemoOffsetsTable[] = { + 1, 32, 0, /* HOLOCUBE */ + 6, 33, 0, /* CHUTE2 */ + 255 +}; + const uint16_t Cutscene::_cosTable[] = { 0x0100, 0x00FF, 0x00FF, 0x00FF, 0x00FF, 0x00FF, 0x00FE, 0x00FE, 0x00FD, 0x00FC, 0x00FC, 0x00FB, 0x00FA, 0x00F9, 0x00F8, 0x00F7, @@ -2545,13 +2543,6 @@ const uint8_t Video::_palSlot0xF[] = { 0xA7, 0xAF, 0xA7, 0xB3, 0xBF, 0xBB, 0xBF, 0xCF, 0xCF, 0xCF, 0x00, 0x33, 0x00, 0x17, 0x0F, 0x1F }; -const int8_t ModPlayer::_sineWaveTable[] = { - 0, 24, 49, 74, 97, 120, -115, -95, -76, -59, -44, -32, -21, -12, -6, -3, - -1, -3, -6, -12, -21, -32, -44, -59, -76, -95, -115, 120, 97, 74, 49, 24, - 0, -24, -49, -74, -97, -120, 115, 95, 76, 59, 44, 32, 21, 12, 6, 3, - 1, 3, 6, 12, 21, 32, 44, 59, 76, 95, 115, -120, -97, -74, -49, -24 -}; - const uint16_t ModPlayer::_periodTable[] = { 856, 808, 762, 720, 678, 640, 604, 570, 538, 508, 480, 453, // C-1 to B-1 Finetune 0 428, 404, 381, 360, 339, 320, 302, 285, 269, 254, 240, 226, // C-2 to B-2 Finetune 0 diff --git a/systemstub.h b/systemstub.h index d1560d5..09fe49f 100644 --- a/systemstub.h +++ b/systemstub.h @@ -1,18 +1,7 @@ -/* REminiscence - Flashback interpreter - * Copyright (C) 2005-2015 Gregory Montoir - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . + +/* + * REminiscence - Flashback interpreter + * Copyright (C) 2005-2015 Gregory Montoir (cyx@users.sourceforge.net) */ #ifndef SYSTEMSTUB_H__ @@ -62,9 +51,10 @@ struct SystemStub { virtual ~SystemStub() {} - virtual void init(const char *title, int w, int h) = 0; + virtual void init(const char *title, int w, int h, int scaler, bool fullscreen) = 0; virtual void destroy() = 0; + virtual void setScreenSize(int w, int h) = 0; virtual void setPalette(const uint8_t *pal, int n) = 0; virtual void setPaletteEntry(int i, const Color *c) = 0; virtual void getPaletteEntry(int i, Color *c) = 0; diff --git a/systemstub_sdl.cpp b/systemstub_sdl.cpp index 2e78613..00e61f4 100644 --- a/systemstub_sdl.cpp +++ b/systemstub_sdl.cpp @@ -1,24 +1,13 @@ -/* REminiscence - Flashback interpreter - * Copyright (C) 2005-2015 Gregory Montoir - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . + +/* + * REminiscence - Flashback interpreter + * Copyright (C) 2005-2015 Gregory Montoir (cyx@users.sourceforge.net) */ #include #include "scaler.h" #include "systemstub.h" - +#include "util.h" struct SystemStub_SDL : SystemStub { enum { @@ -41,10 +30,12 @@ struct SystemStub_SDL : SystemStub { bool _fadeOnUpdateScreen; void (*_audioCbProc)(void *, int8_t *, int); void *_audioCbData; + int _screenshot; virtual ~SystemStub_SDL() {} - virtual void init(const char *title, int w, int h); + virtual void init(const char *title, int w, int h, int scaler, bool fullscreen); virtual void destroy(); + virtual void setScreenSize(int w, int h); virtual void setPalette(const uint8_t *pal, int n); virtual void setPaletteEntry(int i, const Color *c); virtual void getPaletteEntry(int i, Color *c); @@ -62,8 +53,6 @@ struct SystemStub_SDL : SystemStub { virtual void unlockAudio(); void processEvent(const SDL_Event &ev, bool &paused); - void updateScreen_GL(int shakeOffset); - void updateScreen_SW(int shakeOffset); void prepareGfxMode(); void cleanupGfxMode(); void switchGfxMode(bool fullscreen, uint8_t scaler); @@ -76,30 +65,23 @@ SystemStub *SystemStub_SDL_create() { return new SystemStub_SDL(); } -void SystemStub_SDL::init(const char *title, int w, int h) { +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); memset(&_pi, 0, sizeof(_pi)); - _screenW = w; - _screenH = h; - // allocate some extra bytes for the scaling routines - const int screenBufferSize = (w + 2) * (h + 2) * sizeof(uint16_t); - _screenBuffer = (uint16_t *)malloc(screenBufferSize); - if (!_screenBuffer) { - error("SystemStub_SDL::init() Unable to allocate offscreen buffer"); - } - memset(_screenBuffer, 0, screenBufferSize); + _screenBuffer = 0; _fadeScreenBuffer = 0; _fadeOnUpdateScreen = false; - _fullscreen = false; - _currentScaler = SCALER_SCALE_3X; + _fullscreen = fullscreen; + _currentScaler = scaler; memset(_pal, 0, sizeof(_pal)); - prepareGfxMode(); + setScreenSize(w, h); _joystick = NULL; if (SDL_NumJoysticks() > 0) { _joystick = SDL_JoystickOpen(0); } + _screenshot = 1; } void SystemStub_SDL::destroy() { @@ -110,6 +92,25 @@ void SystemStub_SDL::destroy() { SDL_Quit(); } +void SystemStub_SDL::setScreenSize(int w, int h) { + if (_screenW == w && _screenH == h) { + return; + } + free(_screenBuffer); + _screenBuffer = 0; + free(_fadeScreenBuffer); + _fadeScreenBuffer = 0; + // allocate some extra bytes for the scaling routines + const int screenBufferSize = (w + 2) * (h + 2) * sizeof(uint16_t); + _screenBuffer = (uint16_t *)calloc(1, screenBufferSize); + if (!_screenBuffer) { + error("SystemStub_SDL::setScreenSize() Unable to allocate offscreen buffer, w=%d, h=%d", w, h); + } + _screenW = w; + _screenH = h; + prepareGfxMode(); +} + void SystemStub_SDL::setPalette(const uint8_t *pal, int n) { assert(n <= 256); for (int i = 0; i < n; ++i) { @@ -121,17 +122,11 @@ void SystemStub_SDL::setPalette(const uint8_t *pal, int n) { } void SystemStub_SDL::setPaletteEntry(int i, const Color *c) { - uint8_t r = (c->r << 2) | (c->r & 3); - uint8_t g = (c->g << 2) | (c->g & 3); - uint8_t b = (c->b << 2) | (c->b & 3); - _pal[i] = SDL_MapRGB(_screenSurface->format, r, g, b); + _pal[i] = SDL_MapRGB(_screenSurface->format, 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); - c->r >>= 2; - c->g >>= 2; - c->b >>= 2; } void SystemStub_SDL::setOverscanColor(int i) { @@ -278,8 +273,8 @@ void SystemStub_SDL::updateScreen(int shakeOffset) { } void SystemStub_SDL::processEvents() { + bool paused = false; while (true) { - bool paused = false; SDL_Event ev; while (SDL_PollEvent(&ev)) { processEvent(ev, paused); @@ -431,6 +426,12 @@ void SystemStub_SDL::processEvent(const SDL_Event &ev, bool &paused) { if (_currentScaler > 0) { switchGfxMode(_fullscreen, s); } + } else if (ev.key.keysym.sym == SDLK_s) { + char name[32]; + snprintf(name, sizeof(name), "screenshot-%03d.bmp", _screenshot); + SDL_SaveBMP(_screenSurface, name); + ++_screenshot; + debug(DBG_INFO, "Written '%s'", name); } break; } else if (ev.key.keysym.mod & KMOD_CTRL) { diff --git a/unpack.cpp b/unpack.cpp index e9444dc..fa569d7 100644 --- a/unpack.cpp +++ b/unpack.cpp @@ -1,18 +1,7 @@ -/* REminiscence - Flashback interpreter - * Copyright (C) 2005-2015 Gregory Montoir - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . + +/* + * REminiscence - Flashback interpreter + * Copyright (C) 2005-2015 Gregory Montoir (cyx@users.sourceforge.net) */ #include "unpack.h" diff --git a/unpack.h b/unpack.h index b0e06a1..9b8773f 100644 --- a/unpack.h +++ b/unpack.h @@ -1,18 +1,7 @@ -/* REminiscence - Flashback interpreter - * Copyright (C) 2005-2015 Gregory Montoir - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . + +/* + * REminiscence - Flashback interpreter + * Copyright (C) 2005-2015 Gregory Montoir (cyx@users.sourceforge.net) */ #ifndef UNPACK_H__ diff --git a/util.cpp b/util.cpp index 195a69b..e1c977c 100644 --- a/util.cpp +++ b/util.cpp @@ -1,18 +1,7 @@ -/* REminiscence - Flashback interpreter - * Copyright (C) 2005-2015 Gregory Montoir - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . + +/* + * REminiscence - Flashback interpreter + * Copyright (C) 2005-2015 Gregory Montoir (cyx@users.sourceforge.net) */ #ifdef _WIN32 diff --git a/util.h b/util.h index b4e7d16..ed896a1 100644 --- a/util.h +++ b/util.h @@ -1,18 +1,7 @@ -/* REminiscence - Flashback interpreter - * Copyright (C) 2005-2015 Gregory Montoir - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . + +/* + * REminiscence - Flashback interpreter + * Copyright (C) 2005-2015 Gregory Montoir (cyx@users.sourceforge.net) */ #ifndef UTIL_H__ diff --git a/video.cpp b/video.cpp index 8bbb9bf..f2c4319 100644 --- a/video.cpp +++ b/video.cpp @@ -1,38 +1,25 @@ -/* REminiscence - Flashback interpreter - * Copyright (C) 2005-2015 Gregory Montoir - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . + +/* + * REminiscence - Flashback interpreter + * Copyright (C) 2005-2015 Gregory Montoir (cyx@users.sourceforge.net) */ #include "resource.h" #include "systemstub.h" #include "unpack.h" +#include "util.h" #include "video.h" - Video::Video(Resource *res, SystemStub *stub) : _res(res), _stub(stub) { - _frontLayer = (uint8_t *)malloc(GAMESCREEN_W * GAMESCREEN_H); - memset(_frontLayer, 0, GAMESCREEN_W * GAMESCREEN_H); - _backLayer = (uint8_t *)malloc(GAMESCREEN_W * GAMESCREEN_H); - memset(_backLayer, 0, GAMESCREEN_W * GAMESCREEN_H); - _tempLayer = (uint8_t *)malloc(GAMESCREEN_W * GAMESCREEN_H); - memset(_tempLayer, 0, GAMESCREEN_W * GAMESCREEN_H); - _tempLayer2 = (uint8_t *)malloc(GAMESCREEN_W * GAMESCREEN_H); - memset(_tempLayer2, 0, GAMESCREEN_W * GAMESCREEN_H); - _screenBlocks = (uint8_t *)malloc((GAMESCREEN_W / SCREENBLOCK_W) * (GAMESCREEN_H / SCREENBLOCK_H)); - memset(_screenBlocks, 0, (GAMESCREEN_W / SCREENBLOCK_W) * (GAMESCREEN_H / SCREENBLOCK_H)); + _w = GAMESCREEN_W; + _h = GAMESCREEN_H; + _layerSize = _w * _h; + _frontLayer = (uint8_t *)calloc(1, _layerSize); + _backLayer = (uint8_t *)calloc(1,_layerSize); + _tempLayer = (uint8_t *)calloc(1, _layerSize); + _tempLayer2 = (uint8_t *)calloc(1, _layerSize); + _screenBlocks = (uint8_t *)calloc(1, (_w / SCREENBLOCK_W) * (_h / SCREENBLOCK_H)); _fullRefresh = true; _shakeOffset = 0; _charFrontColor = 0; @@ -50,15 +37,15 @@ Video::~Video() { void Video::markBlockAsDirty(int16_t x, int16_t y, uint16_t w, uint16_t h) { debug(DBG_VIDEO, "Video::markBlockAsDirty(%d, %d, %d, %d)", x, y, w, h); - assert(x >= 0 && x + w <= GAMESCREEN_W && y >= 0 && y + h <= GAMESCREEN_H); + assert(x >= 0 && x + w <= _w && y >= 0 && y + h <= _h); int bx1 = x / SCREENBLOCK_W; int by1 = y / SCREENBLOCK_H; int bx2 = (x + w - 1) / SCREENBLOCK_W; int by2 = (y + h - 1) / SCREENBLOCK_H; - assert(bx2 < GAMESCREEN_W / SCREENBLOCK_W && by2 < GAMESCREEN_H / SCREENBLOCK_H); + assert(bx2 < _w / SCREENBLOCK_W && by2 < _h / SCREENBLOCK_H); for (; by1 <= by2; ++by1) { for (int i = bx1; i <= bx2; ++i) { - _screenBlocks[by1 * (GAMESCREEN_W / SCREENBLOCK_W) + i] = 2; + _screenBlocks[by1 * (_w / SCREENBLOCK_W) + i] = 2; } } } @@ -67,16 +54,16 @@ void Video::updateScreen() { debug(DBG_VIDEO, "Video::updateScreen()"); // _fullRefresh = true; if (_fullRefresh) { - _stub->copyRect(0, 0, Video::GAMESCREEN_W, Video::GAMESCREEN_H, _frontLayer, 256); + _stub->copyRect(0, 0, _w, _h, _frontLayer, 256); _stub->updateScreen(_shakeOffset); _fullRefresh = false; } else { int i, j; int count = 0; uint8_t *p = _screenBlocks; - for (j = 0; j < GAMESCREEN_H / SCREENBLOCK_H; ++j) { + for (j = 0; j < _h / SCREENBLOCK_H; ++j) { uint16_t nh = 0; - for (i = 0; i < GAMESCREEN_W / SCREENBLOCK_W; ++i) { + for (i = 0; i < _w / SCREENBLOCK_W; ++i) { if (p[i] != 0) { --p[i]; ++nh; @@ -92,7 +79,7 @@ void Video::updateScreen() { _stub->copyRect(x, j * SCREENBLOCK_H, nh * SCREENBLOCK_W, SCREENBLOCK_H, _frontLayer, 256); ++count; } - p += GAMESCREEN_W / SCREENBLOCK_W; + p += _w / SCREENBLOCK_W; } if (count != 0) { _stub->updateScreen(_shakeOffset); @@ -107,13 +94,16 @@ void Video::updateScreen() { void Video::fullRefresh() { debug(DBG_VIDEO, "Video::fullRefresh()"); _fullRefresh = true; - memset(_screenBlocks, 0, (GAMESCREEN_W / SCREENBLOCK_W) * (GAMESCREEN_H / SCREENBLOCK_H)); + memset(_screenBlocks, 0, (_w / SCREENBLOCK_W) * (_h / SCREENBLOCK_H)); } void Video::fadeOut() { debug(DBG_VIDEO, "Video::fadeOut()"); - _stub->fadeScreen(); -// fadeOutPalette(); + if (g_options.fade_out_palette) { + fadeOutPalette(); + } else { + _stub->fadeScreen(); + } } void Video::fadeOutPalette() { @@ -134,15 +124,7 @@ void Video::fadeOutPalette() { void Video::setPaletteColorBE(int num, int offset) { const int color = READ_BE_UINT16(_res->_pal + offset * 2); - Color c; - c.r = (color & 0x00F) << 2; - c.g = (color & 0x0F0) >> 2; - c.b = (color & 0xF00) >> 6; - if (color != 0) { - c.r |= 3; - c.g |= 3; - c.b |= 3; - } + Color c = AMIGA_convertColor(color, true); _stub->setPaletteEntry(num, &c); } @@ -151,15 +133,7 @@ void Video::setPaletteSlotBE(int palSlot, int palNum) { const uint8_t *p = _res->_pal + palNum * 0x20; for (int i = 0; i < 16; ++i) { const int color = READ_BE_UINT16(p); p += 2; - Color c; - c.r = (color & 0x00F) << 2; - c.g = (color & 0x0F0) >> 2; - c.b = (color & 0xF00) >> 6; - if (color != 0) { - c.r |= 3; - c.g |= 3; - c.b |= 3; - } + Color c = AMIGA_convertColor(color, true); _stub->setPaletteEntry(palSlot * 0x10 + i, &c); } } @@ -168,10 +142,7 @@ 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; - Color c; - c.b = (color & 0x00F) << 2; - c.g = (color & 0x0F0) >> 2; - c.r = (color & 0xF00) >> 6; + Color c = AMIGA_convertColor(color); _stub->setPaletteEntry(palSlot * 0x10 + i, &c); } } @@ -186,13 +157,22 @@ void Video::setPalette0xF() { const uint8_t *p = _palSlot0xF; for (int i = 0; i < 16; ++i) { Color c; - c.r = *p++ >> 2; - c.g = *p++ >> 2; - c.b = *p++ >> 2; + c.r = *p++; + c.g = *p++; + c.b = *p++; _stub->setPaletteEntry(0xF0 + i, &c); } } +void Video::PC_decodeLev(int level, int room) { + uint8_t *tmp = _res->_mbk; + _res->_mbk = _res->_bnq; + _res->clearBankData(); + AMIGA_decodeLev(level, room); + _res->_mbk = tmp; + _res->clearBankData(); +} + static void PC_decodeMapHelper(int sz, const uint8_t *src, uint8_t *dst) { const uint8_t *end = src + sz; while (src < end) { @@ -248,7 +228,7 @@ void Video::PC_decodeMap(int level, int room) { } } } - memcpy(_backLayer, _frontLayer, Video::GAMESCREEN_W * Video::GAMESCREEN_H); + memcpy(_backLayer, _frontLayer, _layerSize); } void Video::PC_setLevelPalettes() { @@ -294,14 +274,15 @@ void Video::PC_decodeSpc(const uint8_t *src, int w, int h, uint8_t *dst) { } } -static void AMIGA_blit3pNxN(uint8_t *dst, int pitch, int w, int h, const uint8_t *src) { +static void AMIGA_planar16(uint8_t *dst, int w, int h, int depth, const uint8_t *src) { + const int pitch = w * 16; const int planarSize = w * 2 * h; for (int y = 0; y < h; ++y) { for (int x = 0; x < w; ++x) { for (int i = 0; i < 16; ++i) { int color = 0; const int mask = 1 << (15 - i); - for (int bit = 0; bit < 3; ++bit) { + for (int bit = 0; bit < depth; ++bit) { if (READ_BE_UINT16(src + bit * planarSize) & mask) { color |= 1 << bit; } @@ -314,26 +295,7 @@ static void AMIGA_blit3pNxN(uint8_t *dst, int pitch, int w, int h, const uint8_t } } -static void AMIGA_blit4p16xN(uint8_t *dst, int w, int h, const uint8_t *src) { - const int planarSize = w * 2 * h; - assert(w == 1); - 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 * planarSize) & mask) { - color |= 1 << bit; - } - } - dst[i] = color; - } - src += 2; - dst += 16; - } -} - -static void AMIGA_blit4p8xN(uint8_t *dst, int w, int h, const uint8_t *src) { +static void AMIGA_planar8(uint8_t *dst, int w, int h, const uint8_t *src) { assert(w == 8); for (int y = 0; y < h; ++y) { for (int i = 0; i < 8; ++i) { @@ -351,27 +313,7 @@ static void AMIGA_blit4p8xN(uint8_t *dst, int w, int h, const uint8_t *src) { } } -static void AMIGA_blit4pNxN(uint8_t *dst, int w, int h, const uint8_t *src) { - const int planarSize = w / 8 * h; - for (int y = 0; y < h; ++y) { - for (int x = 0; x < w / 8; ++x) { - 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 * planarSize] & mask) { - color |= 1 << bit; - } - } - dst[x * 8 + i] = color; - } - ++src; - } - dst += w; - } -} - -static void AMIGA_blit4pNxN_mask(uint8_t *dst, int x0, int y0, int w, int h, uint8_t *src, uint8_t *mask, int size) { +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) { for (int x = 0; x < w * 2; ++x) { @@ -398,7 +340,7 @@ static void AMIGA_blit4pNxN_mask(uint8_t *dst, int x0, int y0, int w, int h, uin } } -static void AMIGA_decodeRLE(uint8_t *dst, const uint8_t *src) { +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 { @@ -417,7 +359,30 @@ static void AMIGA_decodeRLE(uint8_t *dst, const uint8_t *src) { assert(src == end); } -static void AMIGA_decodeSgd(uint8_t *dst, const uint8_t *src, const uint8_t *data) { +static void PC_drawTileMask(uint8_t *dst, int x0, int y0, int w, int h, uint8_t *m, uint8_t *p, int size) { + assert(size == (w * 2 * h)); + for (int y = 0; y < h; ++y) { + for (int x = 0; x < w; ++x) { + const int bits = READ_BE_UINT16(m); m += 2; + for (int bit = 0; bit < 8; ++bit) { + const int j = y0 + y; + const int i = x0 + 2 * (x * 8 + bit); + if (i >= 0 && i < Video::GAMESCREEN_W && j >= 0 && j < Video::GAMESCREEN_H) { + const uint8_t color = *p; + if (bits & (1 << (15 - (bit * 2)))) { + dst[j * Video::GAMESCREEN_W + i] = color >> 4; + } + if (bits & (1 << (15 - (bit * 2 + 1)))) { + dst[j * Video::GAMESCREEN_W + i + 1] = color & 15; + } + } + ++p; + } + } + } +} + +static void decodeSgd(uint8_t *dst, const uint8_t *src, const uint8_t *data, const bool isAmiga) { int num = -1; uint8_t buf[256 * 32]; int count = READ_BE_UINT16(src) - 1; src += 2; @@ -427,7 +392,7 @@ static void AMIGA_decodeSgd(uint8_t *dst, const uint8_t *src, const uint8_t *dat int d1 = READ_BE_UINT16(src); src += 2; if (d2 != 0xFFFF) { d2 &= ~(1 << 15); - const int offset = READ_BE_UINT32(data + d2 * 4); + const int32_t offset = READ_BE_UINT32(data + d2 * 4); if (offset < 0) { const uint8_t *ptr = data - offset; const int size = READ_BE_UINT16(ptr); ptr += 2; @@ -441,18 +406,22 @@ static void AMIGA_decodeSgd(uint8_t *dst, const uint8_t *src, const uint8_t *dat num = d2; const int size = READ_BE_UINT16(data + offset) & 0x7FFF; assert(size <= (int)sizeof(buf)); - AMIGA_decodeRLE(buf, data + offset); + AMIGA_decodeRle(buf, data + offset); } } } const int w = (buf[0] + 1) >> 1; const int h = buf[1] + 1; const int planarSize = READ_BE_UINT16(buf + 2); - AMIGA_blit4pNxN_mask(dst, (int16_t)d0, (int16_t)d1, w, h, buf + 4, buf + 4 + planarSize, planarSize); + if (isAmiga) { + AMIGA_planar_mask(dst, (int16_t)d0, (int16_t)d1, w, h, buf + 4, buf + 4 + planarSize, planarSize); + } else { + PC_drawTileMask(dst, (int16_t)d0, (int16_t)d1, w, h, buf + 4, buf + 4 + planarSize, planarSize); + } } while (--count >= 0); } -static const uint8_t *AMIGA_mirrorY(const uint8_t *a2) { +static const uint8_t *AMIGA_mirrorTileY(const uint8_t *a2) { static uint8_t buf[32]; a2 += 24; @@ -465,7 +434,7 @@ static const uint8_t *AMIGA_mirrorY(const uint8_t *a2) { return buf; } -static const uint8_t *AMIGA_mirrorX(const uint8_t *a2) { +static const uint8_t *AMIGA_mirrorTileX(const uint8_t *a2) { static uint8_t buf[32]; for (int i = 0; i < 32; ++i) { @@ -480,7 +449,13 @@ static const uint8_t *AMIGA_mirrorX(const uint8_t *a2) { return buf; } -static void AMIGA_blit4p8x8(uint8_t *dst, int pitch, const uint8_t *src, int pal, int colorKey = -1) { +static void AMIGA_drawTile(uint8_t *dst, int pitch, const uint8_t *src, int pal, const bool xflip, const bool yflip, int colorKey) { + if (yflip) { + src = AMIGA_mirrorTileY(src); + } + if (xflip) { + src = AMIGA_mirrorTileX(src); + } for (int y = 0; y < 8; ++y) { for (int i = 0; i < 8; ++i) { const int mask = 1 << (7 - i); @@ -499,26 +474,53 @@ static void AMIGA_blit4p8x8(uint8_t *dst, int pitch, const uint8_t *src, int pal } } -static void AMIGA_decodeLevHelper(uint8_t *dst, const uint8_t *src, int offset10, int offset12, const uint8_t *a5, bool sgdBuf) { +static void PC_drawTile(uint8_t *dst, const uint8_t *src, int mask, const bool xflip, const bool yflip, int colorKey) { + int pitch = Video::GAMESCREEN_W; + if (yflip) { + dst += 7 * pitch; + pitch = -pitch; + } + int inc = 1; + if (xflip) { + dst += 7; + inc = -inc; + } + for (int y = 0; y < 8; ++y) { + for (int i = 0; i < 8; i += 2) { + int color = *src >> 4; + if (color != colorKey) { + dst[inc * i] = mask | color; + } + color = *src & 15; + if (color != colorKey) { + dst[inc * (i + 1)] = mask | color; + } + ++src; + } + dst += pitch; + } +} + +static void decodeLevHelper(uint8_t *dst, const uint8_t *src, int offset10, int offset12, const uint8_t *a5, bool sgdBuf, bool isPC) { if (offset10 != 0) { const uint8_t *a0 = src + offset10; for (int y = 0; y < 224; y += 8) { for (int x = 0; x < 256; x += 8) { - const int d3 = READ_BE_UINT16(a0); a0 += 2; + const int d3 = isPC ? READ_LE_UINT16(a0) : READ_BE_UINT16(a0); a0 += 2; const int d0 = d3 & 0x7FF; if (d0 != 0) { const uint8_t *a2 = a5 + d0 * 32; - if ((d3 & (1 << 12)) != 0) { - a2 = AMIGA_mirrorY(a2); - } - if ((d3 & (1 << 11)) != 0) { - a2 = AMIGA_mirrorX(a2); - } + const bool yflip = (d3 & (1 << 12)) != 0; + const bool xflip = (d3 & (1 << 11)) != 0; int mask = 0; if ((d3 < (1 << 15)) == 0) { mask = 0x80; } - AMIGA_blit4p8x8(dst + y * 256 + x, 256, a2, mask); + if (isPC) { + PC_drawTile(dst + y * 256 + x, a2, mask, xflip, yflip, -1); + } else { + AMIGA_drawTile(dst + y * 256 + x, 256, a2, mask, xflip, yflip, -1); + } } } } @@ -527,26 +529,26 @@ static void AMIGA_decodeLevHelper(uint8_t *dst, const uint8_t *src, int offset10 const uint8_t *a0 = src + offset12; for (int y = 0; y < 224; y += 8) { for (int x = 0; x < 256; x += 8) { - int d3 = READ_BE_UINT16(a0); a0 += 2; + const int d3 = isPC ? READ_LE_UINT16(a0) : READ_BE_UINT16(a0); a0 += 2; int d0 = d3 & 0x7FF; if (d0 != 0 && sgdBuf) { d0 -= 896; } if (d0 != 0) { const uint8_t *a2 = a5 + d0 * 32; - if ((d3 & (1 << 12)) != 0) { - a2 = AMIGA_mirrorY(a2); - } - if ((d3 & (1 << 11)) != 0) { - a2 = AMIGA_mirrorX(a2); - } + const bool yflip = (d3 & (1 << 12)) != 0; + const bool xflip = (d3 & (1 << 11)) != 0; int mask = 0; if ((d3 & 0x6000) != 0 && sgdBuf) { mask = 0x10; } else if ((d3 < (1 << 15)) == 0) { mask = 0x80; } - AMIGA_blit4p8x8(dst + y * 256 + x, 256, a2, mask, 0); + if (isPC) { + PC_drawTile(dst + y * 256 + x, a2, mask, xflip, yflip, 0); + } else { + AMIGA_drawTile(dst + y * 256 + x, 256, a2, mask, xflip, yflip, 0); + } } } } @@ -584,49 +586,52 @@ void Video::AMIGA_decodeLev(int level, int room) { } const int d3 = *a1++; if (d3 == 255) { - assert(sz + d1 < kTempMbkSize * 32); + assert(sz + d1 <= kTempMbkSize * 32); memcpy(buf + sz, a6, d1); sz += d1; } else { for (int i = 0; i < d3 + 1; ++i) { const int d4 = *a1++; - assert(sz + 32 < kTempMbkSize * 32); + assert(sz + 32 <= kTempMbkSize * 32); memcpy(buf + sz, a6 + d4 * 32, 32); sz += 32; } } } - memset(_frontLayer, 0, Video::GAMESCREEN_W * Video::GAMESCREEN_H); + memset(_frontLayer, 0, _layerSize); if (tmp[1] != 0) { assert(_res->_sgd); - AMIGA_decodeSgd(_frontLayer, tmp + offset10, _res->_sgd); + decodeSgd(_frontLayer, tmp + offset10, _res->_sgd, _res->isAmiga()); offset10 = 0; } - AMIGA_decodeLevHelper(_frontLayer, tmp, offset10, offset12, buf, tmp[1] != 0); - memcpy(_backLayer, _frontLayer, Video::GAMESCREEN_W * Video::GAMESCREEN_H); - uint16_t num[4]; - for (int i = 0; i < 4; ++i) { - num[i] = READ_BE_UINT16(tmp + 2 + i * 2); + decodeLevHelper(_frontLayer, tmp, offset10, offset12, buf, tmp[1] != 0, _res->isDOS()); + free(buf); + memcpy(_backLayer, _frontLayer, _layerSize); + _mapPalSlot1 = READ_BE_UINT16(tmp + 2); + _mapPalSlot2 = READ_BE_UINT16(tmp + 4); + _mapPalSlot3 = READ_BE_UINT16(tmp + 6); + _mapPalSlot4 = READ_BE_UINT16(tmp + 8); + if (_res->isDOS()) { + // done in ::PC_setLevelPalettes + return; } - _mapPalSlot1 = num[1]; - _mapPalSlot2 = num[2]; - setPaletteSlotBE(0x0, num[0]); + setPaletteSlotBE(0x0, _mapPalSlot1); for (int i = 1; i < 5; ++i) { - setPaletteSlotBE(i, _mapPalSlot2); + setPaletteSlotBE(i, _mapPalSlot3); } - setPaletteSlotBE(0x6, _mapPalSlot2); - setPaletteSlotBE(0x8, num[0]); - setPaletteSlotBE(0xA, _mapPalSlot2); + setPaletteSlotBE(0x6, _mapPalSlot3); + setPaletteSlotBE(0x8, _mapPalSlot1); + setPaletteSlotBE(0xA, _mapPalSlot3); } void Video::AMIGA_decodeSpm(const uint8_t *src, uint8_t *dst) { uint8_t buf[256 * 32]; const int size = READ_BE_UINT16(src + 3) & 0x7FFF; assert(size <= (int)sizeof(buf)); - AMIGA_decodeRLE(buf, src + 3); + AMIGA_decodeRle(buf, src + 3); const int w = (src[2] >> 7) + 1; const int h = src[2] & 0x7F; - AMIGA_blit3pNxN(dst, w * 16, w, h, buf); + AMIGA_planar16(dst, w, h, 3, buf); } void Video::AMIGA_decodeIcn(const uint8_t *src, int num, uint8_t *dst) { @@ -638,20 +643,28 @@ void Video::AMIGA_decodeIcn(const uint8_t *src, int num, uint8_t *dst) { } const int h = 1 + *src++; const int w = 1 + *src++; - AMIGA_blit4p16xN(dst, w, h, src + 4); + AMIGA_planar16(dst, w, h, 4, src + 4); } void Video::AMIGA_decodeSpc(const uint8_t *src, int w, int h, uint8_t *dst) { switch (w) { case 8: - AMIGA_blit4p8xN(dst, w, h, src); + AMIGA_planar8(dst, w, h, src); + break; + case 16: + case 32: + AMIGA_planar16(dst, w / 16, h, 4, src); break; default: - AMIGA_blit4pNxN(dst, w, h, src); + warning("AMIGA_decodeSpc w=%d unimplemented", w); break; } } +void Video::AMIGA_decodeCmp(const uint8_t *src, uint8_t *dst) { + AMIGA_planar16(dst, 20, 224, 5, src); +} + void Video::drawSpriteSub1(const uint8_t *src, uint8_t *dst, int pitch, int h, int w, uint8_t colMask) { debug(DBG_VIDEO, "Video::drawSpriteSub1(0x%X, 0x%X, 0x%X, 0x%X)", pitch, w, h, colMask); while (h--) { @@ -811,7 +824,7 @@ const char *Video::drawString(const char *str, int16_t x, int16_t y, uint8_t col case kResourceTypeAmiga: drawCharFunc = &Video::AMIGA_drawStringChar; break; - case kResourceTypePC: + case kResourceTypeDOS: drawCharFunc = &Video::PC_drawStringChar; break; } @@ -829,3 +842,17 @@ const char *Video::drawString(const char *str, int16_t x, int16_t y, uint8_t col markBlockAsDirty(x, y, len * 8, 8); return str - 1; } + +Color Video::AMIGA_convertColor(const uint16_t color, bool bgr) { // 4bits to 8bits + int r = (color & 0xF00) >> 8; + int g = (color & 0xF0) >> 4; + int b = color & 0xF; + if (bgr) { + SWAP(r, b); + } + Color c; + c.r = (r << 4) | r; + c.g = (g << 4) | g; + c.b = (b << 4) | b; + return c; +} diff --git a/video.h b/video.h index c232e95..9a827ee 100644 --- a/video.h +++ b/video.h @@ -1,18 +1,7 @@ -/* REminiscence - Flashback interpreter - * Copyright (C) 2005-2015 Gregory Montoir - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . + +/* + * REminiscence - Flashback interpreter + * Copyright (C) 2005-2015 Gregory Montoir (cyx@users.sourceforge.net) */ #ifndef VIDEO_H__ @@ -41,6 +30,8 @@ struct Video { Resource *_res; SystemStub *_stub; + int _w, _h; + int _layerSize; uint8_t *_frontLayer; uint8_t *_backLayer; uint8_t *_tempLayer; @@ -67,6 +58,7 @@ struct Video { void setPaletteSlotLE(int palSlot, const uint8_t *palData); void setTextPalette(); void setPalette0xF(); + void PC_decodeLev(int level, int room); void PC_decodeMap(int level, int room); void PC_setLevelPalettes(); void PC_decodeIcn(const uint8_t *src, int num, uint8_t *dst); @@ -75,6 +67,7 @@ struct Video { void AMIGA_decodeSpm(const uint8_t *src, uint8_t *dst); void AMIGA_decodeIcn(const uint8_t *src, int num, uint8_t *dst); void AMIGA_decodeSpc(const uint8_t *src, int w, int h, uint8_t *dst); + void AMIGA_decodeCmp(const uint8_t *src, uint8_t *dst); void drawSpriteSub1(const uint8_t *src, uint8_t *dst, int pitch, int h, int w, uint8_t colMask); void drawSpriteSub2(const uint8_t *src, uint8_t *dst, int pitch, int h, int w, uint8_t colMask); void drawSpriteSub3(const uint8_t *src, uint8_t *dst, int pitch, int h, int w, uint8_t colMask); @@ -85,6 +78,7 @@ struct Video { void PC_drawStringChar(uint8_t *dst, int pitch, const uint8_t *src, uint8_t color, uint8_t chr); void AMIGA_drawStringChar(uint8_t *dst, int pitch, const uint8_t *src, uint8_t color, uint8_t chr); const char *drawString(const char *str, int16_t x, int16_t y, uint8_t col); + static Color AMIGA_convertColor(const uint16_t color, bool bgr = false); }; #endif // VIDEO_H__