Import 0.3.0

This commit is contained in:
Gregory Montoir 2016-03-21 00:00:00 +08:00
parent f10e912082
commit cb9e469636
46 changed files with 1548 additions and 1300 deletions

View File

@ -2,24 +2,21 @@
SDL_CFLAGS = `sdl-config --cflags` SDL_CFLAGS = `sdl-config --cflags`
SDL_LIBS = `sdl-config --libs` SDL_LIBS = `sdl-config --libs`
VORBIS_LIBS = -lvorbisidec VORBIS_LIBS = -lvorbisidec
MODPLUG_LIBS = -lmodplug
ZLIB_LIBS = -lz ZLIB_LIBS = -lz
DEFINES = -DBYPASS_PROTECTION CXX := clang++
#DEFINES = -DBYPASS_PROTECTION -DENABLE_PASSWORD_MENU -DNDEBUG CXXFLAGS := -Wall -MMD $(SDL_CFLAGS) -DUSE_ZLIB # -DUSE_MODPLUG
CXXFLAGS += -Wall -Wuninitialized -Wshadow -Wundef -Wreorder -Wnon-virtual-dtor -Wno-multichar
CXXFLAGS += -MMD $(SDL_CFLAGS) -DUSE_ZLIB $(DEFINES)
SRCS = collision.cpp cutscene.cpp file.cpp fs.cpp game.cpp graphics.cpp main.cpp menu.cpp \ 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 sfx_player.cpp staticres.cpp systemstub_sdl.cpp unpack.cpp util.cpp video.cpp
OBJS = $(SRCS:.cpp=.o) OBJS = $(SRCS:.cpp=.o)
DEPS = $(SRCS:.cpp=.d) DEPS = $(SRCS:.cpp=.d)
LIBS = $(SDL_LIBS) $(VORBIS_LIBS) $(ZLIB_LIBS) LIBS = $(SDL_LIBS) $(VORBIS_LIBS) $(MODPLUG_LIBS) $(ZLIB_LIBS)
-include Makefile.local
rs: $(OBJS) rs: $(OBJS)
$(CXX) $(LDFLAGS) -o $@ $(OBJS) $(LIBS) $(CXX) $(LDFLAGS) -o $@ $(OBJS) $(LIBS)

3
README
View File

@ -1,6 +1,6 @@
REminiscence README 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 Backspace display the inventory
Alt Enter toggle windowed/fullscreen mode Alt Enter toggle windowed/fullscreen mode
Alt + and - change video scaler Alt + and - change video scaler
Alt S write screenshot as .bmp
Ctrl S save game state Ctrl S save game state
Ctrl L load game state Ctrl L load game state
Ctrl + and - change game state slot Ctrl + and - change game state slot

View File

@ -1,23 +1,12 @@
/* REminiscence - Flashback interpreter
* Copyright (C) 2005-2015 Gregory Montoir /*
* * REminiscence - Flashback interpreter
* This program is free software: you can redistribute it and/or modify * Copyright (C) 2005-2015 Gregory Montoir (cyx@users.sourceforge.net)
* 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 <http://www.gnu.org/licenses/>.
*/ */
#include "game.h" #include "game.h"
#include "resource.h" #include "resource.h"
#include "util.h"
void Game::col_prepareRoomState() { void Game::col_prepareRoomState() {
memset(_col_activeCollisionSlots, 0xFF, sizeof(_col_activeCollisionSlots)); memset(_col_activeCollisionSlots, 0xFF, sizeof(_col_activeCollisionSlots));

View File

@ -1,28 +1,19 @@
/* REminiscence - Flashback interpreter
* Copyright (C) 2005-2015 Gregory Montoir /*
* * REminiscence - Flashback interpreter
* This program is free software: you can redistribute it and/or modify * Copyright (C) 2005-2015 Gregory Montoir (cyx@users.sourceforge.net)
* 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 <http://www.gnu.org/licenses/>.
*/ */
#include <math.h>
#include "cutscene.h"
#include "resource.h" #include "resource.h"
#include "systemstub.h" #include "systemstub.h"
#include "util.h"
#include "video.h" #include "video.h"
#include "cutscene.h"
Cutscene::Cutscene(Resource *res, SystemStub *stub, Video *vid) Cutscene::Cutscene(Resource *res, SystemStub *stub, Video *vid)
: _res(res), _stub(stub), _vid(vid) { : _res(res), _stub(stub), _vid(vid) {
_patchedOffsetsTable = 0;
memset(_palBuf, 0, sizeof(_palBuf)); memset(_palBuf, 0, sizeof(_palBuf));
} }
@ -51,12 +42,8 @@ void Cutscene::updatePalette() {
if (_newPal) { if (_newPal) {
const uint8_t *p = _palBuf; const uint8_t *p = _palBuf;
for (int i = 0; i < 32; ++i) { for (int i = 0; i < 32; ++i) {
uint16_t color = READ_BE_UINT16(p); p += 2; const uint16_t color = READ_BE_UINT16(p); p += 2;
uint8_t t = (color == 0) ? 0 : 3; Color c = Video::AMIGA_convertColor(color);
Color c;
c.r = ((color & 0xF00) >> 6) | t;
c.g = ((color & 0x0F0) >> 2) | t;
c.b = ((color & 0x00F) << 2) | t;
_stub->setPaletteEntry(0xC0 + i, &c); _stub->setPaletteEntry(0xC0 + i, &c);
} }
_newPal = false; _newPal = false;
@ -67,21 +54,36 @@ void Cutscene::setPalette() {
sync(); sync();
updatePalette(); updatePalette();
SWAP(_page0, _page1); 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); _stub->updateScreen(0);
} }
void Cutscene::initRotationData(uint16_t a, uint16_t b, uint16_t c) { #if 0
int16_t n1 = _sinTable[a]; #define SIN(a) (int16_t)(sin(a * M_PI / 180) * 256)
int16_t n2 = _cosTable[a]; #define COS(a) (int16_t)(cos(a * M_PI / 180) * 256)
int16_t n3 = _sinTable[c]; #else
int16_t n4 = _cosTable[c]; #define SIN(a) _sinTable[a]
int16_t n5 = _sinTable[b]; #define COS(a) _cosTable[a]
int16_t n6 = _cosTable[b]; #endif
_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; cos(60) table: 128, math: 127
_rotData[3] = (-n3 * n2) >> 8; 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) { 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() { void Cutscene::swapLayers() {
if (_clearScreen == 0) { if (_clearScreen == 0) {
memcpy(_page1, _pageC, Video::GAMESCREEN_W * Video::GAMESCREEN_H); memcpy(_page1, _pageC, _vid->_layerSize);
} else { } else {
memset(_page1, 0xC0, Video::GAMESCREEN_W * Video::GAMESCREEN_H); memset(_page1, 0xC0, _vid->_layerSize);
} }
} }
@ -196,9 +198,7 @@ void Cutscene::drawCreditsText() {
} }
} else { } else {
*_textCurBuf++ = code; *_textCurBuf++ = code;
_textCurBuf = _textCurBuf; *_textCurBuf++ = 0xA;
*_textCurBuf = 0xA;
++_textCurPtr;
} }
} else { } else {
_creditsTextCounter -= 10; // XXX adjust _creditsTextCounter -= 10; // XXX adjust
@ -216,7 +216,7 @@ void Cutscene::drawProtectionShape(uint8_t shapeNum, int16_t zoom) {
int16_t x = 0; int16_t x = 0;
int16_t y = 0; int16_t y = 0;
zoom += 512; zoom += 512;
initRotationData(0, 180, 90); setRotationTransform(0, 180, 90);
const uint8_t *shapeOffsetTable = _protectionShapeData + READ_BE_UINT16(_protectionShapeData + 0x02); const uint8_t *shapeOffsetTable = _protectionShapeData + READ_BE_UINT16(_protectionShapeData + 0x02);
const uint8_t *shapeDataTable = _protectionShapeData + READ_BE_UINT16(_protectionShapeData + 0x0E); const uint8_t *shapeDataTable = _protectionShapeData + READ_BE_UINT16(_protectionShapeData + 0x0E);
@ -272,7 +272,7 @@ void Cutscene::op_waitForSync() {
if (_textBuf == _textCurBuf) { if (_textBuf == _textCurBuf) {
_creditsTextCounter = 20; _creditsTextCounter = 20;
} }
memcpy(_page1, _page0, Video::GAMESCREEN_W * Video::GAMESCREEN_H); memcpy(_page1, _page0, _vid->_layerSize);
drawCreditsText(); drawCreditsText();
setPalette(); setPalette();
} while (--n); } while (--n);
@ -364,7 +364,7 @@ void Cutscene::op_drawShape() {
drawShape(primitiveVertices, x + dx, y + dy); drawShape(primitiveVertices, x + dx, y + dy);
} }
if (_clearScreen != 0) { 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; y = READ_BE_UINT16(data); data += 2;
_shape_cur_x16 = _shape_ix - ix; _shape_cur_x16 = _shape_ix - ix;
_shape_cur_y16 = _shape_iy - iy; _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_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 * _rotData[2] + _shape_cur_y16 * _rotData[3]) >> 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].x = 0;
pr[0].y = -y; pr[0].y = -y;
pr[1].x = -x; 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; pt.y = c + READ_BE_UINT16(data); data += 2;
_shape_cur_x16 = _shape_ix - pt.x; _shape_cur_x16 = _shape_ix - pt.x;
_shape_cur_y16 = _shape_iy - pt.y; _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_x = _shape_ix + ((_rotMat[0] * _shape_cur_x16 + _rotMat[1] * _shape_cur_y16) >> 8);
_shape_cur_y = _shape_iy + ((_rotData[2] * _shape_cur_x16 + _rotData[3] * _shape_cur_y16) >> 8); _shape_cur_y = _shape_iy + ((_rotMat[2] * _shape_cur_x16 + _rotMat[3] * _shape_cur_y16) >> 8);
if (_shape_count != 0) { if (_shape_count != 0) {
_shape_cur_x16 = _shape_prev_x16 + (_shape_cur_x - _shape_prev_x) * zoom * 128; _shape_cur_x16 = _shape_prev_x16 + (_shape_cur_x - _shape_prev_x) * zoom * 128;
pt.x = ((_shape_cur_x16 + 0x8000) >> 16) + _shape_ix + d; 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_x16 = _shape_ix - x;
_shape_cur_y16 = _shape_iy - y; _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) { if (_shape_count == 0) {
_shape_ox = a; _shape_ox = a;
} }
_shape_cur_x = shape_last_x = 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) { if (_shape_count == 0) {
_shape_oy = a; _shape_oy = a;
} }
@ -712,10 +712,10 @@ void Cutscene::drawShapeScaleRotate(const uint8_t *data, int16_t zoom, int16_t b
sx = 0; sx = 0;
_shape_cur_x16 = _shape_ix - ix; _shape_cur_x16 = _shape_ix - ix;
_shape_cur_y16 = _shape_iy - iy; _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; pt2->x = a - shape_last_x;
shape_last_x = a; 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; pt2->y = a - shape_last_y;
shape_last_y = a; shape_last_y = a;
++pt2; ++pt2;
@ -786,7 +786,7 @@ void Cutscene::op_drawShapeScaleRotate() {
if (shapeOffset & 0x1000) { if (shapeOffset & 0x1000) {
r3 = fetchNextCmdWord(); r3 = fetchNextCmdWord();
} }
initRotationData(r1, r2, r3); setRotationTransform(r1, r2, r3);
const uint8_t *shapeOffsetTable = _polPtr + READ_BE_UINT16(_polPtr + 0x02); const uint8_t *shapeOffsetTable = _polPtr + READ_BE_UINT16(_polPtr + 0x02);
const uint8_t *shapeDataTable = _polPtr + READ_BE_UINT16(_polPtr + 0x0E); const uint8_t *shapeDataTable = _polPtr + READ_BE_UINT16(_polPtr + 0x0E);
@ -822,7 +822,7 @@ void Cutscene::op_drawCreditsText() {
if (_textCurBuf == _textBuf) { if (_textCurBuf == _textBuf) {
++_creditsTextCounter; ++_creditsTextCounter;
} }
memcpy(_page1, _page0, Video::GAMESCREEN_W * Video::GAMESCREEN_H); memcpy(_page1, _page0, _vid->_layerSize);
_frameDelay = 10; _frameDelay = 10;
setPalette(); setPalette();
} }
@ -842,7 +842,7 @@ void Cutscene::op_drawStringAtPos() {
// workaround for buggy cutscene script // workaround for buggy cutscene script
if (_id == 0x34 && (strId & 0xFFF) == 0x45) { if (_id == 0x34 && (strId & 0xFFF) == 0x45) {
if ((_cmdPtr - _cmdPtrBak) == 0xA) { 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); _stub->updateScreen(0);
} else { } else {
_stub->sleep(15); _stub->sleep(15);
@ -965,7 +965,7 @@ void Cutscene::load(uint16_t cutName) {
} }
_res->load(name, Resource::OT_CMP); _res->load(name, Resource::OT_CMP);
break; break;
case kResourceTypePC: case kResourceTypeDOS:
_res->load(name, Resource::OT_CMD); _res->load(name, Resource::OT_CMD);
_res->load(name, Resource::OT_POL); _res->load(name, Resource::OT_POL);
_res->load_CINE(); _res->load_CINE();
@ -1019,6 +1019,31 @@ void Cutscene::play() {
prepare(); prepare();
uint16_t cutName = _offsetsTable[_id * 2 + 0]; uint16_t cutName = _offsetsTable[_id * 2 + 0];
uint16_t cutOff = _offsetsTable[_id * 2 + 1]; 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) { if (cutName != 0xFFFF) {
load(cutName); load(cutName);
mainLoop(cutOff); mainLoop(cutOff);

View File

@ -1,18 +1,7 @@
/* REminiscence - Flashback interpreter
* Copyright (C) 2005-2015 Gregory Montoir /*
* * REminiscence - Flashback interpreter
* This program is free software: you can redistribute it and/or modify * Copyright (C) 2005-2015 Gregory Montoir (cyx@users.sourceforge.net)
* 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 <http://www.gnu.org/licenses/>.
*/ */
#ifndef CUTSCENE_H__ #ifndef CUTSCENE_H__
@ -36,6 +25,7 @@ struct Cutscene {
static const OpcodeStub _opcodeTable[]; static const OpcodeStub _opcodeTable[];
static const char *_namesTable[]; static const char *_namesTable[];
static const uint16_t _offsetsTable[]; static const uint16_t _offsetsTable[];
static const uint8_t _amigaDemoOffsetsTable[];
static const uint16_t _cosTable[]; static const uint16_t _cosTable[];
static const uint16_t _sinTable[]; static const uint16_t _sinTable[];
static const uint8_t _creditsData[]; static const uint8_t _creditsData[];
@ -47,6 +37,7 @@ struct Cutscene {
Resource *_res; Resource *_res;
SystemStub *_stub; SystemStub *_stub;
Video *_vid; Video *_vid;
const uint8_t *_patchedOffsetsTable;
uint16_t _id; uint16_t _id;
uint16_t _deathCutsceneId; uint16_t _deathCutsceneId;
@ -61,7 +52,7 @@ struct Cutscene {
uint8_t _palBuf[0x20 * 2]; uint8_t _palBuf[0x20 * 2];
uint16_t _startOffset; uint16_t _startOffset;
bool _creditsSequence; bool _creditsSequence;
uint32_t _rotData[4]; uint32_t _rotMat[4];
uint8_t _primitiveColor; uint8_t _primitiveColor;
uint8_t _clearScreen; uint8_t _clearScreen;
Point _vertices[0x80]; Point _vertices[0x80];
@ -97,7 +88,7 @@ struct Cutscene {
void copyPalette(const uint8_t *pal, uint16_t num); void copyPalette(const uint8_t *pal, uint16_t num);
void updatePalette(); void updatePalette();
void setPalette(); 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); 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 drawText(int16_t x, int16_t y, const uint8_t *p, uint16_t color, uint8_t *page, uint8_t n);
void swapLayers(); void swapLayers();

View File

@ -1,26 +1,16 @@
/* REminiscence - Flashback interpreter
* Copyright (C) 2005-2015 Gregory Montoir /*
* * REminiscence - Flashback interpreter
* This program is free software: you can redistribute it and/or modify * Copyright (C) 2005-2015 Gregory Montoir (cyx@users.sourceforge.net)
* 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 <http://www.gnu.org/licenses/>.
*/ */
#include <sys/param.h>
#include "file.h"
#include "fs.h" #include "fs.h"
#include "util.h"
#ifdef USE_ZLIB #ifdef USE_ZLIB
#include "zlib.h" #include "zlib.h"
#endif #endif
#include "file.h"
struct File_impl { struct File_impl {
bool _ioErr; bool _ioErr;
@ -183,7 +173,7 @@ bool File::open(const char *filename, const char *mode, const char *directory) {
if (!_impl) { if (!_impl) {
_impl = new stdFile; _impl = new stdFile;
} }
char path[512]; char path[MAXPATHLEN];
snprintf(path, sizeof(path), "%s/%s", directory, filename); snprintf(path, sizeof(path), "%s/%s", directory, filename);
debug(DBG_FILE, "Open file name '%s' mode '%s' path '%s'", filename, mode, path); debug(DBG_FILE, "Open file name '%s' mode '%s' path '%s'", filename, mode, path);
return _impl->open(path, mode); return _impl->open(path, mode);

19
file.h
View File

@ -1,18 +1,7 @@
/* REminiscence - Flashback interpreter
* Copyright (C) 2005-2015 Gregory Montoir /*
* * REminiscence - Flashback interpreter
* This program is free software: you can redistribute it and/or modify * Copyright (C) 2005-2015 Gregory Montoir (cyx@users.sourceforge.net)
* 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 <http://www.gnu.org/licenses/>.
*/ */
#ifndef FILE_H__ #ifndef FILE_H__

23
fs.cpp
View File

@ -1,18 +1,7 @@
/* REminiscence - Flashback interpreter
* Copyright (C) 2005-2015 Gregory Montoir /*
* * REminiscence - Flashback interpreter
* This program is free software: you can redistribute it and/or modify * Copyright (C) 2005-2015 Gregory Montoir (cyx@users.sourceforge.net)
* 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 <http://www.gnu.org/licenses/>.
*/ */
#ifdef _WIN32 #ifdef _WIN32
@ -21,8 +10,10 @@
#else #else
#include <dirent.h> #include <dirent.h>
#include <sys/stat.h> #include <sys/stat.h>
#include <sys/param.h>
#endif #endif
#include "fs.h" #include "fs.h"
#include "util.h"
struct FileName { struct FileName {
char *name; char *name;
@ -129,7 +120,7 @@ void FileSystem_impl::getPathListFromDirectory(const char *dir) {
if (de->d_name[0] == '.') { if (de->d_name[0] == '.') {
continue; continue;
} }
char filePath[512]; char filePath[MAXPATHLEN];
snprintf(filePath, sizeof(filePath), "%s/%s", dir, de->d_name); snprintf(filePath, sizeof(filePath), "%s/%s", dir, de->d_name);
struct stat st; struct stat st;
if (stat(filePath, &st) == 0) { if (stat(filePath, &st) == 0) {

19
fs.h
View File

@ -1,18 +1,7 @@
/* REminiscence - Flashback interpreter
* Copyright (C) 2005-2015 Gregory Montoir /*
* * REminiscence - Flashback interpreter
* This program is free software: you can redistribute it and/or modify * Copyright (C) 2005-2015 Gregory Montoir (cyx@users.sourceforge.net)
* 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 <http://www.gnu.org/licenses/>.
*/ */
#ifndef FS_H__ #ifndef FS_H__

246
game.cpp
View File

@ -1,28 +1,17 @@
/* REminiscence - Flashback interpreter
* Copyright (C) 2005-2015 Gregory Montoir /*
* * REminiscence - Flashback interpreter
* This program is free software: you can redistribute it and/or modify * Copyright (C) 2005-2015 Gregory Montoir (cyx@users.sourceforge.net)
* 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 <http://www.gnu.org/licenses/>.
*/ */
#include <ctime> #include <ctime>
#include "file.h" #include "file.h"
#include "fs.h" #include "fs.h"
#include "systemstub.h"
#include "unpack.h"
#include "game.h" #include "game.h"
#include "seq_player.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) Game::Game(SystemStub *stub, FileSystem *fs, const char *savePath, int level, ResourceType ver, Language lang)
: _cut(&_res, stub, &_vid), _menu(&_res, stub, &_vid), : _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_demo = 0;
_inp_record = false; _inp_record = false;
_inp_replay = false; _inp_replay = false;
_skillLevel = 1; _skillLevel = _menu._skill = 1;
_currentLevel = level; _currentLevel = _menu._level = level;
} }
void Game::run() { void Game::run() {
_stub->init(g_caption, Video::GAMESCREEN_W, Video::GAMESCREEN_H);
_randSeed = time(0); _randSeed = time(0);
_res.init();
_res.load_TEXT(); _res.load_TEXT();
switch (_res._type) { switch (_res._type) {
case kResourceTypeAmiga: case kResourceTypeAmiga:
_res.load("FONT8", Resource::OT_FNT, "SPR"); _res.load("FONT8", Resource::OT_FNT, "SPR");
break; break;
case kResourceTypePC: case kResourceTypeDOS:
_res.load("FB_TXT", Resource::OT_FNT); _res.load("FB_TXT", Resource::OT_FNT);
_res._hasSeqData = _fs->exists("INTRO.SEQ"); _res._hasSeqData = _fs->exists("INTRO.SEQ");
break; break;
} }
#ifndef BYPASS_PROTECTION if (!g_options.bypass_protection) {
while (!handleProtectionScreen()); while (!handleProtectionScreen());
if (_stub->_pi.quit) { if (_stub->_pi.quit) {
return; return;
}
} }
#endif
_mix.init(); _mix.init();
_mix._mod._isAmiga = _res.isAmiga();
playCutscene(0x40); playCutscene(0x40);
playCutscene(0x0D); playCutscene(0x0D);
if (!_cut._interrupted && _res._type == kResourceTypePC) {
playCutscene(0x4A);
}
switch (_res._type) { switch (_res._type) {
case kResourceTypeAmiga: case kResourceTypeAmiga:
@ -74,7 +60,7 @@ void Game::run() {
_res.load("ICON", Resource::OT_ICN, "SPR"); _res.load("ICON", Resource::OT_ICN, "SPR");
_res.load("PERSO", Resource::OT_SPM); _res.load("PERSO", Resource::OT_SPM);
break; break;
case kResourceTypePC: case kResourceTypeDOS:
_res.load("GLOBAL", Resource::OT_ICN); _res.load("GLOBAL", Resource::OT_ICN);
_res.load("GLOBAL", Resource::OT_SPC); _res.load("GLOBAL", Resource::OT_SPC);
_res.load("PERSO", Resource::OT_SPR); _res.load("PERSO", Resource::OT_SPR);
@ -83,12 +69,20 @@ void Game::run() {
break; break;
} }
if (_res.isAmiga()) {
displayTitleScreenAmiga();
_stub->setScreenSize(Video::GAMESCREEN_W, Video::GAMESCREEN_H);
}
while (!_stub->_pi.quit) { while (!_stub->_pi.quit) {
if (_res._type == kResourceTypePC) { if (_res.isDOS()) {
_mix.playMusic(1); _mix.playMusic(1);
if (!_menu.handleTitleScreen(_skillLevel, _currentLevel)) { _menu.handleTitleScreen();
if (_menu._selectedOption == Menu::MENU_OPTION_ITEM_QUIT) {
break; break;
} }
_skillLevel = _menu._skill;
_currentLevel = _menu._level;
_mix.stopMusic(); _mix.stopMusic();
} }
if (_currentLevel == 7) { if (_currentLevel == 7) {
@ -105,6 +99,7 @@ void Game::run() {
loadLevelData(); loadLevelData();
resetGameState(); resetGameState();
_endLoop = false; _endLoop = false;
_frameTimestamp = _stub->getTimeStamp();
while (!_stub->_pi.quit && !_endLoop) { while (!_stub->_pi.quit && !_endLoop) {
mainLoop(); mainLoop();
} }
@ -112,9 +107,45 @@ void Game::run() {
} }
_res.free_TEXT(); _res.free_TEXT();
_mix.free(); _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() { void Game::resetGameState() {
@ -166,7 +197,7 @@ void Game::mainLoop() {
return; return;
} }
} }
memcpy(_vid._frontLayer, _vid._backLayer, Video::GAMESCREEN_W * Video::GAMESCREEN_H); memcpy(_vid._frontLayer, _vid._backLayer, _vid._layerSize);
pge_getInput(); pge_getInput();
pge_prepare(); pge_prepare();
col_prepareRoomState(); col_prepareRoomState();
@ -221,14 +252,14 @@ void Game::mainLoop() {
} }
void Game::updateTiming() { void Game::updateTiming() {
static uint32_t tstamp = 0; static const int frameHz = 30;
int32_t delay = _stub->getTimeStamp() - tstamp; int32_t delay = _stub->getTimeStamp() - _frameTimestamp;
int32_t pause = (_stub->_pi.dbgMask & PlayerInput::DF_FASTMODE) ? 20 : 30; int32_t pause = (_stub->_pi.dbgMask & PlayerInput::DF_FASTMODE) ? 20 : (1000 / frameHz);
pause -= delay; pause -= delay;
if (pause > 0) { if (pause > 0) {
_stub->sleep(pause); _stub->sleep(pause);
} }
tstamp = _stub->getTimeStamp(); _frameTimestamp = _stub->getTimeStamp();
} }
void Game::playCutscene(int id) { void Game::playCutscene(int id) {
@ -292,12 +323,14 @@ void Game::playCutscene(int id) {
_mix.playMusic(Cutscene::_musicTable[_cut._id]); _mix.playMusic(Cutscene::_musicTable[_cut._id]);
} }
_cut.play(); _cut.play();
if (id == 0xD && !_cut._interrupted && _res.isDOS()) {
_cut._id = 0x4A;
_cut.play();
}
if (id == 0x3D) { if (id == 0x3D) {
_cut.startCredits(); _cut.startCredits();
} }
if (_cut._interrupted || id != 0x0D) { _mix.stopMusic();
_mix.stopMusic();
}
} }
} }
@ -348,7 +381,8 @@ void Game::inp_handleSpecialKeys() {
} else { } else {
if (_inp_demo->open(demoFile, "zwb", _savePath)) { if (_inp_demo->open(demoFile, "zwb", _savePath)) {
debug(DBG_INFO, "Recording input keys"); 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->writeUint16BE(0);
_inp_demo->writeUint32BE(_randSeed); _inp_demo->writeUint32BE(_randSeed);
record = true; record = true;
@ -395,7 +429,7 @@ void Game::showFinalScore() {
strcpy(buf, _menu._passwords[7][_skillLevel]); strcpy(buf, _menu._passwords[7][_skillLevel]);
_vid.drawString(buf, (256 - strlen(buf) * 8) / 2, 16, 0xE7); _vid.drawString(buf, (256 - strlen(buf) * 8) / 2, 16, 0xE7);
while (!_stub->_pi.quit) { 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->updateScreen(0);
_stub->processEvents(); _stub->processEvents();
if (_stub->_pi.enter) { if (_stub->_pi.enter) {
@ -407,7 +441,7 @@ void Game::showFinalScore() {
} }
bool Game::handleConfigPanel() { bool Game::handleConfigPanel() {
if (_res._type == kResourceTypeAmiga) { if (_res.isAmiga()) {
return true; return true;
} }
const int x = 7; const int x = 7;
@ -499,6 +533,10 @@ bool Game::handleConfigPanel() {
} }
break; break;
} }
if (_stub->_pi.escape) {
_stub->_pi.escape = false;
break;
}
} }
_vid.fullRefresh(); _vid.fullRefresh();
return (current == MENU_ITEM_ABORT); return (current == MENU_ITEM_ABORT);
@ -512,7 +550,7 @@ bool Game::handleContinueAbort() {
uint8_t color_inc = 0xFF; uint8_t color_inc = 0xFF;
Color col; Color col;
_stub->getPaletteEntry(0xE4, &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) { while (timeout >= 0 && !_stub->_pi.quit) {
const char *str; const char *str;
str = _res.getMenuString(LocaleData::LI_01_CONTINUE_OR_ABORT); str = _res.getMenuString(LocaleData::LI_01_CONTINUE_OR_ABORT);
@ -545,26 +583,28 @@ bool Game::handleContinueAbort() {
_stub->_pi.enter = false; _stub->_pi.enter = false;
return (current_color == 0); 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); _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; color_inc = 0;
} } else if (col.b < COLOR_MIN) {
if (col.b < 2) {
color_inc = 0xFF; color_inc = 0xFF;
} }
if (color_inc == 0xFF) { if (color_inc == 0xFF) {
col.b += 2; col.b += COLOR_STEP;
col.g += 2; col.g += COLOR_STEP;
} else { } else {
col.b -= 2; col.b -= COLOR_STEP;
col.g -= 2; col.g -= COLOR_STEP;
} }
_stub->setPaletteEntry(0xE4, &col); _stub->setPaletteEntry(0xE4, &col);
_stub->processEvents(); _stub->processEvents();
_stub->sleep(100); _stub->sleep(100);
--timeout; --timeout;
memcpy(_vid._frontLayer, _vid._tempLayer, Video::GAMESCREEN_W * Video::GAMESCREEN_H); memcpy(_vid._frontLayer, _vid._tempLayer, _vid._layerSize);
} }
return false; return false;
} }
@ -584,7 +624,7 @@ bool Game::handleProtectionScreen() {
int shapeNum = getRandomNumber() % 30; int shapeNum = getRandomNumber() % 30;
for (int16_t zoom = 2000; zoom != 0; zoom -= 100) { for (int16_t zoom = 2000; zoom != 0; zoom -= 100) {
_cut.drawProtectionShape(shapeNum, zoom); _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->updateScreen(0);
_stub->sleep(30); _stub->sleep(30);
} }
@ -595,7 +635,7 @@ bool Game::handleProtectionScreen() {
int len = 0; int len = 0;
do { do {
codeText[len] = '\0'; 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); _menu.drawString("PROTECTION", 2, 11, 5);
char buf[20]; char buf[20];
snprintf(buf, sizeof(buf), "CODE %d : %s", codeNum + 1, codeText); snprintf(buf, sizeof(buf), "CODE %d : %s", codeNum + 1, codeText);
@ -654,7 +694,7 @@ void Game::printLevelCode() {
if (_printLevelCodeCounter != 0) { if (_printLevelCodeCounter != 0) {
char buf[32]; char buf[32];
snprintf(buf, sizeof(buf), "CODE: %s", _menu._passwords[_currentLevel][_skillLevel]); 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; uint8_t icon_num = obj - 1;
drawIcon(icon_num, 80, 8, 0xA); drawIcon(icon_num, 80, 8, 0xA);
uint8_t txt_num = pge->init_PGE->text_num; 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); _vid.drawString(str, (176 - strlen(str) * 8) / 2, 26, 0xE6);
if (icon_num == 2) { if (icon_num == 2) {
printSaveStateCompleted(); printSaveStateCompleted();
@ -695,7 +735,7 @@ void Game::drawStoryTexts() {
if (_textToDisplay != 0xFFFF) { if (_textToDisplay != 0xFFFF) {
uint16_t text_col_mask = 0xE8; uint16_t text_col_mask = 0xE8;
const uint8_t *str = _res.getGameString(_textToDisplay); 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; int textSpeechSegment = 0;
while (!_stub->_pi.quit) { while (!_stub->_pi.quit) {
drawIcon(_currentInventoryIconNum, 80, 8, 0xA); drawIcon(_currentInventoryIconNum, 80, 8, 0xA);
@ -735,7 +775,7 @@ void Game::drawStoryTexts() {
break; break;
} }
++str; ++str;
memcpy(_vid._frontLayer, _vid._tempLayer, Video::GAMESCREEN_W * Video::GAMESCREEN_H); memcpy(_vid._frontLayer, _vid._tempLayer, _vid._layerSize);
} }
_textToDisplay = 0xFFFF; _textToDisplay = 0xFFFF;
} }
@ -811,7 +851,7 @@ void Game::prepareAnimsHelper(LivePGE *pge, int16_t dx, int16_t dy) {
w = ((dataPtr[2] >> 7) + 1) * 16; w = ((dataPtr[2] >> 7) + 1) * 16;
h = dataPtr[2] & 0x7F; h = dataPtr[2] & 0x7F;
break; break;
case kResourceTypePC: case kResourceTypeDOS:
w = dataPtr[2]; w = dataPtr[2];
h = dataPtr[3]; h = dataPtr[3];
dataPtr += 4; dataPtr += 4;
@ -885,7 +925,7 @@ void Game::drawAnimBuffer(uint8_t stateNum, AnimBufferState *state) {
_vid.AMIGA_decodeSpm(state->dataPtr, _res._memBuf); _vid.AMIGA_decodeSpm(state->dataPtr, _res._memBuf);
drawCharacter(_res._memBuf, state->x, state->y, state->h, state->w, pge->flags); drawCharacter(_res._memBuf, state->x, state->y, state->h, state->w, pge->flags);
break; break;
case kResourceTypePC: case kResourceTypeDOS:
if (!(state->dataPtr[-2] & 0x80)) { if (!(state->dataPtr[-2] & 0x80)) {
decodeCharacterFrame(state->dataPtr, _res._memBuf); decodeCharacterFrame(state->dataPtr, _res._memBuf);
drawCharacter(_res._memBuf, state->x, state->y, state->h, state->w, pge->flags); 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]; count = dataPtr[8];
dataPtr += 9; dataPtr += 9;
break; break;
case kResourceTypePC: case kResourceTypeDOS:
count = dataPtr[5]; count = dataPtr[5];
dataPtr += 6; dataPtr += 6;
break; break;
@ -956,13 +996,9 @@ void Game::drawObjectFrame(const uint8_t *bankDataPtr, const uint8_t *dataPtr, i
switch (_res._type) { switch (_res._type) {
case kResourceTypeAmiga: case kResourceTypeAmiga:
if (sprite_w == 24) {
// TODO: fix p24xN
return;
}
_vid.AMIGA_decodeSpc(src, sprite_w, sprite_h, _res._memBuf); _vid.AMIGA_decodeSpc(src, sprite_w, sprite_h, _res._memBuf);
break; break;
case kResourceTypePC: case kResourceTypeDOS:
_vid.PC_decodeSpc(src, sprite_w, sprite_h, _res._memBuf); _vid.PC_decodeSpc(src, sprite_w, sprite_h, _res._memBuf);
break; break;
} }
@ -1197,10 +1233,10 @@ int Game::loadMonsterSprites(LivePGE *pge) {
_curMonsterFrame = mList[0]; _curMonsterFrame = mList[0];
if (_curMonsterNum != mList[1]) { if (_curMonsterNum != mList[1]) {
_curMonsterNum = mList[1]; _curMonsterNum = mList[1];
if (_res._type == kResourceTypeAmiga) { if (_res.isAmiga()) {
_res.load(_monsterNames[1][_curMonsterNum], Resource::OT_SPM); _res.load(_monsterNames[1][_curMonsterNum], Resource::OT_SPM);
static const uint8_t tab[4] = { 0, 8, 0, 8 }; 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) { for (int i = 0; i < 8; ++i) {
_vid.setPaletteColorBE(0x50 + i, offset + i); _vid.setPaletteColorBE(0x50 + i, offset + i);
} }
@ -1220,15 +1256,23 @@ void Game::loadLevelMap() {
switch (_res._type) { switch (_res._type) {
case kResourceTypeAmiga: case kResourceTypeAmiga:
if (_currentLevel == 1) { if (_currentLevel == 1) {
static const uint8_t tab[64] = { int num = 0;
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 1, 0, switch (_currentRoom) {
0, 0, 0, 1, 0, 0, 0, 0, 2, 0, 0, 2, 0, 0, 0, 0, case 14:
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, case 19:
0, 0, 0, 0, 1, 1, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0 case 52:
}; case 53:
const int num = tab[_currentRoom]; num = 1;
break;
case 11:
case 24:
case 27:
case 56:
num = 2;
break;
}
if (num != 0 && _res._levNum != num) { if (num != 0 && _res._levNum != num) {
char name[8]; char name[9];
snprintf(name, sizeof(name), "level2_%d", num); snprintf(name, sizeof(name), "level2_%d", num);
_res.load(name, Resource::OT_LEV); _res.load(name, Resource::OT_LEV);
_res._levNum = num; _res._levNum = num;
@ -1236,8 +1280,12 @@ void Game::loadLevelMap() {
} }
_vid.AMIGA_decodeLev(_currentLevel, _currentRoom); _vid.AMIGA_decodeLev(_currentLevel, _currentRoom);
break; break;
case kResourceTypePC: case kResourceTypeDOS:
_vid.PC_decodeMap(_currentLevel, _currentRoom); if (_res._map) {
_vid.PC_decodeMap(_currentLevel, _currentRoom);
} else if (_res._lev) {
_vid.PC_decodeLev(_currentLevel, _currentRoom);
}
_vid.PC_setLevelPalettes(); _vid.PC_setLevelPalettes();
break; break;
} }
@ -1248,9 +1296,8 @@ void Game::loadLevelData() {
const Level *lvl = &_gameLevels[_currentLevel]; const Level *lvl = &_gameLevels[_currentLevel];
switch (_res._type) { switch (_res._type) {
case kResourceTypeAmiga: case kResourceTypeAmiga:
if (_fs->exists("demo.lev")) { // demo data files if (_res._isDemo) {
Cutscene::_namesTable[1] = "HOLOCUBE"; _cut._patchedOffsetsTable = Cutscene::_amigaDemoOffsetsTable;
Cutscene::_namesTable[4] = "CHUTE2";
static const char *fname1 = "demo"; static const char *fname1 = "demo";
static const char *fname2 = "demof"; static const char *fname2 = "demof";
_res.load(fname1, Resource::OT_MBK); _res.load(fname1, Resource::OT_MBK);
@ -1301,12 +1348,20 @@ void Game::loadLevelData() {
_res.load(lvl->nameAmiga, Resource::OT_SGD); _res.load(lvl->nameAmiga, Resource::OT_SGD);
} }
break; break;
case kResourceTypePC: case kResourceTypeDOS:
_res.load(lvl->name, Resource::OT_MBK); _res.load(lvl->name, Resource::OT_MBK);
_res.load(lvl->name, Resource::OT_CT); _res.load(lvl->name, Resource::OT_CT);
_res.load(lvl->name, Resource::OT_PAL); _res.load(lvl->name, Resource::OT_PAL);
_res.load(lvl->name, Resource::OT_RP); _res.load(lvl->name, Resource::OT_RP);
_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_PGE);
_res.load(lvl->name2, Resource::OT_OBJ); _res.load(lvl->name2, Resource::OT_OBJ);
_res.load(lvl->name2, Resource::OT_ANI); _res.load(lvl->name2, Resource::OT_ANI);
@ -1315,6 +1370,9 @@ void Game::loadLevelData() {
} }
_cut._id = lvl->cutscene_id; _cut._id = lvl->cutscene_id;
if (_res._isDemo && _currentLevel == 5) { // PC demo does not include TELEPORT.*
_cut._id = 0xFFFF;
}
_curMonsterNum = 0xFFFF; _curMonsterNum = 0xFFFF;
_curMonsterFrame = 0; _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); _vid.AMIGA_decodeIcn(_res._icn, iconNum, buf);
} }
break; break;
case kResourceTypePC: case kResourceTypeDOS:
_vid.PC_decodeIcn(_res._icn, iconNum, buf); _vid.PC_decodeIcn(_res._icn, iconNum, buf);
break; break;
} }
@ -1392,7 +1450,7 @@ void Game::playSound(uint8_t sfxId, uint8_t softVol) {
MixerChunk mc; MixerChunk mc;
mc.data = sfx->data; mc.data = sfx->data;
mc.len = sfx->len; 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); _mix.play(&mc, freq, Mixer::MAX_VOLUME >> softVol);
} }
} else { } else {
@ -1476,7 +1534,7 @@ void Game::handleInventory() {
drawIcon(76, icon_x_pos, 157, 0xA); drawIcon(76, icon_x_pos, 157, 0xA);
selected_pge = items[item_it].live_pge; selected_pge = items[item_it].live_pge;
uint8_t txt_num = items[item_it].init_pge->text_num; 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); _vid.drawString(str, (256 - strlen(str) * 8) / 2, 189, 0xED);
if (items[item_it].init_pge->init_flags & 4) { if (items[item_it].init_pge->init_flags & 4) {
char buf[10]; 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); sprintf(buf, "rs-level%d-%02d.state", _currentLevel + 1, slot);
} }
static const uint32_t TAG_FBSV = 0x46425356;
bool Game::saveGameState(uint8_t slot) { bool Game::saveGameState(uint8_t slot) {
bool success = false; bool success = false;
char stateFile[20]; char stateFile[20];
@ -1602,7 +1662,7 @@ bool Game::saveGameState(uint8_t slot) {
warning("Unable to save state file '%s'", stateFile); warning("Unable to save state file '%s'", stateFile);
} else { } else {
// header // header
f.writeUint32BE('FBSV'); f.writeUint32BE(TAG_FBSV);
f.writeUint16BE(2); f.writeUint16BE(2);
char buf[32]; char buf[32];
memset(buf, 0, sizeof(buf)); memset(buf, 0, sizeof(buf));
@ -1629,7 +1689,7 @@ bool Game::loadGameState(uint8_t slot) {
warning("Unable to open state file '%s'", stateFile); warning("Unable to open state file '%s'", stateFile);
} else { } else {
uint32_t id = f.readUint32BE(); uint32_t id = f.readUint32BE();
if (id != 'FBSV') { if (id != TAG_FBSV) {
warning("Bad save state format"); warning("Bad save state format");
} else { } else {
uint16_t ver = f.readUint16BE(); uint16_t ver = f.readUint16BE();

21
game.h
View File

@ -1,18 +1,7 @@
/* REminiscence - Flashback interpreter
* Copyright (C) 2005-2015 Gregory Montoir /*
* * REminiscence - Flashback interpreter
* This program is free software: you can redistribute it and/or modify * Copyright (C) 2005-2015 Gregory Montoir (cyx@users.sourceforge.net)
* 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 <http://www.gnu.org/licenses/>.
*/ */
#ifndef GAME_H__ #ifndef GAME_H__
@ -94,10 +83,12 @@ struct Game {
uint16_t _deathCutsceneCounter; uint16_t _deathCutsceneCounter;
bool _saveStateCompleted; bool _saveStateCompleted;
bool _endLoop; bool _endLoop;
uint32_t _frameTimestamp;
Game(SystemStub *, FileSystem *, const char *savePath, int level, ResourceType ver, Language lang); Game(SystemStub *, FileSystem *, const char *savePath, int level, ResourceType ver, Language lang);
void run(); void run();
void displayTitleScreenAmiga();
void resetGameState(); void resetGameState();
void mainLoop(); void mainLoop();
void updateTiming(); void updateTiming();

View File

@ -1,22 +1,11 @@
/* REminiscence - Flashback interpreter
* Copyright (C) 2005-2015 Gregory Montoir /*
* * REminiscence - Flashback interpreter
* This program is free software: you can redistribute it and/or modify * Copyright (C) 2005-2015 Gregory Montoir (cyx@users.sourceforge.net)
* 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 <http://www.gnu.org/licenses/>.
*/ */
#include "graphics.h" #include "graphics.h"
#include "util.h"
void Graphics::setClippingRect(int16_t rx, int16_t ry, int16_t rw, int16_t rh) { 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); debug(DBG_VIDEO, "Graphics::setClippingRect(%d, %d, %d, %d)", rx, ry, rw, rh);

View File

@ -1,18 +1,7 @@
/* REminiscence - Flashback interpreter
* Copyright (C) 2005-2015 Gregory Montoir /*
* * REminiscence - Flashback interpreter
* This program is free software: you can redistribute it and/or modify * Copyright (C) 2005-2015 Gregory Montoir (cyx@users.sourceforge.net)
* 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 <http://www.gnu.org/licenses/>.
*/ */
#ifndef GRAPHICS_H__ #ifndef GRAPHICS_H__

View File

@ -1,18 +1,7 @@
/* REminiscence - Flashback interpreter
* Copyright (C) 2005-2015 Gregory Montoir /*
* * REminiscence - Flashback interpreter
* This program is free software: you can redistribute it and/or modify * Copyright (C) 2005-2015 Gregory Montoir (cyx@users.sourceforge.net)
* 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 <http://www.gnu.org/licenses/>.
*/ */
#ifndef INTERN_H__ #ifndef INTERN_H__
@ -24,22 +13,19 @@
#include <cassert> #include <cassert>
#include <stdint.h> #include <stdint.h>
#include "util.h" #ifndef ABS
#define ABS(x) ((x)<0?-(x):(x)) #define ABS(x) ((x)<0?-(x):(x))
#endif
#ifndef MAX
#define MAX(x,y) ((x)>(y)?(x):(y)) #define MAX(x,y) ((x)>(y)?(x):(y))
#endif
#ifndef MIN
#define MIN(x,y) ((x)<(y)?(x):(y)) #define MIN(x,y) ((x)<(y)?(x):(y))
#endif
#ifndef ARRAYSIZE #ifndef ARRAYSIZE
#define ARRAYSIZE(a) (sizeof(a)/sizeof(a[0])) #define ARRAYSIZE(a) (sizeof(a)/sizeof(a[0]))
#endif #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) { inline uint16_t READ_BE_UINT16(const void *ptr) {
const uint8_t *b = (const uint8_t *)ptr; const uint8_t *b = (const uint8_t *)ptr;
return (b[0] << 8) | b[1]; return (b[0] << 8) | b[1];
@ -77,7 +63,15 @@ enum Language {
enum ResourceType { enum ResourceType {
kResourceTypeAmiga, 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 { struct Color {
@ -219,6 +213,7 @@ struct SoundFx {
uint8_t *data; uint8_t *data;
}; };
extern Options g_options;
extern const char *g_caption; extern const char *g_caption;
#endif // INTERN_H__ #endif // INTERN_H__

View File

@ -1,18 +1,7 @@
/* REminiscence - Flashback interpreter
* Copyright (C) 2005-2015 Gregory Montoir /*
* * REminiscence - Flashback interpreter
* This program is free software: you can redistribute it and/or modify * Copyright (C) 2005-2015 Gregory Montoir (cyx@users.sourceforge.net)
* 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 <http://www.gnu.org/licenses/>.
*/ */
#include "locale.h" #include "locale.h"

View File

@ -1,18 +1,7 @@
/* REminiscence - Flashback interpreter
* Copyright (C) 2005-2015 Gregory Montoir /*
* * REminiscence - Flashback interpreter
* This program is free software: you can redistribute it and/or modify * Copyright (C) 2005-2015 Gregory Montoir (cyx@users.sourceforge.net)
* 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 <http://www.gnu.org/licenses/>.
*/ */
#ifndef LOCALE_H__ #ifndef LOCALE_H__

105
main.cpp
View File

@ -1,33 +1,28 @@
/* REminiscence - Flashback interpreter
* Copyright (C) 2005-2015 Gregory Montoir /*
* * REminiscence - Flashback interpreter
* This program is free software: you can redistribute it and/or modify * Copyright (C) 2005-2015 Gregory Montoir (cyx@users.sourceforge.net)
* 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 <http://www.gnu.org/licenses/>.
*/ */
#include <ctype.h>
#include <getopt.h> #include <getopt.h>
#include <sys/stat.h> #include <sys/stat.h>
#include "file.h" #include "file.h"
#include "fs.h" #include "fs.h"
#include "game.h" #include "game.h"
#include "scaler.h"
#include "systemstub.h" #include "systemstub.h"
#include "util.h"
static const char *USAGE = static const char *USAGE =
"REminiscence - Flashback Interpreter\n" "REminiscence - Flashback Interpreter\n"
"Usage: %s [OPTIONS]...\n" "Usage: %s [OPTIONS]...\n"
" --datapath=PATH Path to data files (default 'DATA')\n" " --datapath=PATH Path to data files (default 'DATA')\n"
" --savepath=PATH Path to save files (default '.')\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 int detectVersion(FileSystem *fs) {
static const struct { static const struct {
@ -35,9 +30,11 @@ static int detectVersion(FileSystem *fs) {
int type; int type;
const char *name; const char *name;
} table[] = { } table[] = {
{ "LEVEL1.MAP", kResourceTypePC, "PC" }, { "DEMO_UK.ABA", kResourceTypeDOS, "DOS (Demo)" },
{ "INTRO.SEQ", kResourceTypeDOS, "DOS CD" },
{ "LEVEL1.MAP", kResourceTypeDOS, "DOS" },
{ "LEVEL1.LEV", kResourceTypeAmiga, "Amiga" }, { "LEVEL1.LEV", kResourceTypeAmiga, "Amiga" },
{ "DEMO.LEV", kResourceTypeAmiga, "Amiga" }, { "DEMO.LEV", kResourceTypeAmiga, "Amiga (Demo)" },
{ 0, -1 } { 0, -1 }
}; };
for (int i = 0; table[i].filename; ++i) { for (int i = 0; table[i].filename; ++i) {
@ -74,13 +71,65 @@ static Language detectLanguage(FileSystem *fs) {
return LANG_EN; return LANG_EN;
} }
Options g_options;
const char *g_caption = "REminiscence"; 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 #undef main
int main(int argc, char *argv[]) { int main(int argc, char *argv[]) {
const char *dataPath = "DATA"; const char *dataPath = "DATA";
const char *savePath = "."; const char *savePath = ".";
int levelNum = 0; int levelNum = 0;
int scaler = DEFAULT_SCALER;
bool fullscreen = false;
if (argc == 2) { if (argc == 2) {
// data path as the only command line argument // data path as the only command line argument
struct stat st; struct stat st;
@ -90,9 +139,11 @@ int main(int argc, char *argv[]) {
} }
while (1) { while (1) {
static struct option options[] = { static struct option options[] = {
{ "datapath", required_argument, 0, 1 }, { "datapath", required_argument, 0, 1 },
{ "savepath", required_argument, 0, 2 }, { "savepath", required_argument, 0, 2 },
{ "levelnum", required_argument, 0, 3 }, { "levelnum", required_argument, 0, 3 },
{ "fullscreen", no_argument, 0, 4 },
{ "scaler", required_argument, 0, 5 },
{ 0, 0, 0, 0 } { 0, 0, 0, 0 }
}; };
int index; int index;
@ -110,11 +161,21 @@ int main(int argc, char *argv[]) {
case 3: case 3:
levelNum = atoi(optarg); levelNum = atoi(optarg);
break; break;
case 4:
fullscreen = true;
break;
case 5:
scaler = atoi(optarg);
if (scaler < 0 || scaler >= NUM_SCALERS) {
scaler = DEFAULT_SCALER;
}
break;
default: default:
printf(USAGE, argv[0]); printf(USAGE, argv[0]);
return 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; 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); FileSystem fs(dataPath);
const int version = detectVersion(&fs); const int version = detectVersion(&fs);
@ -125,8 +186,10 @@ int main(int argc, char *argv[]) {
Language language = detectLanguage(&fs); Language language = detectLanguage(&fs);
SystemStub *stub = SystemStub_SDL_create(); SystemStub *stub = SystemStub_SDL_create();
Game *g = new Game(stub, &fs, savePath, levelNum, (ResourceType)version, language); 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(); g->run();
delete g; delete g;
stub->destroy();
delete stub; delete stub;
return 0; return 0;
} }

218
menu.cpp
View File

@ -1,29 +1,20 @@
/* REminiscence - Flashback interpreter
* Copyright (C) 2005-2015 Gregory Montoir /*
* * REminiscence - Flashback interpreter
* This program is free software: you can redistribute it and/or modify * Copyright (C) 2005-2015 Gregory Montoir (cyx@users.sourceforge.net)
* 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 <http://www.gnu.org/licenses/>.
*/ */
#include "game.h" #include "game.h"
#include "menu.h"
#include "resource.h" #include "resource.h"
#include "systemstub.h" #include "systemstub.h"
#include "util.h"
#include "video.h" #include "video.h"
#include "menu.h"
Menu::Menu(Resource *res, SystemStub *stub, Video *vid) Menu::Menu(Resource *res, SystemStub *stub, Video *vid)
: _res(res), _stub(stub), _vid(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) { 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) { void Menu::drawString2(const char *str, int16_t y, int16_t x) {
debug(DBG_MENU, "Menu::drawString2()"); debug(DBG_MENU, "Menu::drawString2()");
int len = 0; int i = 0;
while (*str) { for (; str[i]; ++i) {
_vid->PC_drawChar((uint8_t)*str, y, x + len); _vid->PC_drawChar((uint8_t)str[i], y, x + i);
++str;
++len;
} }
_vid->markBlockAsDirty(x * 8, y * 8, len * 8, 8); _vid->markBlockAsDirty(x * 8, y * 8, i * 8, 8);
} }
void Menu::loadPicture(const char *prefix) { void Menu::loadPicture(const char *prefix) {
@ -99,22 +88,20 @@ void Menu::loadPicture(const char *prefix) {
void Menu::handleInfoScreen() { void Menu::handleInfoScreen() {
debug(DBG_MENU, "Menu::handleInfoScreen()"); debug(DBG_MENU, "Menu::handleInfoScreen()");
_vid->fadeOut(); _vid->fadeOut();
switch (_res->_lang) { if (_res->_lang == LANG_FR) {
case LANG_FR:
loadPicture("instru_f"); loadPicture("instru_f");
break; } else {
case LANG_EN:
case LANG_DE:
case LANG_SP:
case LANG_IT:
loadPicture("instru_e"); loadPicture("instru_e");
break;
} }
_vid->fullRefresh(); _vid->fullRefresh();
_vid->updateScreen(); _vid->updateScreen();
do { do {
_stub->sleep(EVENTS_DELAY); _stub->sleep(EVENTS_DELAY);
_stub->processEvents(); _stub->processEvents();
if (_stub->_pi.escape) {
_stub->_pi.escape = false;
break;
}
if (_stub->_pi.enter) { if (_stub->_pi.enter) {
_stub->_pi.enter = false; _stub->_pi.enter = false;
break; break;
@ -122,18 +109,22 @@ void Menu::handleInfoScreen() {
} while (!_stub->_pi.quit); } while (!_stub->_pi.quit);
} }
void Menu::handleSkillScreen(uint8_t &new_skill) { void Menu::handleSkillScreen() {
debug(DBG_MENU, "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(); _vid->fadeOut();
loadPicture("menu3"); loadPicture("menu3");
_vid->fullRefresh(); _vid->fullRefresh();
drawString(_res->getMenuString(LocaleData::LI_12_SKILL_LEVEL), 12, 4, 3); drawString(_res->getMenuString(LocaleData::LI_12_SKILL_LEVEL), 12, 4, 3);
int skill_level = new_skill; int skill_level = _skill;
do { do {
drawString(_res->getMenuString(LocaleData::LI_13_EASY), 15, 14, option_colors[skill_level][0]); drawString(_res->getMenuString(LocaleData::LI_13_EASY), 15, 14, colors[skill_level][0]);
drawString(_res->getMenuString(LocaleData::LI_14_NORMAL), 17, 14, option_colors[skill_level][1]); drawString(_res->getMenuString(LocaleData::LI_14_NORMAL), 17, 14, colors[skill_level][1]);
drawString(_res->getMenuString(LocaleData::LI_15_EXPERT), 19, 14, option_colors[skill_level][2]); drawString(_res->getMenuString(LocaleData::LI_15_EXPERT), 19, 14, colors[skill_level][2]);
_vid->updateScreen(); _vid->updateScreen();
_stub->sleep(EVENTS_DELAY); _stub->sleep(EVENTS_DELAY);
@ -155,16 +146,20 @@ void Menu::handleSkillScreen(uint8_t &new_skill) {
skill_level = 0; skill_level = 0;
} }
} }
if (_stub->_pi.escape) {
_stub->_pi.escape = false;
break;
}
if (_stub->_pi.enter) { if (_stub->_pi.enter) {
_stub->_pi.enter = false; _stub->_pi.enter = false;
new_skill = skill_level; _skill = skill_level;
return; return;
} }
} while (!_stub->_pi.quit); } 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()"); debug(DBG_MENU, "Menu::handlePasswordScreen()");
_vid->fadeOut(); _vid->fadeOut();
_vid->_charShadowColor = _charVar1; _vid->_charShadowColor = _charVar1;
@ -206,14 +201,18 @@ bool Menu::handlePasswordScreen(uint8_t &new_skill, uint8_t &new_level) {
--len; --len;
} }
} }
if (_stub->_pi.escape) {
_stub->_pi.escape = false;
break;
}
if (_stub->_pi.enter) { if (_stub->_pi.enter) {
_stub->_pi.enter = false; _stub->_pi.enter = false;
password[len] = '\0'; password[len] = '\0';
for (int level = 0; level < 8; ++level) { for (int level = 0; level < 8; ++level) {
for (int skill = 0; skill < 3; ++skill) { for (int skill = 0; skill < 3; ++skill) {
if (strcmp(_passwords[level][skill], password) == 0) { if (strcmp(_passwords[level][skill], password) == 0) {
new_level = level; _level = level;
new_skill = skill; _skill = skill;
return true; return true;
} }
} }
@ -224,13 +223,13 @@ bool Menu::handlePasswordScreen(uint8_t &new_skill, uint8_t &new_level) {
return false; return false;
} }
bool Menu::handleLevelScreen(uint8_t &new_skill, uint8_t &new_level) { bool Menu::handleLevelScreen() {
debug(DBG_MENU, "Menu::handleLevelScreen()"); debug(DBG_MENU, "Menu::handleLevelScreen()");
_vid->fadeOut(); _vid->fadeOut();
loadPicture("menu2"); loadPicture("menu2");
_vid->fullRefresh(); _vid->fullRefresh();
uint8_t currentSkill = new_skill; int currentSkill = _skill;
uint8_t currentLevel = new_level; int currentLevel = _level;
do { do {
static const char *levelTitles[] = { static const char *levelTitles[] = {
"Titan / The Jungle", "Titan / The Jungle",
@ -287,56 +286,77 @@ bool Menu::handleLevelScreen(uint8_t &new_skill, uint8_t &new_level) {
currentSkill = 0; currentSkill = 0;
} }
} }
if (_stub->_pi.escape) {
_stub->_pi.escape = false;
break;
}
if (_stub->_pi.enter) { if (_stub->_pi.enter) {
_stub->_pi.enter = false; _stub->_pi.enter = false;
new_skill = currentSkill; _skill = currentSkill;
new_level = currentLevel; _level = currentLevel;
return true; return true;
} }
} while (!_stub->_pi.quit); } while (!_stub->_pi.quit);
return false; return false;
} }
bool Menu::handleTitleScreen(uint8_t &new_skill, uint8_t &new_level) { void Menu::handleTitleScreen() {
debug(DBG_MENU, "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; _charVar1 = 0;
_charVar2 = 0; _charVar2 = 0;
_charVar3 = 0; _charVar3 = 0;
_charVar4 = 0; _charVar4 = 0;
_charVar5 = 0; _charVar5 = 0;
static const struct {
int str; static const int MAX_MENU_ITEMS = 5;
int opt; Item menuItems[MAX_MENU_ITEMS];
} menu_items[] = { int menuItemsCount = 0;
{ LocaleData::LI_07_START, MENU_OPTION_ITEM_START },
#ifdef ENABLE_PASSWORD_MENU menuItems[menuItemsCount].str = LocaleData::LI_07_START;
{ LocaleData::LI_08_SKILL, MENU_OPTION_ITEM_SKILL }, menuItems[menuItemsCount].opt = MENU_OPTION_ITEM_START;
{ LocaleData::LI_09_PASSWORD, MENU_OPTION_ITEM_PASSWORD }, ++menuItemsCount;
#else if (g_options.enable_password_menu) {
{ LocaleData::LI_06_LEVEL, MENU_OPTION_ITEM_LEVEL }, menuItems[menuItemsCount].str = LocaleData::LI_08_SKILL;
#endif menuItems[menuItemsCount].opt = MENU_OPTION_ITEM_SKILL;
{ LocaleData::LI_10_INFO, MENU_OPTION_ITEM_INFO }, ++menuItemsCount;
{ LocaleData::LI_11_QUIT, MENU_OPTION_ITEM_QUIT } menuItems[menuItemsCount].str = LocaleData::LI_09_PASSWORD;
}; menuItems[menuItemsCount].opt = MENU_OPTION_ITEM_PASSWORD;
static const int menu_items_count = ARRAYSIZE(menu_items); ++menuItemsCount;
while (!quit_loop) { } else {
if (reinit_screen) { 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(); _vid->fadeOut();
loadPicture("menu1"); loadPicture("menu1");
_vid->fullRefresh(); _vid->fullRefresh();
_charVar3 = 1; _charVar3 = 1;
_charVar4 = 2; _charVar4 = 2;
menu_entry = 0; currentEntry = 0;
reinit_screen = false; _currentScreen = _nextScreen;
_nextScreen = -1;
} }
int selected_menu_entry = -1; int selectedItem = -1;
const int y_start = 26 - menu_items_count * 2; const int yPos = 26 - menuItemsCount * 2;
for (int i = 0; i < menu_items_count; ++i) { for (int i = 0; i < menuItemsCount; ++i) {
drawString(_res->getMenuString(menu_items[i].str), y_start + i * 2, 20, (i == menu_entry) ? 2 : 3); drawString(_res->getMenuString(menuItems[i].str), yPos + i * 2, 20, (i == currentEntry) ? 2 : 3);
} }
_vid->updateScreen(); _vid->updateScreen();
@ -345,63 +365,55 @@ bool Menu::handleTitleScreen(uint8_t &new_skill, uint8_t &new_level) {
if (_stub->_pi.dirMask & PlayerInput::DIR_UP) { if (_stub->_pi.dirMask & PlayerInput::DIR_UP) {
_stub->_pi.dirMask &= ~PlayerInput::DIR_UP; _stub->_pi.dirMask &= ~PlayerInput::DIR_UP;
if (menu_entry != 0) { if (currentEntry != 0) {
--menu_entry; --currentEntry;
} else { } else {
menu_entry = menu_items_count - 1; currentEntry = menuItemsCount - 1;
} }
} }
if (_stub->_pi.dirMask & PlayerInput::DIR_DOWN) { if (_stub->_pi.dirMask & PlayerInput::DIR_DOWN) {
_stub->_pi.dirMask &= ~PlayerInput::DIR_DOWN; _stub->_pi.dirMask &= ~PlayerInput::DIR_DOWN;
if (menu_entry != menu_items_count - 1) { if (currentEntry != menuItemsCount - 1) {
++menu_entry; ++currentEntry;
} else { } else {
menu_entry = 0; currentEntry = 0;
} }
} }
if (_stub->_pi.enter) { if (_stub->_pi.enter) {
_stub->_pi.enter = false; _stub->_pi.enter = false;
selected_menu_entry = menu_entry; selectedItem = currentEntry;
} }
if (selected_menu_entry != -1) { if (selectedItem != -1) {
switch (menu_items[selected_menu_entry].opt) { _selectedOption = menuItems[selectedItem].opt;
switch (_selectedOption) {
case MENU_OPTION_ITEM_START: case MENU_OPTION_ITEM_START:
quit_loop = true; quitLoop = true;
break; break;
case MENU_OPTION_ITEM_SKILL: case MENU_OPTION_ITEM_SKILL:
handleSkillScreen(new_skill); _currentScreen = SCREEN_SKILL;
reinit_screen = true; handleSkillScreen();
break; break;
case MENU_OPTION_ITEM_PASSWORD: case MENU_OPTION_ITEM_PASSWORD:
if (handlePasswordScreen(new_skill, new_level)) { _currentScreen = SCREEN_PASSWORD;
quit_loop = true; quitLoop = handlePasswordScreen();
} else {
reinit_screen = true;
}
break; break;
case MENU_OPTION_ITEM_LEVEL: case MENU_OPTION_ITEM_LEVEL:
if (handleLevelScreen(new_skill, new_level)) { _currentScreen = SCREEN_LEVEL;
quit_loop = true; quitLoop = handleLevelScreen();
} else {
reinit_screen = true;
}
break; break;
case MENU_OPTION_ITEM_INFO: case MENU_OPTION_ITEM_INFO:
_currentScreen = SCREEN_INFO;
handleInfoScreen(); handleInfoScreen();
reinit_screen = true;
break; break;
case MENU_OPTION_ITEM_QUIT: case MENU_OPTION_ITEM_QUIT:
continue_game = false; quitLoop = true;
quit_loop = true;
break; break;
} }
_nextScreen = SCREEN_TITLE;
} }
if (_stub->_pi.quit) { if (_stub->_pi.quit) {
continue_game = false;
quit_loop = true;
break; break;
} }
} }
return continue_game;
} }

48
menu.h
View File

@ -1,18 +1,7 @@
/* REminiscence - Flashback interpreter
* Copyright (C) 2005-2015 Gregory Montoir /*
* * REminiscence - Flashback interpreter
* This program is free software: you can redistribute it and/or modify * Copyright (C) 2005-2015 Gregory Montoir (cyx@users.sourceforge.net)
* 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 <http://www.gnu.org/licenses/>.
*/ */
#ifndef MENU_H__ #ifndef MENU_H__
@ -33,18 +22,36 @@ struct Menu {
MENU_OPTION_ITEM_INFO, MENU_OPTION_ITEM_INFO,
MENU_OPTION_ITEM_QUIT MENU_OPTION_ITEM_QUIT
}; };
enum {
SCREEN_TITLE,
SCREEN_SKILL,
SCREEN_PASSWORD,
SCREEN_LEVEL,
SCREEN_INFO
};
enum { enum {
EVENTS_DELAY = 80 EVENTS_DELAY = 80
}; };
struct Item {
int str;
int opt;
};
static const char *_passwords[8][3]; static const char *_passwords[8][3];
Resource *_res; Resource *_res;
SystemStub *_stub; SystemStub *_stub;
Video *_vid; Video *_vid;
const char **_textOptions; int _currentScreen;
int _nextScreen;
int _selectedOption;
int _skill;
int _level;
uint8_t _charVar1; uint8_t _charVar1;
uint8_t _charVar2; uint8_t _charVar2;
uint8_t _charVar3; uint8_t _charVar3;
@ -56,11 +63,12 @@ struct Menu {
void drawString(const char *str, int16_t y, int16_t x, uint8_t color); 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 drawString2(const char *str, int16_t y, int16_t x);
void loadPicture(const char *prefix); void loadPicture(const char *prefix);
void handleInfoScreen(); void handleInfoScreen();
void handleSkillScreen(uint8_t &new_skill); void handleSkillScreen();
bool handlePasswordScreen(uint8_t &new_skill, uint8_t &new_level); bool handlePasswordScreen();
bool handleLevelScreen(uint8_t &new_skill, uint8_t &new_level); bool handleLevelScreen();
bool handleTitleScreen(uint8_t &new_skill, uint8_t &new_level); void handleTitleScreen();
}; };
#endif // MENU_H__ #endif // MENU_H__

View File

@ -1,23 +1,12 @@
/* REminiscence - Flashback interpreter
* Copyright (C) 2005-2015 Gregory Montoir /*
* * REminiscence - Flashback interpreter
* This program is free software: you can redistribute it and/or modify * Copyright (C) 2005-2015 Gregory Montoir (cyx@users.sourceforge.net)
* 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 <http://www.gnu.org/licenses/>.
*/ */
#include "mixer.h" #include "mixer.h"
#include "systemstub.h" #include "systemstub.h"
#include "util.h"
Mixer::Mixer(FileSystem *fs, SystemStub *stub) Mixer::Mixer(FileSystem *fs, SystemStub *stub)
: _stub(stub), _musicType(MT_NONE), _mod(this, fs), _ogg(this, fs), _sfx(this) { : _stub(stub), _musicType(MT_NONE), _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); memset(buf, 0, len);
if (_premixHook) { if (_premixHook) {
if (!_premixHook(_premixHookData, buf, len)) { if (!_premixHook(_premixHookData, buf, len)) {
@ -160,12 +159,13 @@ void Mixer::mix(int8_t *buf, int len) {
ch->active = false; ch->active = false;
break; break;
} }
int out = resampleLinear(&ch->chunk, ch->chunkPos, ch->chunkInc, FRAC_BITS); const int sample = ch->chunk.getPCM(ch->chunkPos >> FRAC_BITS);
addclamp(buf[pos], out * ch->volume / Mixer::MAX_VOLUME); addclamp(buf[pos], sample * ch->volume / Mixer::MAX_VOLUME);
ch->chunkPos += ch->chunkInc; ch->chunkPos += ch->chunkInc;
} }
} }
} }
nr(buf, len, out);
} }
void Mixer::addclamp(int8_t& a, int b) { void Mixer::addclamp(int8_t& a, int b) {

28
mixer.h
View File

@ -1,18 +1,7 @@
/* REminiscence - Flashback interpreter
* Copyright (C) 2005-2015 Gregory Montoir /*
* * REminiscence - Flashback interpreter
* This program is free software: you can redistribute it and/or modify * Copyright (C) 2005-2015 Gregory Montoir (cyx@users.sourceforge.net)
* 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 <http://www.gnu.org/licenses/>.
*/ */
#ifndef MIXER_H__ #ifndef MIXER_H__
@ -96,13 +85,4 @@ struct Mixer {
static void mixCallback(void *param, int8_t *buf, int len); static void mixCallback(void *param, int8_t *buf, int len);
}; };
template <class T>
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__ #endif // MIXER_H__

View File

@ -1,33 +1,168 @@
/* REminiscence - Flashback interpreter
* Copyright (C) 2005-2015 Gregory Montoir /*
* * REminiscence - Flashback interpreter
* This program is free software: you can redistribute it and/or modify * Copyright (C) 2005-2015 Gregory Montoir (cyx@users.sourceforge.net)
* 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 <http://www.gnu.org/licenses/>.
*/ */
#include "file.h" #include "file.h"
#include "mixer.h" #include "mixer.h"
#include "mod_player.h" #include "mod_player.h"
#include "util.h"
#ifdef USE_MODPLUG
#include <libmodplug/modplug.h>
ModPlayer::ModPlayer(Mixer *mixer, FileSystem *fs) struct ModPlayer_impl {
: _playing(false), _mix(mixer), _fs(fs) {
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)); 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) { for (int p = 0; p < 36; ++p) {
if (_periodTable[p] == period) { if (ModPlayer::_periodTable[p] == period) {
return fineTune * 36 + p; return fineTune * 36 + p;
} }
} }
@ -35,7 +170,11 @@ uint16_t ModPlayer::findPeriod(uint16_t period, uint8_t fineTune) const {
return 0; 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); f->read(_modInfo.songName, 20);
_modInfo.songName[20] = 0; _modInfo.songName[20] = 0;
debug(DBG_MOD, "songName = '%s'", _modInfo.songName); 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]) { if (_modInfo.songName[0]) {
free(_modInfo.patternsTable); free(_modInfo.patternsTable);
for (int s = 0; s < NUM_SAMPLES; ++s) { for (int s = 0; s < NUM_SAMPLES; ++s) {
@ -90,49 +244,10 @@ void ModPlayer::unload() {
} }
memset(&_modInfo, 0, sizeof(_modInfo)); memset(&_modInfo, 0, sizeof(_modInfo));
} }
_playing = false;
} }
void ModPlayer::play(uint8_t num) { void ModPlayer_impl::handleNote(int trackNum, uint32_t noteData) {
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) {
Track *tk = &_tracks[trackNum]; Track *tk = &_tracks[trackNum];
uint16_t sampleNum = ((noteData >> 24) & 0xF0) | ((noteData >> 12) & 0xF); uint16_t sampleNum = ((noteData >> 24) & 0xF0) | ((noteData >> 12) & 0xF);
uint16_t samplePeriod = (noteData >> 16) & 0xFFF; uint16_t samplePeriod = (noteData >> 16) & 0xFFF;
@ -146,10 +261,10 @@ void ModPlayer::handleNote(int trackNum, uint32_t noteData) {
if (samplePeriod != 0) { if (samplePeriod != 0) {
tk->periodIndex = findPeriod(samplePeriod, tk->sample->fineTune); tk->periodIndex = findPeriod(samplePeriod, tk->sample->fineTune);
if ((effectData >> 8) != 0x3 && (effectData >> 8) != 0x5) { if ((effectData >> 8) != 0x3 && (effectData >> 8) != 0x5) {
tk->period = _periodTable[tk->periodIndex]; tk->period = ModPlayer::_periodTable[tk->periodIndex];
tk->freq = PAULA_FREQ / tk->period; tk->freq = PAULA_FREQ / tk->period;
} else { } else {
tk->portamento = _periodTable[tk->periodIndex]; tk->portamento = ModPlayer::_periodTable[tk->periodIndex];
} }
tk->vibratoAmp = 0; tk->vibratoAmp = 0;
tk->vibratoSpeed = 0; tk->vibratoSpeed = 0;
@ -158,7 +273,7 @@ void ModPlayer::handleNote(int trackNum, uint32_t noteData) {
tk->effectData = effectData; 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); debug(DBG_MOD, "ModPlayer::applyVolumeSlide(%d, %d)", trackNum, amount);
Track *tk = &_tracks[trackNum]; Track *tk = &_tracks[trackNum];
int vol = tk->volume + amount; int vol = tk->volume + amount;
@ -170,10 +285,16 @@ void ModPlayer::applyVolumeSlide(int trackNum, int amount) {
tk->volume = vol; 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); debug(DBG_MOD, "ModPlayer::applyVibrato(%d)", trackNum);
Track *tk = &_tracks[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) { if (tk->period + vib != 0) {
tk->freq = PAULA_FREQ / (tk->period + vib); 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); debug(DBG_MOD, "ModPlayer::applyPortamento(%d)", trackNum);
Track *tk = &_tracks[trackNum]; Track *tk = &_tracks[trackNum];
if (tk->period < tk->portamento) { 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]; Track *tk = &_tracks[trackNum];
uint8_t effectNum = tk->effectData >> 8; uint8_t effectNum = tk->effectData >> 8;
uint8_t effectXY = tk->effectData & 0xFF; uint8_t effectXY = tk->effectData & 0xFF;
@ -209,10 +330,10 @@ void ModPlayer::handleEffect(int trackNum, bool tick) {
uint16_t period = tk->period; uint16_t period = tk->period;
switch (_currentTick & 3) { switch (_currentTick & 3) {
case 1: case 1:
period = _periodTable[tk->periodIndex + effectX]; period = ModPlayer::_periodTable[tk->periodIndex + effectX];
break; break;
case 2: case 2:
period = _periodTable[tk->periodIndex + effectY]; period = ModPlayer::_periodTable[tk->periodIndex + effectY];
break; break;
} }
tk->freq = PAULA_FREQ / period; 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) { if (!_playing) {
return; return;
} }
@ -436,9 +557,9 @@ void ModPlayer::handleTick() {
// On the amiga version, the introduction cutscene is shorter than the PC version ; // 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 // so the music module doesn't synchronize at all with the PC datafiles, here we
// add a hack to let the music play longer // add a hack to let the music play longer
if (_songNum == 0 && _currentPatternOrder == 3 && !_introSongHack) { if (_currentPatternOrder == 3 && _repeatIntro) {
_currentPatternOrder = 1; _currentPatternOrder = 1;
_introSongHack = true; _repeatIntro = false;
// warning("Introduction module synchronization hack"); // 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) { for (int i = 0; i < NUM_TRACKS; ++i) {
Track *tk = &_tracks[i]; Track *tk = &_tracks[i];
if (tk->sample != 0 && tk->delayCounter == 0) { 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 len = si->len << FRAC_BITS;
int loopLen = si->repeatLen << FRAC_BITS; int loopLen = si->repeatLen << FRAC_BITS;
int loopPos = si->repeatPos << 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 curLen = samplesLen;
int pos = tk->pos; int pos = tk->pos;
while (curLen != 0) { while (curLen != 0) {
@ -485,7 +606,7 @@ void ModPlayer::mixSamples(int8_t *buf, int samplesLen) {
curLen = 0; curLen = 0;
} }
while (count--) { 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); Mixer::addclamp(*mixbuf++, out * tk->volume / 64);
pos += deltaPos; 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) { if (_playing) {
memset(buf, 0, len); memset(buf, 0, len);
const int samplesPerTick = _mix->getSampleRate() / (50 * _songTempo / 125); const int samplesPerTick = _mixingRate / (50 * _songTempo / 125);
while (len != 0) { while (len != 0) {
if (_samplesLeft == 0) { if (_samplesLeft == 0) {
handleTick(); handleTick();
@ -516,7 +637,42 @@ bool ModPlayer::mix(int8_t *buf, int len) {
} }
return _playing; 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) { bool ModPlayer::mixCallback(void *param, int8_t *buf, int len) {
return ((ModPlayer *)param)->mix(buf, len); return ((ModPlayer_impl *)param)->mix(buf, len);
} }

View File

@ -1,18 +1,7 @@
/* REminiscence - Flashback interpreter
* Copyright (C) 2005-2015 Gregory Montoir /*
* * REminiscence - Flashback interpreter
* This program is free software: you can redistribute it and/or modify * Copyright (C) 2005-2015 Gregory Montoir (cyx@users.sourceforge.net)
* 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 <http://www.gnu.org/licenses/>.
*/ */
#ifndef MOD_PLAYER_H__ #ifndef MOD_PLAYER_H__
@ -20,101 +9,27 @@
#include "intern.h" #include "intern.h"
struct File;
struct FileSystem; struct FileSystem;
struct Mixer; struct Mixer;
struct ModPlayer_impl;
struct ModPlayer { 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 uint16_t _periodTable[];
static const char *_modulesFiles[][2]; static const char *_modulesFiles[][2];
static const int _modulesFilesCount; static const int _modulesFilesCount;
ModuleInfo _modInfo; bool _isAmiga;
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 _playing; bool _playing;
Track _tracks[NUM_TRACKS];
Mixer *_mix; 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 play(int num);
void load(File *f);
void unload();
void play(uint8_t num);
void stop(); 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); static bool mixCallback(void *param, int8_t *buf, int len);
}; };

View File

@ -1,18 +1,7 @@
/* REminiscence - Flashback interpreter
* Copyright (C) 2005-2015 Gregory Montoir /*
* * REminiscence - Flashback interpreter
* This program is free software: you can redistribute it and/or modify * Copyright (C) 2005-2015 Gregory Montoir (cyx@users.sourceforge.net)
* 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 <http://www.gnu.org/licenses/>.
*/ */
#ifdef USE_TREMOR #ifdef USE_TREMOR

View File

@ -1,18 +1,7 @@
/* REminiscence - Flashback interpreter
* Copyright (C) 2005-2015 Gregory Montoir /*
* * REminiscence - Flashback interpreter
* This program is free software: you can redistribute it and/or modify * Copyright (C) 2005-2015 Gregory Montoir (cyx@users.sourceforge.net)
* 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 <http://www.gnu.org/licenses/>.
*/ */
#ifndef OGG_PLAYER_H__ #ifndef OGG_PLAYER_H__

View File

@ -1,25 +1,14 @@
/* REminiscence - Flashback interpreter
* Copyright (C) 2005-2015 Gregory Montoir /*
* * REminiscence - Flashback interpreter
* This program is free software: you can redistribute it and/or modify * Copyright (C) 2005-2015 Gregory Montoir (cyx@users.sourceforge.net)
* 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 <http://www.gnu.org/licenses/>.
*/ */
#include "cutscene.h" #include "cutscene.h"
#include "game.h"
#include "resource.h" #include "resource.h"
#include "systemstub.h" #include "systemstub.h"
#include "game.h" #include "util.h"
void Game::pge_resetGroups() { void Game::pge_resetGroups() {
memset(_pge_groupsTable, 0, sizeof(_pge_groupsTable)); memset(_pge_groupsTable, 0, sizeof(_pge_groupsTable));
@ -133,7 +122,7 @@ void Game::pge_process(LivePGE *pge) {
pge_setupNextAnimFrame(pge, le); pge_setupNextAnimFrame(pge, le);
} }
const uint8_t *anim_data = _res.getAniData(pge->obj_type); 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; InitPGE *init_pge = pge->init_PGE;
assert(init_pge->obj_node_number < _res._numObjectNodes); assert(init_pge->obj_node_number < _res._numObjectNodes);
ObjectNode *on = _res._objectNodesMap[init_pge->obj_node_number]; ObjectNode *on = _res._objectNodesMap[init_pge->obj_node_number];
@ -200,7 +189,7 @@ void Game::pge_setupNextAnimFrame(LivePGE *pge, GroupPGE *le) {
set_anim: set_anim:
const uint8_t *anim_data = _res.getAniData(pge->obj_type); 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; uint8_t _dl = pge->anim_seq;
const uint8_t *anim_frame = anim_data + 6 + _dl * 4; const uint8_t *anim_frame = anim_data + 6 + _dl * 4;
while (_dh > _dl) { while (_dh > _dl) {
@ -239,12 +228,12 @@ void Game::pge_playAnimSound(LivePGE *pge, uint16_t arg2) {
void Game::pge_setupAnim(LivePGE *pge) { void Game::pge_setupAnim(LivePGE *pge) {
debug(DBG_PGE, "Game::pge_setupAnim() pgeNum=%d", pge - &_pgeLive[0]); debug(DBG_PGE, "Game::pge_setupAnim() pgeNum=%d", pge - &_pgeLive[0]);
const uint8_t *anim_data = _res.getAniData(pge->obj_type); 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; pge->anim_seq = 0;
} }
const uint8_t *anim_frame = anim_data + 6 + pge->anim_seq * 4; const uint8_t *anim_frame = anim_data + 6 + pge->anim_seq * 4;
if (READ_LE_UINT16(anim_frame) != 0xFFFF) { if (_res._readUint16(anim_frame) != 0xFFFF) {
uint16_t fl = READ_LE_UINT16(anim_frame); uint16_t fl = _res._readUint16(anim_frame);
if (pge->flags & 1) { if (pge->flags & 1) {
fl ^= 0x8000; fl ^= 0x8000;
pge->pos_x -= (int8_t)anim_frame[2]; pge->pos_x -= (int8_t)anim_frame[2];
@ -257,10 +246,10 @@ void Game::pge_setupAnim(LivePGE *pge) {
pge->flags |= 2; pge->flags |= 2;
} }
pge->flags &= ~8; pge->flags &= ~8;
if (READ_LE_UINT16(anim_data + 4) & 0xFFFF) { if (_res._readUint16(anim_data + 4) & 0xFFFF) {
pge->flags |= 8; 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) { void Game::pge_setupDefaultAnim(LivePGE *pge) {
const uint8_t *anim_data = _res.getAniData(pge->obj_type); 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; pge->anim_seq = 0;
} }
const uint8_t *anim_frame = anim_data + 6 + pge->anim_seq * 4; const uint8_t *anim_frame = anim_data + 6 + pge->anim_seq * 4;
if (READ_LE_UINT16(anim_frame) != 0xFFFF) { if (_res._readUint16(anim_frame) != 0xFFFF) {
uint16_t f = READ_LE_UINT16(anim_data); uint16_t f = _res._readUint16(anim_data);
if (pge->flags & 1) { if (pge->flags & 1) {
f ^= 0x8000; f ^= 0x8000;
} }
@ -385,10 +374,10 @@ void Game::pge_setupDefaultAnim(LivePGE *pge) {
pge->flags |= 2; pge->flags |= 2;
} }
pge->flags &= ~8; pge->flags &= ~8;
if (READ_LE_UINT16(anim_data + 4) & 0xFFFF) { if (_res._readUint16(anim_data + 4) & 0xFFFF) {
pge->flags |= 8; 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); 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]; int16_t r = args->pge->init_PGE->counter_values[args->a];
args->pge->room_location = r; args->pge->room_location = r;
if (r == 1) { 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; _loadMap = true;
} }
pge_setupDefaultAnim(args->pge); pge_setupDefaultAnim(args->pge);

View File

@ -1,31 +1,25 @@
/* REminiscence - Flashback interpreter
* Copyright (C) 2005-2015 Gregory Montoir /*
* * REminiscence - Flashback interpreter
* This program is free software: you can redistribute it and/or modify * Copyright (C) 2005-2015 Gregory Montoir (cyx@users.sourceforge.net)
* 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 <http://www.gnu.org/licenses/>.
*/ */
#include "file.h" #include "file.h"
#include "unpack.h" #include "fs.h"
#include "resource.h" #include "resource.h"
#include "unpack.h"
#include "util.h"
Resource::Resource(FileSystem *fs, ResourceType ver, Language lang) { Resource::Resource(FileSystem *fs, ResourceType ver, Language lang) {
memset(this, 0, sizeof(Resource)); memset(this, 0, sizeof(Resource));
_fs = fs;
_type = ver; _type = ver;
_lang = lang; _lang = lang;
_fs = fs; _isDemo = false;
_memBuf = (uint8_t *)malloc(256 * 224); _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) { if (!_memBuf) {
error("Unable to allocate temporary memory buffer"); error("Unable to allocate temporary memory buffer");
} }
@ -56,6 +50,25 @@ Resource::~Resource() {
} }
free(_sfxList); free(_sfxList);
free(_bankData); 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() { void Resource::clearLevelRes() {
@ -66,6 +79,7 @@ void Resource::clearLevelRes() {
free(_lev); _lev = 0; free(_lev); _lev = 0;
_levNum = -1; _levNum = -1;
free(_sgd); _sgd = 0; free(_sgd); _sgd = 0;
free(_bnq); _bnq = 0;
free(_ani); _ani = 0; free(_ani); _ani = 0;
free_OBJ(); free_OBJ();
} }
@ -119,7 +133,7 @@ void Resource::load_FIB(const char *fileName) {
error("I/O error when reading '%s'", _entryName); error("I/O error when reading '%s'", _entryName);
} }
} else { } 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) { void Resource::load_MAP_menu(const char *fileName, uint8_t *dstPtr) {
debug(DBG_RES, "Resource::load_MAP_menu('%s')", fileName); debug(DBG_RES, "Resource::load_MAP_menu('%s')", fileName);
static const int kMenuMapSize = 0x3800 * 4;
snprintf(_entryName, sizeof(_entryName), "%s.MAP", fileName); snprintf(_entryName, sizeof(_entryName), "%s.MAP", fileName);
File f; File f;
if (f.open(_entryName, "rb", _fs)) { if (f.open(_entryName, "rb", _fs)) {
if (f.size() != 0x3800 * 4) { if (f.read(dstPtr, kMenuMapSize) != kMenuMapSize) {
error("Wrong file size for '%s', %d", _entryName, f.size()); error("Failed to read '%s'", _entryName);
} }
f.read(dstPtr, 0x3800 * 4);
if (f.ioErr()) { if (f.ioErr()) {
error("I/O error when reading '%s'", _entryName); error("I/O error when reading '%s'", _entryName);
} }
} else { return;
error("Can't open '%s'", _entryName); } 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) { void Resource::load_PAL_menu(const char *fileName, uint8_t *dstPtr) {
debug(DBG_RES, "Resource::load_PAL_menu('%s')", fileName); debug(DBG_RES, "Resource::load_PAL_menu('%s')", fileName);
static const int kMenuPalSize = 768;
snprintf(_entryName, sizeof(_entryName), "%s.PAL", fileName); snprintf(_entryName, sizeof(_entryName), "%s.PAL", fileName);
File f; File f;
if (f.open(_entryName, "rb", _fs)) { if (f.open(_entryName, "rb", _fs)) {
if (f.size() != 768) { if (f.read(dstPtr, kMenuPalSize) != kMenuPalSize) {
error("Wrong file size for '%s', %d", _entryName, f.size()); error("Failed to read '%s'", _entryName);
} }
f.read(dstPtr, 768);
if (f.ioErr()) { if (f.ioErr()) {
error("I/O error when reading '%s'", _entryName); error("I/O error when reading '%s'", _entryName);
} }
} else { return;
error("Can't open '%s'", _entryName); } 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) { void Resource::load_SPR_OFF(const char *fileName, uint8_t *sprData) {
debug(DBG_RES, "Resource::load_SPR_OFF('%s')", fileName); debug(DBG_RES, "Resource::load_SPR_OFF('%s')", fileName);
snprintf(_entryName, sizeof(_entryName), "%s.OFF", fileName); snprintf(_entryName, sizeof(_entryName), "%s.OFF", fileName);
uint8_t *offData = 0;
File f; File f;
if (f.open(_entryName, "rb", _fs)) { if (f.open(_entryName, "rb", _fs)) {
int len = f.size(); const int len = f.size();
uint8_t *offData = (uint8_t *)malloc(len); offData = (uint8_t *)malloc(len);
if (!offData) { if (!offData) {
error("Unable to allocate sprite offsets"); error("Unable to allocate sprite offsets");
} }
@ -192,6 +247,10 @@ void Resource::load_SPR_OFF(const char *fileName, uint8_t *sprData) {
if (f.ioErr()) { if (f.ioErr()) {
error("I/O error when reading '%s'", _entryName); error("I/O error when reading '%s'", _entryName);
} }
} else if (_aba) {
offData = _aba->loadEntry(_entryName);
}
if (offData) {
const uint8_t *p = offData; const uint8_t *p = offData;
uint16_t pos; uint16_t pos;
while ((pos = READ_LE_UINT16(p)) != 0xFFFF) { while ((pos = READ_LE_UINT16(p)) != 0xFFFF) {
@ -205,9 +264,9 @@ void Resource::load_SPR_OFF(const char *fileName, uint8_t *sprData) {
p += 6; p += 6;
} }
free(offData); free(offData);
} else { return;
error("Can't open '%s'", _entryName);
} }
error("Cannot load '%s'", _entryName);
} }
void Resource::load_CINE() { void Resource::load_CINE() {
@ -243,8 +302,11 @@ void Resource::load_CINE() {
if (f.ioErr()) { if (f.ioErr()) {
error("I/O error when reading '%s'", _entryName); error("I/O error when reading '%s'", _entryName);
} }
} else { } else if (_aba) {
error("Can't open '%s'", _entryName); _cine_off = _aba->loadEntry(_entryName);
}
if (!_cine_off) {
error("Cannot load '%s'", _entryName);
} }
} }
if (_cine_txt == 0) { if (_cine_txt == 0) {
@ -260,8 +322,11 @@ void Resource::load_CINE() {
if (f.ioErr()) { if (f.ioErr()) {
error("I/O error when reading '%s'", _entryName); error("I/O error when reading '%s'", _entryName);
} }
} else { } else if (_aba) {
error("Can't open '%s'", _entryName); _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); snprintf(_entryName, sizeof(_entryName), "%s.SGD", objName);
loadStub = &Resource::load_SGD; loadStub = &Resource::load_SGD;
break; break;
case OT_BNQ:
snprintf(_entryName, sizeof(_entryName), "%s.BNQ", objName);
loadStub = &Resource::load_BNQ;
break;
case OT_SPM: case OT_SPM:
snprintf(_entryName, sizeof(_entryName), "%s.SPM", objName); snprintf(_entryName, sizeof(_entryName), "%s.SPM", objName);
loadStub = &Resource::load_SPM; 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); error("I/O error when reading '%s'", _entryName);
} }
} else { } 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; int tmpOffset = 0;
_numObjectNodes = 230; _numObjectNodes = 230;
for (int i = 0; i < _numObjectNodes; ++i) { 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; offsets[_numObjectNodes] = size;
int numObjectsCount = 0; int numObjectsCount = 0;
@ -724,23 +856,23 @@ void Resource::decodeOBJ(const uint8_t *tmp, int size) {
error("Unable to allocate ObjectNode num=%d", i); error("Unable to allocate ObjectNode num=%d", i);
} }
const uint8_t *objData = tmp + offsets[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->num_objects = objectsCount[iObj];
on->objects = (Object *)malloc(sizeof(Object) * on->num_objects); on->objects = (Object *)malloc(sizeof(Object) * on->num_objects);
for (int j = 0; j < on->num_objects; ++j) { for (int j = 0; j < on->num_objects; ++j) {
Object *obj = &on->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->dx = *objData++;
obj->dy = *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->opcode2 = *objData++;
obj->opcode1 = *objData++; obj->opcode1 = *objData++;
obj->flags = *objData++; obj->flags = *objData++;
obj->opcode3 = *objData++; obj->opcode3 = *objData++;
obj->init_obj_number = READ_BE_UINT16(objData); objData += 2; obj->init_obj_number = _readUint16(objData); objData += 2;
obj->opcode_arg1 = READ_BE_UINT16(objData); objData += 2; obj->opcode_arg1 = _readUint16(objData); objData += 2;
obj->opcode_arg2 = READ_BE_UINT16(objData); objData += 2; obj->opcode_arg2 = _readUint16(objData); objData += 2;
obj->opcode_arg3 = READ_BE_UINT16(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); 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; ++iObj;
@ -753,13 +885,20 @@ void Resource::decodeOBJ(const uint8_t *tmp, int size) {
void Resource::load_PGE(File *f) { void Resource::load_PGE(File *f) {
debug(DBG_RES, "Resource::load_PGE()"); debug(DBG_RES, "Resource::load_PGE()");
int len = f->size() - 2;
_pgeNum = f->readUint16LE();
if (_type == kResourceTypeAmiga) { 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)); 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) { for (uint16_t i = 0; i < _pgeNum; ++i) {
InitPGE *pge = &_pgeInit[i]; InitPGE *pge = &_pgeInit[i];
pge->type = f->readUint16LE(); pge->type = f->readUint16LE();
@ -784,56 +923,46 @@ void Resource::load_PGE(File *f) {
f->readByte(); f->readByte();
pge->text_num = f->readUint16LE(); pge->text_num = f->readUint16LE();
} }
if (_type == kResourceTypeAmiga) { }
for (uint16_t i = 0; i < _pgeNum; ++i) {
InitPGE *pge = &_pgeInit[i]; void Resource::decodePGE(const uint8_t *p, int size) {
SWAP_UINT16((uint16_t *)&pge->type); _pgeNum = _readUint16(p); p += 2;
SWAP_UINT16((uint16_t *)&pge->pos_x); memset(_pgeInit, 0, sizeof(_pgeInit));
SWAP_UINT16((uint16_t *)&pge->pos_y); debug(DBG_RES, "len=%d _pgeNum=%d", size, _pgeNum);
SWAP_UINT16((uint16_t *)&pge->obj_node_number); for (uint16_t i = 0; i < _pgeNum; ++i) {
SWAP_UINT16((uint16_t *)&pge->life); InitPGE *pge = &_pgeInit[i];
for (int lc = 0; lc < 4; ++lc) { pge->type = _readUint16(p); p += 2;
SWAP_UINT16((uint16_t *)&pge->counter_values[lc]); pge->pos_x = _readUint16(p); p += 2;
} pge->pos_y = _readUint16(p); p += 2;
SWAP_UINT16((uint16_t *)&pge->text_num); 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) { void Resource::load_ANI(File *f) {
debug(DBG_RES, "Resource::load_ANI()"); debug(DBG_RES, "Resource::load_ANI()");
int size = f->size() - 2; const int size = f->size();
_ani = (uint8_t *)malloc(size); _ani = (uint8_t *)malloc(size);
if (!_ani) { if (!_ani) {
error("Unable to allocate ANI buffer"); error("Unable to allocate ANI buffer");
} else { } else {
uint16_t count = f->readUint16LE();
f->read(_ani, size); 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<uint8_t>(_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<uint8_t>(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<uint8_t>(p[6 + j * 4], p[6 + j * 4 + 1]);
}
}
}
} }
} }
@ -846,13 +975,6 @@ void Resource::load_TBN(File *f) {
} else { } else {
f->read(_tbn, len); 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<uint8_t>(_tbn[i], _tbn[i + 1]);
}
}
} }
void Resource::load_CMD(File *pf) { void Resource::load_CMD(File *pf) {
@ -1011,6 +1133,17 @@ void Resource::load_LEV(File *f) {
void Resource::load_SGD(File *f) { void Resource::load_SGD(File *f) {
const int len = f->size(); 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); f->seek(len - 4);
int size = f->readUint32BE(); int size = f->readUint32BE();
f->seek(0); f->seek(0);
@ -1029,6 +1162,16 @@ void Resource::load_SGD(File *f) {
free(tmp); 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) { void Resource::load_SPM(File *f) {
static const int kPersoDatSize = 178647; static const int kPersoDatSize = 178647;
const int len = f->size(); const int len = f->size();
@ -1072,19 +1215,23 @@ void Resource::clearBankData() {
int Resource::getBankDataSize(uint16_t num) { int Resource::getBankDataSize(uint16_t num) {
int len = READ_BE_UINT16(_mbk + num * 6 + 4); int len = READ_BE_UINT16(_mbk + num * 6 + 4);
int size = 0;
switch (_type) { switch (_type) {
case kResourceTypeAmiga: case kResourceTypeAmiga:
if (len & 0x8000) { if (len & 0x8000) {
len = -(int16_t)len; len = -(int16_t)len;
} }
size = len * 32;
break; break;
case kResourceTypePC: case kResourceTypeDOS:
size = (len & 0x7FFF) * 32; if (len & 0x8000) {
if (_mbk == _bnq) { // demo .bnq use signed int
len = -(int16_t)len;
break;
}
len &= 0x7FFF;
}
break; break;
} }
return size; return len * 32;
} }
uint8_t *Resource::findBankData(uint16_t num) { 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) { uint8_t *Resource::loadBankData(uint16_t num) {
const uint8_t *ptr = _mbk + num * 6; const uint8_t *ptr = _mbk + num * 6;
int dataOffset = READ_BE_UINT32(ptr); int dataOffset = READ_BE_UINT32(ptr);
if (_type == kResourceTypePC) { if (_type == kResourceTypeDOS) {
// first byte of the data buffer corresponds // first byte of the data buffer corresponds
// to the total count of entries // to the total count of entries
dataOffset &= 0xFFFF; dataOffset &= 0xFFFF;

View File

@ -1,24 +1,14 @@
/* REminiscence - Flashback interpreter
* Copyright (C) 2005-2015 Gregory Montoir /*
* * REminiscence - Flashback interpreter
* This program is free software: you can redistribute it and/or modify * Copyright (C) 2005-2015 Gregory Montoir (cyx@users.sourceforge.net)
* 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 <http://www.gnu.org/licenses/>.
*/ */
#ifndef RESOURCE_H__ #ifndef RESOURCE_H__
#define RESOURCE_H__ #define RESOURCE_H__
#include "intern.h" #include "intern.h"
#include "resource_aba.h"
struct File; struct File;
struct FileSystem; struct FileSystem;
@ -93,11 +83,13 @@ struct Resource {
OT_SPL, OT_SPL,
OT_LEV, OT_LEV,
OT_SGD, OT_SGD,
OT_BNQ,
OT_SPM OT_SPM
}; };
enum { enum {
NUM_SFXS = 66, NUM_SFXS = 66,
NUM_BANK_BUFFERS = 50,
NUM_SPRITES = 1287 NUM_SPRITES = 1287
}; };
@ -108,6 +100,10 @@ struct Resource {
FileSystem *_fs; FileSystem *_fs;
ResourceType _type; ResourceType _type;
Language _lang; Language _lang;
bool _isDemo;
ResourceAba *_aba;
uint16_t (*_readUint16)(const void *);
uint32_t (*_readUint32)(const void *);
bool _hasSeqData; bool _hasSeqData;
char _entryName[32]; char _entryName[32];
uint8_t *_fnt; uint8_t *_fnt;
@ -131,6 +127,7 @@ struct Resource {
uint8_t *_lev; uint8_t *_lev;
int _levNum; int _levNum;
uint8_t *_sgd; uint8_t *_sgd;
uint8_t *_bnq;
uint16_t _numObjectNodes; uint16_t _numObjectNodes;
ObjectNode *_objectNodesMap[255]; ObjectNode *_objectNodesMap[255];
uint8_t *_memBuf; uint8_t *_memBuf;
@ -147,17 +144,24 @@ struct Resource {
uint8_t *_bankData; uint8_t *_bankData;
uint8_t *_bankDataHead; uint8_t *_bankDataHead;
uint8_t *_bankDataTail; uint8_t *_bankDataTail;
BankSlot _bankBuffers[50]; BankSlot _bankBuffers[NUM_BANK_BUFFERS];
int _bankBuffersCount; int _bankBuffersCount;
Resource(FileSystem *fs, ResourceType type, Language lang); Resource(FileSystem *fs, ResourceType type, Language lang);
~Resource(); ~Resource();
void init();
void fini();
bool isDOS() const { return _type == kResourceTypeDOS; }
bool isAmiga() const { return _type == kResourceTypeAmiga; }
void clearLevelRes(); void clearLevelRes();
void load_FIB(const char *fileName); void load_FIB(const char *fileName);
void load_SPL_demo(); void load_SPL_demo();
void load_MAP_menu(const char *fileName, uint8_t *dstPtr); void load_MAP_menu(const char *fileName, uint8_t *dstPtr);
void load_PAL_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_SPR_OFF(const char *fileName, uint8_t *sprData);
void load_CINE(); void load_CINE();
void load_TEXT(); void load_TEXT();
@ -178,6 +182,7 @@ struct Resource {
void load_OBC(File *pf); void load_OBC(File *pf);
void decodeOBJ(const uint8_t *, int); void decodeOBJ(const uint8_t *, int);
void load_PGE(File *pf); void load_PGE(File *pf);
void decodePGE(const uint8_t *, int);
void load_ANI(File *pf); void load_ANI(File *pf);
void load_TBN(File *pf); void load_TBN(File *pf);
void load_CMD(File *pf); void load_CMD(File *pf);
@ -187,10 +192,14 @@ struct Resource {
void load_SPL(File *pf); void load_SPL(File *pf);
void load_LEV(File *pf); void load_LEV(File *pf);
void load_SGD(File *pf); void load_SGD(File *pf);
void load_BNQ(File *pf);
void load_SPM(File *f); void load_SPM(File *f);
const uint8_t *getAniData(int num) const { const uint8_t *getAniData(int num) const {
const int offset = READ_LE_UINT16(_ani + num * 2); const int offset = _readUint16(_ani + 2 + num * 2);
return _ani + offset; return _ani + 2 + offset;
}
const uint8_t *getTextString(int num) {
return _tbn + _readUint16(_tbn + num * 2);
} }
const uint8_t *getGameString(int num) { const uint8_t *getGameString(int num) {
return _stringsTable + READ_LE_UINT16(_stringsTable + num * 2); return _stringsTable + READ_LE_UINT16(_stringsTable + num * 2);

85
resource_aba.cpp Normal file
View File

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

34
resource_aba.h Normal file
View File

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

14
rs.cfg Normal file
View File

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

View File

@ -1,22 +1,11 @@
/* REminiscence - Flashback interpreter
* Copyright (C) 2005-2015 Gregory Montoir /*
* * REminiscence - Flashback interpreter
* This program is free software: you can redistribute it and/or modify * Copyright (C) 2005-2015 Gregory Montoir (cyx@users.sourceforge.net)
* 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 <http://www.gnu.org/licenses/>.
*/ */
#include "scaler.h" #include "scaler.h"
#include "util.h"
static void point1x(uint16_t *dst, int dstPitch, const uint16_t *src, int srcPitch, int w, int h) { static void point1x(uint16_t *dst, int dstPitch, const uint16_t *src, int srcPitch, int w, int h) {
dstPitch >>= 1; dstPitch >>= 1;

View File

@ -1,18 +1,7 @@
/* REminiscence - Flashback interpreter
* Copyright (C) 2005-2015 Gregory Montoir /*
* * REminiscence - Flashback interpreter
* This program is free software: you can redistribute it and/or modify * Copyright (C) 2005-2015 Gregory Montoir (cyx@users.sourceforge.net)
* 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 <http://www.gnu.org/licenses/>.
*/ */
#ifndef SCALER_H__ #ifndef SCALER_H__

View File

@ -1,18 +1,7 @@
/* REminiscence - Flashback interpreter
* Copyright (C) 2005-2015 Gregory Montoir /*
* * REminiscence - Flashback interpreter
* This program is free software: you can redistribute it and/or modify * Copyright (C) 2005-2015 Gregory Montoir (cyx@users.sourceforge.net)
* 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 <http://www.gnu.org/licenses/>.
*/ */
#include "file.h" #include "file.h"
@ -20,7 +9,7 @@
#include "mixer.h" #include "mixer.h"
#include "seq_player.h" #include "seq_player.h"
#include "systemstub.h" #include "systemstub.h"
#include "util.h"
bool SeqDemuxer::open(File *f) { bool SeqDemuxer::open(File *f) {
_f = f; _f = f;

View File

@ -1,18 +1,7 @@
/* REminiscence - Flashback interpreter
* Copyright (C) 2005-2015 Gregory Montoir /*
* * REminiscence - Flashback interpreter
* This program is free software: you can redistribute it and/or modify * Copyright (C) 2005-2015 Gregory Montoir (cyx@users.sourceforge.net)
* 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 <http://www.gnu.org/licenses/>.
*/ */
#ifndef SEQ_PLAYER_H__ #ifndef SEQ_PLAYER_H__

View File

@ -1,23 +1,12 @@
/* REminiscence - Flashback interpreter
* Copyright (C) 2005-2015 Gregory Montoir /*
* * REminiscence - Flashback interpreter
* This program is free software: you can redistribute it and/or modify * Copyright (C) 2005-2015 Gregory Montoir (cyx@users.sourceforge.net)
* 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 <http://www.gnu.org/licenses/>.
*/ */
#include "mixer.h" #include "mixer.h"
#include "sfx_player.h" #include "sfx_player.h"
#include "util.h"
SfxPlayer::SfxPlayer(Mixer *mixer) SfxPlayer::SfxPlayer(Mixer *mixer)
: _mod(0), _playing(false), _mix(mixer) { : _mod(0), _playing(false), _mix(mixer) {
@ -137,7 +126,7 @@ void SfxPlayer::mixSamples(int8_t *buf, int samplesLen) {
curLen = 0; curLen = 0;
} }
while (count--) { 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); Mixer::addclamp(*mixbuf++, out * si->vol / 64);
pos += deltaPos; pos += deltaPos;
} }

View File

@ -1,18 +1,7 @@
/* REminiscence - Flashback interpreter
* Copyright (C) 2005-2015 Gregory Montoir /*
* * REminiscence - Flashback interpreter
* This program is free software: you can redistribute it and/or modify * Copyright (C) 2005-2015 Gregory Montoir (cyx@users.sourceforge.net)
* 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 <http://www.gnu.org/licenses/>.
*/ */
#ifndef SFX_PLAYER_H__ #ifndef SFX_PLAYER_H__

View File

@ -1,18 +1,7 @@
/* REminiscence - Flashback interpreter
* Copyright (C) 2005-2015 Gregory Montoir /*
* * REminiscence - Flashback interpreter
* This program is free software: you can redistribute it and/or modify * Copyright (C) 2005-2015 Gregory Montoir (cyx@users.sourceforge.net)
* 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 <http://www.gnu.org/licenses/>.
*/ */
#include "game.h" #include "game.h"
@ -72,7 +61,10 @@ const char *Cutscene::_namesTable[] = {
"LOGOS", "LOGOS",
"OVER", "OVER",
"SCORE", "SCORE",
"INTRO2" "INTRO2",
"SERRURE",
"HOLOCUBE",
"CHUTE2"
}; };
const uint16_t Cutscene::_offsetsTable[] = { const uint16_t Cutscene::_offsetsTable[] = {
@ -94,6 +86,12 @@ const uint16_t Cutscene::_offsetsTable[] = {
0xFFFF, 0x0000 0xFFFF, 0x0000
}; };
const uint8_t Cutscene::_amigaDemoOffsetsTable[] = {
1, 32, 0, /* HOLOCUBE */
6, 33, 0, /* CHUTE2 */
255
};
const uint16_t Cutscene::_cosTable[] = { const uint16_t Cutscene::_cosTable[] = {
0x0100, 0x00FF, 0x00FF, 0x00FF, 0x00FF, 0x00FF, 0x00FE, 0x00FE, 0x0100, 0x00FF, 0x00FF, 0x00FF, 0x00FF, 0x00FF, 0x00FE, 0x00FE,
0x00FD, 0x00FC, 0x00FC, 0x00FB, 0x00FA, 0x00F9, 0x00F8, 0x00F7, 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 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[] = { const uint16_t ModPlayer::_periodTable[] = {
856, 808, 762, 720, 678, 640, 604, 570, 538, 508, 480, 453, // C-1 to B-1 Finetune 0 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 428, 404, 381, 360, 339, 320, 302, 285, 269, 254, 240, 226, // C-2 to B-2 Finetune 0

View File

@ -1,18 +1,7 @@
/* REminiscence - Flashback interpreter
* Copyright (C) 2005-2015 Gregory Montoir /*
* * REminiscence - Flashback interpreter
* This program is free software: you can redistribute it and/or modify * Copyright (C) 2005-2015 Gregory Montoir (cyx@users.sourceforge.net)
* 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 <http://www.gnu.org/licenses/>.
*/ */
#ifndef SYSTEMSTUB_H__ #ifndef SYSTEMSTUB_H__
@ -62,9 +51,10 @@ struct SystemStub {
virtual ~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 destroy() = 0;
virtual void setScreenSize(int w, int h) = 0;
virtual void setPalette(const uint8_t *pal, int n) = 0; virtual void setPalette(const uint8_t *pal, int n) = 0;
virtual void setPaletteEntry(int i, const Color *c) = 0; virtual void setPaletteEntry(int i, const Color *c) = 0;
virtual void getPaletteEntry(int i, Color *c) = 0; virtual void getPaletteEntry(int i, Color *c) = 0;

View File

@ -1,24 +1,13 @@
/* REminiscence - Flashback interpreter
* Copyright (C) 2005-2015 Gregory Montoir /*
* * REminiscence - Flashback interpreter
* This program is free software: you can redistribute it and/or modify * Copyright (C) 2005-2015 Gregory Montoir (cyx@users.sourceforge.net)
* 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 <http://www.gnu.org/licenses/>.
*/ */
#include <SDL.h> #include <SDL.h>
#include "scaler.h" #include "scaler.h"
#include "systemstub.h" #include "systemstub.h"
#include "util.h"
struct SystemStub_SDL : SystemStub { struct SystemStub_SDL : SystemStub {
enum { enum {
@ -41,10 +30,12 @@ struct SystemStub_SDL : SystemStub {
bool _fadeOnUpdateScreen; bool _fadeOnUpdateScreen;
void (*_audioCbProc)(void *, int8_t *, int); void (*_audioCbProc)(void *, int8_t *, int);
void *_audioCbData; void *_audioCbData;
int _screenshot;
virtual ~SystemStub_SDL() {} 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 destroy();
virtual void setScreenSize(int w, int h);
virtual void setPalette(const uint8_t *pal, int n); virtual void setPalette(const uint8_t *pal, int n);
virtual void setPaletteEntry(int i, const Color *c); virtual void setPaletteEntry(int i, const Color *c);
virtual void getPaletteEntry(int i, Color *c); virtual void getPaletteEntry(int i, Color *c);
@ -62,8 +53,6 @@ struct SystemStub_SDL : SystemStub {
virtual void unlockAudio(); virtual void unlockAudio();
void processEvent(const SDL_Event &ev, bool &paused); void processEvent(const SDL_Event &ev, bool &paused);
void updateScreen_GL(int shakeOffset);
void updateScreen_SW(int shakeOffset);
void prepareGfxMode(); void prepareGfxMode();
void cleanupGfxMode(); void cleanupGfxMode();
void switchGfxMode(bool fullscreen, uint8_t scaler); void switchGfxMode(bool fullscreen, uint8_t scaler);
@ -76,30 +65,23 @@ SystemStub *SystemStub_SDL_create() {
return new SystemStub_SDL(); 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_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_JOYSTICK);
SDL_ShowCursor(SDL_DISABLE); SDL_ShowCursor(SDL_DISABLE);
SDL_WM_SetCaption(title, NULL); SDL_WM_SetCaption(title, NULL);
memset(&_pi, 0, sizeof(_pi)); memset(&_pi, 0, sizeof(_pi));
_screenW = w; _screenBuffer = 0;
_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);
_fadeScreenBuffer = 0; _fadeScreenBuffer = 0;
_fadeOnUpdateScreen = false; _fadeOnUpdateScreen = false;
_fullscreen = false; _fullscreen = fullscreen;
_currentScaler = SCALER_SCALE_3X; _currentScaler = scaler;
memset(_pal, 0, sizeof(_pal)); memset(_pal, 0, sizeof(_pal));
prepareGfxMode(); setScreenSize(w, h);
_joystick = NULL; _joystick = NULL;
if (SDL_NumJoysticks() > 0) { if (SDL_NumJoysticks() > 0) {
_joystick = SDL_JoystickOpen(0); _joystick = SDL_JoystickOpen(0);
} }
_screenshot = 1;
} }
void SystemStub_SDL::destroy() { void SystemStub_SDL::destroy() {
@ -110,6 +92,25 @@ void SystemStub_SDL::destroy() {
SDL_Quit(); 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) { void SystemStub_SDL::setPalette(const uint8_t *pal, int n) {
assert(n <= 256); assert(n <= 256);
for (int i = 0; i < n; ++i) { 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) { void SystemStub_SDL::setPaletteEntry(int i, const Color *c) {
uint8_t r = (c->r << 2) | (c->r & 3); _pal[i] = SDL_MapRGB(_screenSurface->format, c->r, c->g, c->b);
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);
} }
void SystemStub_SDL::getPaletteEntry(int i, Color *c) { void SystemStub_SDL::getPaletteEntry(int i, Color *c) {
SDL_GetRGB(_pal[i], _screenSurface->format, &c->r, &c->g, &c->b); 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) { void SystemStub_SDL::setOverscanColor(int i) {
@ -278,8 +273,8 @@ void SystemStub_SDL::updateScreen(int shakeOffset) {
} }
void SystemStub_SDL::processEvents() { void SystemStub_SDL::processEvents() {
bool paused = false;
while (true) { while (true) {
bool paused = false;
SDL_Event ev; SDL_Event ev;
while (SDL_PollEvent(&ev)) { while (SDL_PollEvent(&ev)) {
processEvent(ev, paused); processEvent(ev, paused);
@ -431,6 +426,12 @@ void SystemStub_SDL::processEvent(const SDL_Event &ev, bool &paused) {
if (_currentScaler > 0) { if (_currentScaler > 0) {
switchGfxMode(_fullscreen, s); 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; break;
} else if (ev.key.keysym.mod & KMOD_CTRL) { } else if (ev.key.keysym.mod & KMOD_CTRL) {

View File

@ -1,18 +1,7 @@
/* REminiscence - Flashback interpreter
* Copyright (C) 2005-2015 Gregory Montoir /*
* * REminiscence - Flashback interpreter
* This program is free software: you can redistribute it and/or modify * Copyright (C) 2005-2015 Gregory Montoir (cyx@users.sourceforge.net)
* 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 <http://www.gnu.org/licenses/>.
*/ */
#include "unpack.h" #include "unpack.h"

View File

@ -1,18 +1,7 @@
/* REminiscence - Flashback interpreter
* Copyright (C) 2005-2015 Gregory Montoir /*
* * REminiscence - Flashback interpreter
* This program is free software: you can redistribute it and/or modify * Copyright (C) 2005-2015 Gregory Montoir (cyx@users.sourceforge.net)
* 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 <http://www.gnu.org/licenses/>.
*/ */
#ifndef UNPACK_H__ #ifndef UNPACK_H__

View File

@ -1,18 +1,7 @@
/* REminiscence - Flashback interpreter
* Copyright (C) 2005-2015 Gregory Montoir /*
* * REminiscence - Flashback interpreter
* This program is free software: you can redistribute it and/or modify * Copyright (C) 2005-2015 Gregory Montoir (cyx@users.sourceforge.net)
* 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 <http://www.gnu.org/licenses/>.
*/ */
#ifdef _WIN32 #ifdef _WIN32

19
util.h
View File

@ -1,18 +1,7 @@
/* REminiscence - Flashback interpreter
* Copyright (C) 2005-2015 Gregory Montoir /*
* * REminiscence - Flashback interpreter
* This program is free software: you can redistribute it and/or modify * Copyright (C) 2005-2015 Gregory Montoir (cyx@users.sourceforge.net)
* 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 <http://www.gnu.org/licenses/>.
*/ */
#ifndef UTIL_H__ #ifndef UTIL_H__

331
video.cpp
View File

@ -1,38 +1,25 @@
/* REminiscence - Flashback interpreter
* Copyright (C) 2005-2015 Gregory Montoir /*
* * REminiscence - Flashback interpreter
* This program is free software: you can redistribute it and/or modify * Copyright (C) 2005-2015 Gregory Montoir (cyx@users.sourceforge.net)
* 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 <http://www.gnu.org/licenses/>.
*/ */
#include "resource.h" #include "resource.h"
#include "systemstub.h" #include "systemstub.h"
#include "unpack.h" #include "unpack.h"
#include "util.h"
#include "video.h" #include "video.h"
Video::Video(Resource *res, SystemStub *stub) Video::Video(Resource *res, SystemStub *stub)
: _res(res), _stub(stub) { : _res(res), _stub(stub) {
_frontLayer = (uint8_t *)malloc(GAMESCREEN_W * GAMESCREEN_H); _w = GAMESCREEN_W;
memset(_frontLayer, 0, GAMESCREEN_W * GAMESCREEN_H); _h = GAMESCREEN_H;
_backLayer = (uint8_t *)malloc(GAMESCREEN_W * GAMESCREEN_H); _layerSize = _w * _h;
memset(_backLayer, 0, GAMESCREEN_W * GAMESCREEN_H); _frontLayer = (uint8_t *)calloc(1, _layerSize);
_tempLayer = (uint8_t *)malloc(GAMESCREEN_W * GAMESCREEN_H); _backLayer = (uint8_t *)calloc(1,_layerSize);
memset(_tempLayer, 0, GAMESCREEN_W * GAMESCREEN_H); _tempLayer = (uint8_t *)calloc(1, _layerSize);
_tempLayer2 = (uint8_t *)malloc(GAMESCREEN_W * GAMESCREEN_H); _tempLayer2 = (uint8_t *)calloc(1, _layerSize);
memset(_tempLayer2, 0, GAMESCREEN_W * GAMESCREEN_H); _screenBlocks = (uint8_t *)calloc(1, (_w / SCREENBLOCK_W) * (_h / SCREENBLOCK_H));
_screenBlocks = (uint8_t *)malloc((GAMESCREEN_W / SCREENBLOCK_W) * (GAMESCREEN_H / SCREENBLOCK_H));
memset(_screenBlocks, 0, (GAMESCREEN_W / SCREENBLOCK_W) * (GAMESCREEN_H / SCREENBLOCK_H));
_fullRefresh = true; _fullRefresh = true;
_shakeOffset = 0; _shakeOffset = 0;
_charFrontColor = 0; _charFrontColor = 0;
@ -50,15 +37,15 @@ Video::~Video() {
void Video::markBlockAsDirty(int16_t x, int16_t y, uint16_t w, uint16_t h) { 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); 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 bx1 = x / SCREENBLOCK_W;
int by1 = y / SCREENBLOCK_H; int by1 = y / SCREENBLOCK_H;
int bx2 = (x + w - 1) / SCREENBLOCK_W; int bx2 = (x + w - 1) / SCREENBLOCK_W;
int by2 = (y + h - 1) / SCREENBLOCK_H; 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 (; by1 <= by2; ++by1) {
for (int i = bx1; i <= bx2; ++i) { 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()"); debug(DBG_VIDEO, "Video::updateScreen()");
// _fullRefresh = true; // _fullRefresh = true;
if (_fullRefresh) { 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); _stub->updateScreen(_shakeOffset);
_fullRefresh = false; _fullRefresh = false;
} else { } else {
int i, j; int i, j;
int count = 0; int count = 0;
uint8_t *p = _screenBlocks; 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; 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) { if (p[i] != 0) {
--p[i]; --p[i];
++nh; ++nh;
@ -92,7 +79,7 @@ void Video::updateScreen() {
_stub->copyRect(x, j * SCREENBLOCK_H, nh * SCREENBLOCK_W, SCREENBLOCK_H, _frontLayer, 256); _stub->copyRect(x, j * SCREENBLOCK_H, nh * SCREENBLOCK_W, SCREENBLOCK_H, _frontLayer, 256);
++count; ++count;
} }
p += GAMESCREEN_W / SCREENBLOCK_W; p += _w / SCREENBLOCK_W;
} }
if (count != 0) { if (count != 0) {
_stub->updateScreen(_shakeOffset); _stub->updateScreen(_shakeOffset);
@ -107,13 +94,16 @@ void Video::updateScreen() {
void Video::fullRefresh() { void Video::fullRefresh() {
debug(DBG_VIDEO, "Video::fullRefresh()"); debug(DBG_VIDEO, "Video::fullRefresh()");
_fullRefresh = true; _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() { void Video::fadeOut() {
debug(DBG_VIDEO, "Video::fadeOut()"); debug(DBG_VIDEO, "Video::fadeOut()");
_stub->fadeScreen(); if (g_options.fade_out_palette) {
// fadeOutPalette(); fadeOutPalette();
} else {
_stub->fadeScreen();
}
} }
void Video::fadeOutPalette() { void Video::fadeOutPalette() {
@ -134,15 +124,7 @@ void Video::fadeOutPalette() {
void Video::setPaletteColorBE(int num, int offset) { void Video::setPaletteColorBE(int num, int offset) {
const int color = READ_BE_UINT16(_res->_pal + offset * 2); const int color = READ_BE_UINT16(_res->_pal + offset * 2);
Color c; Color c = AMIGA_convertColor(color, true);
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;
}
_stub->setPaletteEntry(num, &c); _stub->setPaletteEntry(num, &c);
} }
@ -151,15 +133,7 @@ void Video::setPaletteSlotBE(int palSlot, int palNum) {
const uint8_t *p = _res->_pal + palNum * 0x20; const uint8_t *p = _res->_pal + palNum * 0x20;
for (int i = 0; i < 16; ++i) { for (int i = 0; i < 16; ++i) {
const int color = READ_BE_UINT16(p); p += 2; const int color = READ_BE_UINT16(p); p += 2;
Color c; Color c = AMIGA_convertColor(color, true);
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;
}
_stub->setPaletteEntry(palSlot * 0x10 + i, &c); _stub->setPaletteEntry(palSlot * 0x10 + i, &c);
} }
} }
@ -168,10 +142,7 @@ void Video::setPaletteSlotLE(int palSlot, const uint8_t *palData) {
debug(DBG_VIDEO, "Video::setPaletteSlotLE()"); debug(DBG_VIDEO, "Video::setPaletteSlotLE()");
for (int i = 0; i < 16; ++i) { for (int i = 0; i < 16; ++i) {
uint16_t color = READ_LE_UINT16(palData); palData += 2; uint16_t color = READ_LE_UINT16(palData); palData += 2;
Color c; Color c = AMIGA_convertColor(color);
c.b = (color & 0x00F) << 2;
c.g = (color & 0x0F0) >> 2;
c.r = (color & 0xF00) >> 6;
_stub->setPaletteEntry(palSlot * 0x10 + i, &c); _stub->setPaletteEntry(palSlot * 0x10 + i, &c);
} }
} }
@ -186,13 +157,22 @@ void Video::setPalette0xF() {
const uint8_t *p = _palSlot0xF; const uint8_t *p = _palSlot0xF;
for (int i = 0; i < 16; ++i) { for (int i = 0; i < 16; ++i) {
Color c; Color c;
c.r = *p++ >> 2; c.r = *p++;
c.g = *p++ >> 2; c.g = *p++;
c.b = *p++ >> 2; c.b = *p++;
_stub->setPaletteEntry(0xF0 + i, &c); _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) { static void PC_decodeMapHelper(int sz, const uint8_t *src, uint8_t *dst) {
const uint8_t *end = src + sz; const uint8_t *end = src + sz;
while (src < end) { 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() { 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; const int planarSize = w * 2 * h;
for (int y = 0; y < h; ++y) { for (int y = 0; y < h; ++y) {
for (int x = 0; x < w; ++x) { for (int x = 0; x < w; ++x) {
for (int i = 0; i < 16; ++i) { for (int i = 0; i < 16; ++i) {
int color = 0; int color = 0;
const int mask = 1 << (15 - i); 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) { if (READ_BE_UINT16(src + bit * planarSize) & mask) {
color |= 1 << bit; 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) { static void AMIGA_planar8(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) {
assert(w == 8); assert(w == 8);
for (int y = 0; y < h; ++y) { for (int y = 0; y < h; ++y) {
for (int i = 0; i < 8; ++i) { 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) { static void AMIGA_planar_mask(uint8_t *dst, int x0, int y0, int w, int h, uint8_t *src, uint8_t *mask, int size) {
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) {
dst += y0 * 256 + x0; dst += y0 * 256 + x0;
for (int y = 0; y < h; ++y) { for (int y = 0; y < h; ++y) {
for (int x = 0; x < w * 2; ++x) { 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; int code = READ_BE_UINT16(src) & 0x7FFF; src += 2;
const uint8_t *end = src + code; const uint8_t *end = src + code;
do { do {
@ -417,7 +359,30 @@ static void AMIGA_decodeRLE(uint8_t *dst, const uint8_t *src) {
assert(src == end); 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; int num = -1;
uint8_t buf[256 * 32]; uint8_t buf[256 * 32];
int count = READ_BE_UINT16(src) - 1; src += 2; 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; int d1 = READ_BE_UINT16(src); src += 2;
if (d2 != 0xFFFF) { if (d2 != 0xFFFF) {
d2 &= ~(1 << 15); 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) { if (offset < 0) {
const uint8_t *ptr = data - offset; const uint8_t *ptr = data - offset;
const int size = READ_BE_UINT16(ptr); ptr += 2; 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; num = d2;
const int size = READ_BE_UINT16(data + offset) & 0x7FFF; const int size = READ_BE_UINT16(data + offset) & 0x7FFF;
assert(size <= (int)sizeof(buf)); assert(size <= (int)sizeof(buf));
AMIGA_decodeRLE(buf, data + offset); AMIGA_decodeRle(buf, data + offset);
} }
} }
} }
const int w = (buf[0] + 1) >> 1; const int w = (buf[0] + 1) >> 1;
const int h = buf[1] + 1; const int h = buf[1] + 1;
const int planarSize = READ_BE_UINT16(buf + 2); 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); } 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]; static uint8_t buf[32];
a2 += 24; a2 += 24;
@ -465,7 +434,7 @@ static const uint8_t *AMIGA_mirrorY(const uint8_t *a2) {
return buf; 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]; static uint8_t buf[32];
for (int i = 0; i < 32; ++i) { for (int i = 0; i < 32; ++i) {
@ -480,7 +449,13 @@ static const uint8_t *AMIGA_mirrorX(const uint8_t *a2) {
return buf; 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 y = 0; y < 8; ++y) {
for (int i = 0; i < 8; ++i) { for (int i = 0; i < 8; ++i) {
const int mask = 1 << (7 - 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) { if (offset10 != 0) {
const uint8_t *a0 = src + offset10; const uint8_t *a0 = src + offset10;
for (int y = 0; y < 224; y += 8) { for (int y = 0; y < 224; y += 8) {
for (int x = 0; x < 256; x += 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; const int d0 = d3 & 0x7FF;
if (d0 != 0) { if (d0 != 0) {
const uint8_t *a2 = a5 + d0 * 32; const uint8_t *a2 = a5 + d0 * 32;
if ((d3 & (1 << 12)) != 0) { const bool yflip = (d3 & (1 << 12)) != 0;
a2 = AMIGA_mirrorY(a2); const bool xflip = (d3 & (1 << 11)) != 0;
}
if ((d3 & (1 << 11)) != 0) {
a2 = AMIGA_mirrorX(a2);
}
int mask = 0; int mask = 0;
if ((d3 < (1 << 15)) == 0) { if ((d3 < (1 << 15)) == 0) {
mask = 0x80; 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; const uint8_t *a0 = src + offset12;
for (int y = 0; y < 224; y += 8) { for (int y = 0; y < 224; y += 8) {
for (int x = 0; x < 256; x += 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; int d0 = d3 & 0x7FF;
if (d0 != 0 && sgdBuf) { if (d0 != 0 && sgdBuf) {
d0 -= 896; d0 -= 896;
} }
if (d0 != 0) { if (d0 != 0) {
const uint8_t *a2 = a5 + d0 * 32; const uint8_t *a2 = a5 + d0 * 32;
if ((d3 & (1 << 12)) != 0) { const bool yflip = (d3 & (1 << 12)) != 0;
a2 = AMIGA_mirrorY(a2); const bool xflip = (d3 & (1 << 11)) != 0;
}
if ((d3 & (1 << 11)) != 0) {
a2 = AMIGA_mirrorX(a2);
}
int mask = 0; int mask = 0;
if ((d3 & 0x6000) != 0 && sgdBuf) { if ((d3 & 0x6000) != 0 && sgdBuf) {
mask = 0x10; mask = 0x10;
} else if ((d3 < (1 << 15)) == 0) { } else if ((d3 < (1 << 15)) == 0) {
mask = 0x80; 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++; const int d3 = *a1++;
if (d3 == 255) { if (d3 == 255) {
assert(sz + d1 < kTempMbkSize * 32); assert(sz + d1 <= kTempMbkSize * 32);
memcpy(buf + sz, a6, d1); memcpy(buf + sz, a6, d1);
sz += d1; sz += d1;
} else { } else {
for (int i = 0; i < d3 + 1; ++i) { for (int i = 0; i < d3 + 1; ++i) {
const int d4 = *a1++; const int d4 = *a1++;
assert(sz + 32 < kTempMbkSize * 32); assert(sz + 32 <= kTempMbkSize * 32);
memcpy(buf + sz, a6 + d4 * 32, 32); memcpy(buf + sz, a6 + d4 * 32, 32);
sz += 32; sz += 32;
} }
} }
} }
memset(_frontLayer, 0, Video::GAMESCREEN_W * Video::GAMESCREEN_H); memset(_frontLayer, 0, _layerSize);
if (tmp[1] != 0) { if (tmp[1] != 0) {
assert(_res->_sgd); assert(_res->_sgd);
AMIGA_decodeSgd(_frontLayer, tmp + offset10, _res->_sgd); decodeSgd(_frontLayer, tmp + offset10, _res->_sgd, _res->isAmiga());
offset10 = 0; offset10 = 0;
} }
AMIGA_decodeLevHelper(_frontLayer, tmp, offset10, offset12, buf, tmp[1] != 0); decodeLevHelper(_frontLayer, tmp, offset10, offset12, buf, tmp[1] != 0, _res->isDOS());
memcpy(_backLayer, _frontLayer, Video::GAMESCREEN_W * Video::GAMESCREEN_H); free(buf);
uint16_t num[4]; memcpy(_backLayer, _frontLayer, _layerSize);
for (int i = 0; i < 4; ++i) { _mapPalSlot1 = READ_BE_UINT16(tmp + 2);
num[i] = READ_BE_UINT16(tmp + 2 + i * 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]; setPaletteSlotBE(0x0, _mapPalSlot1);
_mapPalSlot2 = num[2];
setPaletteSlotBE(0x0, num[0]);
for (int i = 1; i < 5; ++i) { for (int i = 1; i < 5; ++i) {
setPaletteSlotBE(i, _mapPalSlot2); setPaletteSlotBE(i, _mapPalSlot3);
} }
setPaletteSlotBE(0x6, _mapPalSlot2); setPaletteSlotBE(0x6, _mapPalSlot3);
setPaletteSlotBE(0x8, num[0]); setPaletteSlotBE(0x8, _mapPalSlot1);
setPaletteSlotBE(0xA, _mapPalSlot2); setPaletteSlotBE(0xA, _mapPalSlot3);
} }
void Video::AMIGA_decodeSpm(const uint8_t *src, uint8_t *dst) { void Video::AMIGA_decodeSpm(const uint8_t *src, uint8_t *dst) {
uint8_t buf[256 * 32]; uint8_t buf[256 * 32];
const int size = READ_BE_UINT16(src + 3) & 0x7FFF; const int size = READ_BE_UINT16(src + 3) & 0x7FFF;
assert(size <= (int)sizeof(buf)); assert(size <= (int)sizeof(buf));
AMIGA_decodeRLE(buf, src + 3); AMIGA_decodeRle(buf, src + 3);
const int w = (src[2] >> 7) + 1; const int w = (src[2] >> 7) + 1;
const int h = src[2] & 0x7F; 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) { 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 h = 1 + *src++;
const int w = 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) { void Video::AMIGA_decodeSpc(const uint8_t *src, int w, int h, uint8_t *dst) {
switch (w) { switch (w) {
case 8: 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; break;
default: default:
AMIGA_blit4pNxN(dst, w, h, src); warning("AMIGA_decodeSpc w=%d unimplemented", w);
break; 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) { 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); debug(DBG_VIDEO, "Video::drawSpriteSub1(0x%X, 0x%X, 0x%X, 0x%X)", pitch, w, h, colMask);
while (h--) { while (h--) {
@ -811,7 +824,7 @@ const char *Video::drawString(const char *str, int16_t x, int16_t y, uint8_t col
case kResourceTypeAmiga: case kResourceTypeAmiga:
drawCharFunc = &Video::AMIGA_drawStringChar; drawCharFunc = &Video::AMIGA_drawStringChar;
break; break;
case kResourceTypePC: case kResourceTypeDOS:
drawCharFunc = &Video::PC_drawStringChar; drawCharFunc = &Video::PC_drawStringChar;
break; 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); markBlockAsDirty(x, y, len * 8, 8);
return str - 1; 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;
}

24
video.h
View File

@ -1,18 +1,7 @@
/* REminiscence - Flashback interpreter
* Copyright (C) 2005-2015 Gregory Montoir /*
* * REminiscence - Flashback interpreter
* This program is free software: you can redistribute it and/or modify * Copyright (C) 2005-2015 Gregory Montoir (cyx@users.sourceforge.net)
* 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 <http://www.gnu.org/licenses/>.
*/ */
#ifndef VIDEO_H__ #ifndef VIDEO_H__
@ -41,6 +30,8 @@ struct Video {
Resource *_res; Resource *_res;
SystemStub *_stub; SystemStub *_stub;
int _w, _h;
int _layerSize;
uint8_t *_frontLayer; uint8_t *_frontLayer;
uint8_t *_backLayer; uint8_t *_backLayer;
uint8_t *_tempLayer; uint8_t *_tempLayer;
@ -67,6 +58,7 @@ struct Video {
void setPaletteSlotLE(int palSlot, const uint8_t *palData); void setPaletteSlotLE(int palSlot, const uint8_t *palData);
void setTextPalette(); void setTextPalette();
void setPalette0xF(); void setPalette0xF();
void PC_decodeLev(int level, int room);
void PC_decodeMap(int level, int room); void PC_decodeMap(int level, int room);
void PC_setLevelPalettes(); void PC_setLevelPalettes();
void PC_decodeIcn(const uint8_t *src, int num, uint8_t *dst); void PC_decodeIcn(const uint8_t *src, int num, uint8_t *dst);
@ -75,6 +67,7 @@ struct Video {
void AMIGA_decodeSpm(const uint8_t *src, uint8_t *dst); void AMIGA_decodeSpm(const uint8_t *src, uint8_t *dst);
void AMIGA_decodeIcn(const uint8_t *src, int num, uint8_t *dst); void AMIGA_decodeIcn(const uint8_t *src, int num, uint8_t *dst);
void AMIGA_decodeSpc(const uint8_t *src, int w, int h, 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 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 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); 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 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); 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); 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__ #endif // VIDEO_H__