Import 0.4.6

This commit is contained in:
Gregory Montoir 2019-12-29 00:00:00 +08:00
parent 86baaa3a9a
commit 2dc61ca627
21 changed files with 411 additions and 262 deletions

View File

@ -1,3 +1,7 @@
* release 0.4.6
- added rewind to automatic saves
- fixed passwords and protection codes input
* release 0.4.5
- added low-pass filtering for in-game music
- added support for 3DO background music (tunes/*.Cpc)
@ -72,7 +76,7 @@
* release 0.2.2
- added support for level background music
- added italian texts
- added Italian texts
- fixed PC-CD SEQ cutscenes numbering
- fixed several issues with Amiga data files
@ -111,7 +115,7 @@
- added input keys recording
- reduced memory usage
* release 0.2.0 (2005/04/02)
* release 0.1.4 (2005/04/02)
- added screen shaking (level 2)
- added support for Amiga music (experimental)
- fixed screen refresh after teleportation
@ -127,7 +131,7 @@
- added sound effects playback
- added support for polygonal cutscenes
- added support for final credits sequence
- fixed instructions screen display in english version
- fixed instructions screen display in English version
* release 0.1.1 (2005/01/29)
- added missing opcodes, game should now be completable

View File

@ -2,24 +2,22 @@
SDL_CFLAGS := `sdl2-config --cflags`
SDL_LIBS := `sdl2-config --libs`
DL_LIBS := -ldl
MODPLUG_LIBS := -lmodplug
TREMOR_LIBS := -lvorbisidec -logg
ZLIB_LIBS := -lz
CXXFLAGS += -Wall -Wpedantic -MMD $(SDL_CFLAGS) -DUSE_MODPLUG -DUSE_STATIC_SCALER -DUSE_TREMOR -DUSE_ZLIB
CXXFLAGS += -Wall -Wpedantic -MMD $(SDL_CFLAGS) -DUSE_MODPLUG -DUSE_TREMOR -DUSE_ZLIB
SRCS = collision.cpp cpc_player.cpp cutscene.cpp decode_mac.cpp dynlib.cpp file.cpp fs.cpp game.cpp graphics.cpp main.cpp \
SRCS = collision.cpp cpc_player.cpp cutscene.cpp decode_mac.cpp file.cpp fs.cpp game.cpp graphics.cpp main.cpp \
menu.cpp mixer.cpp mod_player.cpp ogg_player.cpp piege.cpp protection.cpp resource.cpp resource_aba.cpp \
resource_mac.cpp scaler.cpp screenshot.cpp seq_player.cpp \
sfx_player.cpp staticres.cpp systemstub_sdl.cpp unpack.cpp util.cpp video.cpp
SCALERS := scalers/scaler_nearest.cpp scalers/scaler_tv2x.cpp scalers/scaler_xbr.cpp
OBJS = $(SRCS:.cpp=.o) $(SCALERS:.cpp=.o)
DEPS = $(SRCS:.cpp=.d) $(SCALERS:.cpp=.d)
LIBS = $(SDL_LIBS) $(DL_LIBS) $(MODPLUG_LIBS) $(TREMOR_LIBS) $(ZLIB_LIBS)
LIBS = $(SDL_LIBS) $(MODPLUG_LIBS) $(TREMOR_LIBS) $(ZLIB_LIBS)
rs: $(OBJS)
$(CXX) $(LDFLAGS) -o $@ $(OBJS) $(LIBS)

View File

@ -1,6 +1,6 @@
REminiscence README
Release version: 0.4.5
Release version: 0.4.6
-------------------------------------------------------------------------------
@ -50,8 +50,8 @@ These paths can be changed using command line switches :
The scaler option specifies the algorithm used to smoothen the image in
addition to a scaling factor. External scalers are also supported, the suffix
shall be used as the name. Eg. If you have scaler_xbrz.dll, you can pass
'--scaler xbrz@2' to use that algorithm with a doubled window size (512x448).
shall be used as the name. Eg. If you have scaler_xbr.dll, you can pass
'--scaler xbr@2' to use that algorithm with a doubled window size (512x448).
The widescreen option accepts two modes :
'adjacent' : left and right rooms bitmaps will be drawn
@ -69,6 +69,7 @@ In-game hotkeys :
Alt S write screenshot as .tga
Ctrl S save game state
Ctrl L load game state
Ctrl R rewind game state buffer (requires --autosave)
Ctrl + and - change game state slot
Function Keys change game screen scaler

View File

@ -410,8 +410,8 @@ void Cutscene::op_setPalette() {
}
}
void Cutscene::op_drawStringAtBottom() {
debug(DBG_CUT, "Cutscene::op_drawStringAtBottom()");
void Cutscene::op_drawCaptionText() {
debug(DBG_CUT, "Cutscene::op_drawCaptionText()");
uint16_t strId = fetchNextCmdWord();
if (!_creditsSequence) {
@ -1036,7 +1036,7 @@ bool Cutscene::load(uint16_t cutName) {
//
uint8_t *p = _res->_cmd + 0x322;
if (memcmp(p, "\x00\x18\x00\x3a", 4) == 0) {
p[0] = 0x06 << 2; // op_drawStringAtBottom
p[0] = 0x06 << 2; // op_drawCaptionText
p[1] = 0x00;
p[2] = 0x3a;
p[3] = 0x00; // op_markCurPos

View File

@ -128,7 +128,7 @@ struct Cutscene {
void op_waitForSync();
void op_drawShape();
void op_setPalette();
void op_drawStringAtBottom();
void op_drawCaptionText();
void op_nop();
void op_skip3();
void op_refreshAll();

View File

@ -1,59 +0,0 @@
#ifdef _WIN32
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#else
#include <dlfcn.h>
#endif
#include <stdio.h>
#include <sys/param.h>
#include "dynlib.h"
#ifdef WIN32
struct DynLib_impl {
HINSTANCE _dl;
DynLib_impl(const char *name) {
char dllname[MAXPATHLEN];
snprintf(dllname, sizeof(dllname), "%s.dll", name);
_dl = LoadLibrary(dllname);
}
~DynLib_impl() {
if (_dl) {
FreeLibrary(_dl);
_dl = 0;
}
}
void *getSymbol(const char *name) {
return (void *)GetProcAddress(_dl, name);
}
};
#else
struct DynLib_impl {
void *_dl;
DynLib_impl(const char *name) {
char soname[MAXPATHLEN];
snprintf(soname, sizeof(soname), "%s.so", name);
_dl = dlopen(soname, RTLD_LAZY);
}
~DynLib_impl() {
if (_dl) {
dlclose(_dl);
}
}
void *getSymbol(const char *name) {
return dlsym(_dl, name);
}
};
#endif
DynLib::DynLib(const char *name) {
_impl = new DynLib_impl(name);
}
DynLib::~DynLib() {
delete _impl;
}
void *DynLib::getSymbol(const char *name) {
return _impl->getSymbol(name);
}

View File

@ -1,16 +0,0 @@
#ifndef DYNLIB_H__
#define DYNLIB_H__
struct DynLib_impl;
struct DynLib {
DynLib_impl *_impl;
DynLib(const char *name);
~DynLib();
void *getSymbol(const char *name);
};
#endif // DYNLIB_H__

View File

@ -197,6 +197,58 @@ struct AssetFile: File_impl {
};
#endif
struct MemoryBufferFile: File_impl {
uint8_t *_ptr;
uint32_t _capacity, _offset, _len;
MemoryBufferFile(int initialCapacity) {
_capacity = initialCapacity;
_ptr = (uint8_t *)malloc(_capacity);
_offset = _len = 0;
}
~MemoryBufferFile() {
free(_ptr);
}
bool open(const char *path, const char *mode) {
return false;
}
void close() {
}
uint32_t size() {
return _len;
}
uint32_t tell() {
return _offset;
}
void seek(int offs) {
_offset = offs;
}
uint32_t read(void *ptr, uint32_t len) {
int count = len;
if (_offset + count > _len) {
count = _len - _offset;
_ioErr = true;
}
if (count != 0) {
memcpy(ptr, _ptr + _offset, count);
_offset += count;
}
return count;
}
uint32_t write(const void *ptr, uint32_t len) {
int count = len;
while (_offset + count > _capacity) {
_capacity *= 2;
_ptr = (uint8_t *)realloc(_ptr, _capacity);
}
if (count != 0) {
memcpy(_ptr + _offset, ptr, count);
_offset += count;
}
_len = _offset;
return count;
}
};
File::File()
: _impl(0) {
}
@ -264,6 +316,15 @@ bool File::open(const char *filename, const char *mode, const char *directory) {
return _impl->open(path, mode);
}
void File::openMemoryBuffer(int initialCapacity) {
if (_impl) {
_impl->close();
delete _impl;
_impl = 0;
}
_impl = new MemoryBufferFile(initialCapacity);
}
void File::close() {
if (_impl) {
_impl->close();

1
file.h
View File

@ -20,6 +20,7 @@ struct File {
bool open(const char *filename, const char *mode, FileSystem *fs);
bool open(const char *filename, const char *mode, const char *directory);
void openMemoryBuffer(int initialCapacity);
void close();
bool ioErr() const;
uint32_t size();

View File

@ -25,6 +25,8 @@ Game::Game(SystemStub *stub, FileSystem *fs, const char *savePath, int level, Re
_demoBin = -1;
_widescreenMode = widescreenMode;
_autoSave = autoSave;
_rewindPtr = -1;
_rewindLen = 0;
}
void Game::run() {
@ -159,6 +161,7 @@ void Game::run() {
_vid._unkPalSlot1 = 0;
_vid._unkPalSlot2 = 0;
_score = 0;
clearStateRewind();
loadLevelData();
resetGameState();
_endLoop = false;
@ -195,7 +198,7 @@ void Game::displayTitleScreenAmiga() {
if (!buf) {
error("Failed to allocate screen buffer w=%d h=%d", kW, kH);
}
static const int kAmigaColors[] = {
static const uint16_t 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,
@ -384,11 +387,12 @@ void Game::mainLoop() {
playCutscene(0x41);
_endLoop = true;
} else {
if (_autoSave && loadGameState(kAutoSaveSlot)) {
if (_autoSave && _rewindLen != 0 && loadGameState(kAutoSaveSlot)) {
// autosave
} else if (_validSaveState && loadGameState(kIngameSaveSlot)) {
// ingame save
} else {
clearStateRewind();
loadLevelData();
resetGameState();
}
@ -453,9 +457,9 @@ void Game::mainLoop() {
}
}
inp_handleSpecialKeys();
if (_stub->getTimeStamp() - _saveTimestamp >= kAutoSaveIntervalMs) {
// do not save if we just died
if (_pgeLive[0].life > 0) {
if (_autoSave && _stub->getTimeStamp() - _saveTimestamp >= kAutoSaveIntervalMs) {
// do not save if we died or about to
if (_pgeLive[0].life > 0 && _deathCutsceneCounter == 0) {
saveGameState(kAutoSaveSlot);
_saveTimestamp = _stub->getTimeStamp();
}
@ -594,6 +598,14 @@ void Game::inp_handleSpecialKeys() {
}
_stub->_pi.stateSlot = 0;
}
if (_stub->_pi.rewind) {
if (_rewindLen != 0) {
loadStateRewind();
} else {
debug(DBG_INFO, "Rewind buffer is empty");
}
_stub->_pi.rewind = false;
}
}
void Game::drawCurrentInventoryItem() {
@ -1817,6 +1829,7 @@ uint16_t Game::getRandomNumber() {
void Game::changeLevel() {
_vid.fadeOut();
clearStateRewind();
loadLevelData();
loadLevelMap();
_vid.setPalette0xF();
@ -1981,6 +1994,9 @@ void Game::makeGameStateName(uint8_t slot, char *buf) {
static const uint32_t TAG_FBSV = 0x46425356;
bool Game::saveGameState(uint8_t slot) {
if (slot == kAutoSaveSlot) {
return saveStateRewind();
}
bool success = false;
char stateFile[32];
makeGameStateName(slot, stateFile);
@ -2008,6 +2024,9 @@ bool Game::saveGameState(uint8_t slot) {
}
bool Game::loadGameState(uint8_t slot) {
if (slot == kAutoSaveSlot) {
return loadStateRewind();
}
bool success = false;
char stateFile[32];
makeGameStateName(slot, stateFile);
@ -2177,6 +2196,53 @@ void Game::loadState(File *f) {
resetGameState();
}
void Game::clearStateRewind() {
// debug(DBG_INFO, "Clear rewind state (count %d)", _rewindLen);
for (int i = 0; i < _rewindLen; ++i) {
int ptr = _rewindPtr - i;
if (ptr < 0) {
ptr += kRewindSize;
}
_rewindBuffer[ptr].close();
}
_rewindPtr = -1;
_rewindLen = 0;
}
bool Game::saveStateRewind() {
if (_rewindPtr == kRewindSize - 1) {
_rewindPtr = 0;
} else {
++_rewindPtr;
}
static const int kGameStateSize = 16384;
File &f = _rewindBuffer[_rewindPtr];
f.openMemoryBuffer(kGameStateSize);
saveState(&f);
if (_rewindLen < kRewindSize) {
++_rewindLen;
}
// debug(DBG_INFO, "Save state for rewind (index %d, count %d, size %d)", _rewindPtr, _rewindLen, f.size());
return !f.ioErr();
}
bool Game::loadStateRewind() {
const int ptr = _rewindPtr;
if (_rewindPtr == 0) {
_rewindPtr = kRewindSize - 1;
} else {
--_rewindPtr;
}
File &f = _rewindBuffer[ptr];
f.seek(0);
loadState(&f);
if (_rewindLen > 0) {
--_rewindLen;
}
// debug(DBG_INFO, "Rewind state (index %d, count %d, size %d)", ptr, _rewindLen, f.size());
return !f.ioErr();
}
void AnimBuffers::addState(uint8_t stateNum, int16_t x, int16_t y, const uint8_t *dataPtr, LivePGE *pge, uint8_t w, uint8_t h) {
debug(DBG_GAME, "AnimBuffers::addState() stateNum=%d x=%d y=%d dataPtr=%p pge=%p", stateNum, x, y, dataPtr, pge);
assert(stateNum < 4);

8
game.h
View File

@ -27,8 +27,9 @@ struct Game {
enum {
kIngameSaveSlot = 0,
kRewindSize = 120, // 10mins (~2MB)
kAutoSaveSlot = 255,
kAutoSaveIntervalMs = 120 * 1000
kAutoSaveIntervalMs = 5 * 1000
};
enum {
@ -68,6 +69,8 @@ struct Game {
SystemStub *_stub;
FileSystem *_fs;
const char *_savePath;
File _rewindBuffer[kRewindSize];
int _rewindPtr, _rewindLen;
const uint8_t *_stringsTable;
const char **_textsTable;
@ -384,6 +387,9 @@ struct Game {
bool loadGameState(uint8_t slot);
void saveState(File *f);
void loadState(File *f);
void clearStateRewind();
bool saveStateRewind();
bool loadStateRewind();
};
#endif // GAME_H__

View File

@ -156,50 +156,13 @@ static void initOptions() {
}
static void parseScaler(char *name, ScalerParameters *scalerParameters) {
static const struct {
const char *name;
int type;
const Scaler *scaler;
} scalers[] = {
{ "point", kScalerTypePoint, 0 },
{ "linear", kScalerTypeLinear, 0 },
{ "scale", kScalerTypeInternal, &_internalScaler },
#ifdef USE_STATIC_SCALER
{ "nearest", kScalerTypeInternal, &scaler_nearest },
{ "tv2x", kScalerTypeInternal, &scaler_tv2x },
{ "xbr", kScalerTypeInternal, &scaler_xbr },
#endif
{ 0, -1 }
};
bool found = false;
char *sep = strchr(name, '@');
if (sep) {
*sep = 0;
}
for (int i = 0; scalers[i].name; ++i) {
if (strcmp(scalers[i].name, name) == 0) {
scalerParameters->type = (ScalerType)scalers[i].type;
scalerParameters->scaler = scalers[i].scaler;
found = true;
break;
}
}
if (!found) {
char libname[32];
snprintf(libname, sizeof(libname), "scaler_%s", name);
const Scaler *scaler = findScaler(libname);
if (!scaler) {
warning("Scaler '%s' not found, using default", libname);
} else if (scaler->tag != SCALER_TAG) {
warning("Unexpected tag %d for scaler '%s'", scaler->tag, libname);
} else {
scalerParameters->type = kScalerTypeExternal;
scalerParameters->scaler = scaler;
}
}
if (sep) {
scalerParameters->factor = atoi(sep + 1);
}
strncpy(scalerParameters->name, name, sizeof(scalerParameters->name) - 1);
scalerParameters->name[sizeof(scalerParameters->name) - 1] = 0;
}
static WidescreenMode parseWidescreen(const char *mode) {

View File

@ -212,9 +212,6 @@ bool Menu::handlePasswordScreen() {
if (c != 0) {
_stub->_pi.lastChar = 0;
if (len < 6) {
if (c >= 'a' && c <= 'z') {
c &= ~0x20;
}
if ((c >= 'A' && c <= 'Z') || (c == 0x20)) {
password[len] = c;
++len;

View File

@ -9,13 +9,11 @@
#include "systemstub.h"
static uint8_t reverseBits(uint8_t ch) {
uint8_t r = 0;
for (int b = 0; b < 8; ++b) {
if (ch & (1 << b)) {
r |= (1 << (7 - b));
}
}
return r;
static const uint8_t lut[] = {
0x0, 0x8, 0x4, 0xC, 0x2, 0xA, 0x6, 0xE,
0x1, 0x9, 0x5, 0xD, 0x3, 0xB, 0x7, 0xF
};
return (lut[ch & 15] << 4) | lut[ch >> 4];
}
static uint8_t decryptChar(uint8_t ch) {
@ -114,9 +112,6 @@ bool Game::handleProtectionScreenShape() {
if (c != 0) {
_stub->_pi.lastChar = 0;
if (len < kCodeLen) {
if (c >= 'a' && c <= 'z') {
c &= ~0x20;
}
if ((c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9')) {
codeText[len] = c;
++len;
@ -201,7 +196,7 @@ bool Game::handleProtectionScreenWords() {
const uint8_t code = getRandomNumber() % kWordsCount;
const uint8_t *protectionData = _protectionWordData + code * 18;
const char *kSecurityCodeText = "SECURITY CODE";
static const char *kSecurityCodeText = "SECURITY CODE";
_vid.drawString(kSecurityCodeText, 72 + (114 - strlen(kSecurityCodeText) * 8) / 2, 158, 0xE4);
char buf[16];
snprintf(buf, sizeof(buf), "PAGE %d", protectionData[0]);
@ -229,9 +224,6 @@ bool Game::handleProtectionScreenWords() {
if (c != 0) {
_stub->_pi.lastChar = 0;
if (len < kCodeLen) {
if (c >= 'a' && c <= 'z') {
c &= ~0x20;
}
if (c >= 'A' && c <= 'Z') {
codeText[len] = c;
++len;

View File

@ -15,6 +15,10 @@ ResourceAba::~ResourceAba() {
free(_entries);
}
static int compareAbaEntry(const void *a, const void *b) {
return strcasecmp(((ResourceAbaEntry *)a)->name, ((ResourceAbaEntry *)b)->name);
}
void ResourceAba::readEntries() {
if (_f.open(FILENAME, "rb", _fs)) {
_entriesCount = _f.readUint16BE();
@ -39,16 +43,14 @@ void ResourceAba::readEntries() {
}
nextOffset = _entries[i].offset + _entries[i].compressedSize;
}
qsort(_entries, _entriesCount, sizeof(ResourceAbaEntry), compareAbaEntry);
}
}
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;
ResourceAbaEntry tmp;
strcpy(tmp.name, name);
return (const ResourceAbaEntry *)bsearch(&tmp, _entries, _entriesCount, sizeof(ResourceAbaEntry), compareAbaEntry);
}
uint8_t *ResourceAba::loadEntry(const char *name, uint32_t *size) {

View File

@ -5,7 +5,6 @@
*/
#include "scaler.h"
#include "dynlib.h"
#include "util.h"
static void scanline2x(uint32_t *dst0, uint32_t *dst1, const uint32_t *src0, const uint32_t *src1, const uint32_t *src2, int w) {
@ -282,17 +281,3 @@ const Scaler _internalScaler = {
2, 4,
scaleNx,
};
static DynLib *dynLib;
static const char *kSoSym = "getScaler";
const Scaler *findScaler(const char *name) {
dynLib = new DynLib(name);
void *symbol = dynLib->getSymbol(kSoSym);
if (symbol) {
typedef const Scaler *(*GetScalerProc)();
return ((GetScalerProc)symbol)();
}
return 0;
}

View File

@ -24,6 +24,7 @@ void SeqDemuxer::close() {
for (int i = 0; i < kBuffersCount; ++i) {
free(_buffers[i].data);
}
memset(_buffers, 0, sizeof(_buffers));
}
bool SeqDemuxer::readHeader() {

View File

@ -17,7 +17,7 @@ const Cutscene::OpcodeStub Cutscene::_opcodeTable[] = {
/* 0x04 */
&Cutscene::op_setPalette,
&Cutscene::op_markCurPos,
&Cutscene::op_drawStringAtBottom,
&Cutscene::op_drawCaptionText,
&Cutscene::op_nop,
/* 0x08 */
&Cutscene::op_skip3,

View File

@ -35,6 +35,7 @@ struct PlayerInput {
bool save;
bool load;
int stateSlot;
bool rewind;
uint8_t dbgMask;
bool quit;
@ -42,7 +43,7 @@ struct PlayerInput {
struct ScalerParameters {
ScalerType type;
const Scaler *scaler;
char name[32];
int factor;
static ScalerParameters defaults();
@ -55,7 +56,7 @@ struct SystemStub {
virtual ~SystemStub() {}
virtual void init(const char *title, int w, int h, bool fullscreen, int widescreenMode, ScalerParameters *scalerParameters) = 0;
virtual void init(const char *title, int w, int h, bool fullscreen, int widescreenMode, const ScalerParameters *scalerParameters) = 0;
virtual void destroy() = 0;
virtual bool hasWidescreen() const = 0;

View File

@ -1,4 +1,3 @@
/*
* REminiscence - Flashback interpreter
* Copyright (C) 2005-2019 Gregory Montoir (cyx@users.sourceforge.net)
@ -22,7 +21,7 @@ static const uint32_t kPixelFormat = SDL_PIXELFORMAT_RGB888;
ScalerParameters ScalerParameters::defaults() {
ScalerParameters params;
params.type = kScalerTypeInternal;
params.scaler = &_internalScaler;
params.name[0] = 0;
params.factor = _internalScaler.factorMin + (_internalScaler.factorMax - _internalScaler.factorMin) / 2;
return params;
}
@ -47,15 +46,16 @@ struct SystemStub_SDL : SystemStub {
void *_audioCbData;
int _screenshot;
ScalerType _scalerType;
const Scaler *_scaler;
int _scaleFactor;
const Scaler *_scaler;
void *_scalerSo;
int _widescreenMode;
SDL_Texture *_widescreenTexture;
int _wideMargin;
bool _enableWidescreen;
virtual ~SystemStub_SDL() {}
virtual void init(const char *title, int w, int h, bool fullscreen, int widescreenMode, ScalerParameters *scalerParameters);
virtual void init(const char *title, int w, int h, bool fullscreen, int widescreenMode, const ScalerParameters *scalerParameters);
virtual void destroy();
virtual bool hasWidescreen() const;
virtual void setScreenSize(int w, int h);
@ -88,7 +88,8 @@ struct SystemStub_SDL : SystemStub {
void prepareGraphics();
void cleanupGraphics();
void changeGraphics(bool fullscreen, int scaleFactor);
void changeScaler(int scaler);
void setScaler(const ScalerParameters *parameters);
void changeScaler(int scalerNum);
void drawRect(int x, int y, int w, int h, uint8_t color);
};
@ -96,7 +97,7 @@ SystemStub *SystemStub_SDL_create() {
return new SystemStub_SDL();
}
void SystemStub_SDL::init(const char *title, int w, int h, bool fullscreen, int widescreenMode, ScalerParameters *scalerParameters) {
void SystemStub_SDL::init(const char *title, int w, int h, bool fullscreen, int widescreenMode, const ScalerParameters *scalerParameters) {
SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_JOYSTICK);
SDL_ShowCursor(SDL_DISABLE);
_caption = title;
@ -108,9 +109,13 @@ void SystemStub_SDL::init(const char *title, int w, int h, bool fullscreen, int
_screenBuffer = 0;
_fadeOnUpdateScreen = false;
_fullscreen = fullscreen;
_scalerType = scalerParameters->type;
_scaler = scalerParameters->scaler;
_scaleFactor = _scaler ? CLIP(scalerParameters->factor, _scaler->factorMin, _scaler->factorMax) : 1;
_scalerType = kScalerTypeInternal;
_scaleFactor = 1;
_scaler = 0;
_scalerSo = 0;
if (scalerParameters->name[0]) {
setScaler(scalerParameters);
}
memset(_rgbPalette, 0, sizeof(_rgbPalette));
memset(_darkPalette, 0, sizeof(_darkPalette));
_screenW = _screenH = 0;
@ -143,6 +148,10 @@ void SystemStub_SDL::destroy() {
SDL_FreeFormat(_fmt);
_fmt = 0;
}
if (_scalerSo) {
SDL_UnloadObject(_scalerSo);
_scalerSo = 0;
}
if (_controller) {
SDL_GameControllerClose(_controller);
_controller = 0;
@ -352,46 +361,107 @@ void SystemStub_SDL::copyWidescreenMirror(int w, int h, const uint8_t *buf) {
}
}
static uint32_t blurPixel(int x, int y, const uint8_t *src, const uint32_t *pal, int pitch, int w, int h, const SDL_PixelFormat *fmt) {
static const uint8_t blurMat[3 * 3] = {
2, 4, 2,
4, 8, 4,
2, 4, 2
};
static const int blurMatSigma = 32 * 2;
static void blur_h(int radius, const uint32_t *src, int srcPitch, int w, int h, const SDL_PixelFormat *fmt, uint32_t *dst, int dstPitch) {
const uint32_t redBlueMask = fmt->Rmask | fmt->Bmask;
const uint32_t greenMask = fmt->Gmask;
const int count = 2 * radius + 1;
uint32_t redBlueBlurSum = 0;
uint32_t greenBlurSum = 0;
for (int y = 0; y < h; ++y) {
for (int v = 0; v < 3; ++v) {
const int ym = CLIP(y + v - 1, 0, h - 1);
for (int u = 0; u < 3; ++u) {
const int xm = CLIP(x + u - 1, 0, w - 1);
const uint32_t color = pal[src[ym * pitch + xm]];
const int mul = blurMat[v * 3 + u];
redBlueBlurSum += (color & redBlueMask) * mul;
greenBlurSum += (color & greenMask) * mul;
uint32_t r = 0;
uint32_t g = 0;
uint32_t b = 0;
uint32_t color;
for (int x = -radius; x <= radius; ++x) {
color = src[MAX(x, 0)];
r += (color & fmt->Rmask) >> fmt->Rshift;
g += (color & fmt->Gmask) >> fmt->Gshift;
b += (color & fmt->Bmask) >> fmt->Bshift;
}
dst[0] = ((r / count) << fmt->Rshift) | ((g / count) << fmt->Gshift) | ((b / count) << fmt->Bshift);
for (int x = 1; x < w; ++x) {
color = src[MIN(x + radius, w - 1)];
r += (color & fmt->Rmask) >> fmt->Rshift;
g += (color & fmt->Gmask) >> fmt->Gshift;
b += (color & fmt->Bmask) >> fmt->Bshift;
color = src[MAX(x - radius - 1, 0)];
r -= (color & fmt->Rmask) >> fmt->Rshift;
g -= (color & fmt->Gmask) >> fmt->Gshift;
b -= (color & fmt->Bmask) >> fmt->Bshift;
dst[x] = ((r / count) << fmt->Rshift) | ((g / count) << fmt->Gshift) | ((b / count) << fmt->Bshift);
}
src += srcPitch;
dst += dstPitch;
}
}
static void blur_v(int radius, const uint32_t *src, int srcPitch, int w, int h, const SDL_PixelFormat *fmt, uint32_t *dst, int dstPitch) {
const int count = 2 * radius + 1;
for (int x = 0; x < w; ++x) {
uint32_t r = 0;
uint32_t g = 0;
uint32_t b = 0;
uint32_t color;
for (int y = -radius; y <= radius; ++y) {
color = src[MAX(y, 0) * srcPitch];
r += (color & fmt->Rmask) >> fmt->Rshift;
g += (color & fmt->Gmask) >> fmt->Gshift;
b += (color & fmt->Bmask) >> fmt->Bshift;
}
dst[0] = ((r / count) << fmt->Rshift) | ((g / count) << fmt->Gshift) | ((b / count) << fmt->Bshift);
for (int y = 1; y < h; ++y) {
color = src[MIN(y + radius, h - 1) * srcPitch];
r += (color & fmt->Rmask) >> fmt->Rshift;
g += (color & fmt->Gmask) >> fmt->Gshift;
b += (color & fmt->Bmask) >> fmt->Bshift;
color = src[MAX(y - radius - 1, 0) * srcPitch];
r -= (color & fmt->Rmask) >> fmt->Rshift;
g -= (color & fmt->Gmask) >> fmt->Gshift;
b -= (color & fmt->Bmask) >> fmt->Bshift;
dst[y * dstPitch] = ((r / count) << fmt->Rshift) | ((g / count) << fmt->Gshift) | ((b / count) << fmt->Bshift);
}
++src;
++dst;
}
return ((redBlueBlurSum / blurMatSigma) & redBlueMask) | ((greenBlurSum / blurMatSigma) & greenMask);
}
void SystemStub_SDL::copyWidescreenBlur(int w, int h, const uint8_t *buf) {
assert(w == _screenW && h == _screenH);
void *dst = 0;
void *ptr = 0;
int pitch = 0;
if (SDL_LockTexture(_widescreenTexture, 0, &dst, &pitch) == 0) {
if (SDL_LockTexture(_widescreenTexture, 0, &ptr, &pitch) == 0) {
assert((pitch & 3) == 0);
uint32_t *p = (uint32_t *)dst;
for (int y = 0; y < h; ++y) {
for (int x = 0; x < w; ++x) {
p[x] = blurPixel(x, y, buf, _rgbPalette, w, w, h, _fmt);
uint32_t *src = (uint32_t *)malloc(w * h * sizeof(uint32_t));
uint32_t *tmp = (uint32_t *)malloc(w * h * sizeof(uint32_t));
uint32_t *dst = (uint32_t *)ptr;
if (src && tmp) {
for (int i = 0; i < w * h; ++i) {
src[i] = _rgbPalette[buf[i]];
}
p += pitch / sizeof(uint32_t);
static const int radius = 8;
blur_h(radius, src, w, w, h, _fmt, tmp, w);
blur_v(radius, tmp, w, w, h, _fmt, dst, pitch / sizeof(uint32_t));
}
free(src);
free(tmp);
SDL_UnlockTexture(_widescreenTexture);
}
}
@ -476,6 +546,23 @@ void SystemStub_SDL::processEvents() {
}
}
// only used for the protection codes and level passwords
static void setAsciiChar(PlayerInput &pi, const SDL_Keysym *key) {
if (key->sym >= SDLK_0 && key->sym <= SDLK_9) {
pi.lastChar = '0' + key->sym - SDLK_0;
} else if (key->sym >= SDLK_a && key->sym <= SDLK_z) {
pi.lastChar = 'A' + key->sym - SDLK_a;
} else if (key->scancode == SDL_SCANCODE_0) {
pi.lastChar = '0';
} else if (key->scancode >= SDL_SCANCODE_1 && key->scancode <= SDL_SCANCODE_9) {
pi.lastChar = '1' + key->scancode - SDL_SCANCODE_1;
} else if (key->sym == SDLK_SPACE || key->sym == SDLK_KP_SPACE) {
pi.lastChar = ' ';
} else {
pi.lastChar = 0;
}
}
void SystemStub_SDL::processEvent(const SDL_Event &ev, bool &paused) {
switch (ev.type) {
case SDL_QUIT:
@ -681,6 +768,9 @@ void SystemStub_SDL::processEvent(const SDL_Event &ev, bool &paused) {
case SDLK_l:
_pi.load = true;
break;
case SDLK_r:
_pi.rewind = true;
break;
case SDLK_KP_PLUS:
case SDLK_PAGEUP:
_pi.stateSlot = 1;
@ -692,7 +782,7 @@ void SystemStub_SDL::processEvent(const SDL_Event &ev, bool &paused) {
}
break;
}
_pi.lastChar = ev.key.keysym.sym;
setAsciiChar(_pi, &ev.key.keysym);
switch (ev.key.keysym.sym) {
case SDLK_LEFT:
_pi.dirMask &= ~PlayerInput::DIR_LEFT;
@ -917,39 +1007,97 @@ void SystemStub_SDL::changeGraphics(bool fullscreen, int scaleFactor) {
prepareGraphics();
}
void SystemStub_SDL::changeScaler(int scaler) {
ScalerParameters scalerParameters = ScalerParameters::defaults();
switch (scaler) {
void SystemStub_SDL::setScaler(const ScalerParameters *parameters) {
static const struct {
const char *name;
int type;
const Scaler *scaler;
} scalers[] = {
{ "point", kScalerTypePoint, 0 },
{ "linear", kScalerTypeLinear, 0 },
{ "scale", kScalerTypeInternal, &_internalScaler },
#ifdef USE_STATIC_SCALER
{ "nearest", kScalerTypeInternal, &scaler_nearest },
{ "tv2x", kScalerTypeInternal, &scaler_tv2x },
{ "xbr", kScalerTypeInternal, &scaler_xbr },
#endif
{ 0, -1 }
};
bool found = false;
for (int i = 0; scalers[i].name; ++i) {
if (strcmp(scalers[i].name, parameters->name) == 0) {
_scalerType = (ScalerType)scalers[i].type;
_scaler = scalers[i].scaler;
found = true;
break;
}
}
if (!found) {
#ifdef _WIN32
static const char *libSuffix = "dll";
#else
static const char *libSuffix = "so";
#endif
char libname[64];
snprintf(libname, sizeof(libname), "scaler_%s.%s", parameters->name, libSuffix);
_scalerSo = SDL_LoadObject(libname);
if (!_scalerSo) {
warning("Scaler '%s' not found, using default", libname);
} else {
static const char *kSoSym = "getScaler";
void *symbol = SDL_LoadFunction(_scalerSo, kSoSym);
if (!symbol) {
warning("Symbol '%s' not found in '%s'", kSoSym, libname);
} else {
typedef const Scaler *(*GetScalerProc)();
const Scaler *scaler = ((GetScalerProc)symbol)();
const int tag = scaler ? scaler->tag : 0;
if (tag != SCALER_TAG) {
warning("Unexpected tag %d for scaler '%s'", tag, libname);
} else {
_scalerType = kScalerTypeExternal;
_scaler = scaler;
}
}
}
}
_scaleFactor = _scaler ? CLIP(parameters->factor, _scaler->factorMin, _scaler->factorMax) : 1;
}
void SystemStub_SDL::changeScaler(int scalerNum) {
ScalerType type = kScalerTypeInternal;
const Scaler *scaler = 0;
switch (scalerNum) {
case 0:
scalerParameters.type = kScalerTypePoint;
type = kScalerTypePoint;
break;
case 1:
scalerParameters.type = kScalerTypeLinear;
type = kScalerTypeLinear;
break;
case 2:
scalerParameters.type = kScalerTypeInternal;
scalerParameters.scaler = &_internalScaler;
type = kScalerTypeInternal;
scaler = &_internalScaler;
break;
#ifdef USE_STATIC_SCALER
case 3:
scalerParameters.type = kScalerTypeInternal;
scalerParameters.scaler = &scaler_nearest;
type = kScalerTypeInternal;
scaler = &scaler_nearest;
break;
case 4:
scalerParameters.type = kScalerTypeInternal;
scalerParameters.scaler = &scaler_tv2x;
type = kScalerTypeInternal;
scaler = &scaler_tv2x;
break;
case 5:
scalerParameters.type = kScalerTypeInternal;
scalerParameters.scaler = &scaler_xbr;
type = kScalerTypeInternal;
scaler = &scaler_xbr;
break;
#endif
default:
return;
}
if (_scalerType != scalerParameters.type || scalerParameters.scaler != _scaler) {
_scalerType = scalerParameters.type;
_scaler = scalerParameters.scaler;
if (_scalerType != type || scaler != _scaler) {
_scalerType = type;
_scaler = scaler;
if (_scalerType == kScalerTypeInternal || _scalerType == kScalerTypeExternal) {
_scaleFactor = CLIP(_scaleFactor, _scaler->factorMin, _scaler->factorMax);
} else {

View File

@ -16,52 +16,48 @@ struct UnpackCtx {
};
static bool nextBit(UnpackCtx *uc) {
bool carry = (uc->bits & 1) != 0;
bool bit = (uc->bits & 1) != 0;
uc->bits >>= 1;
if (uc->bits == 0) { // getnextlwd
uc->bits = READ_BE_UINT32(uc->src); uc->src -= 4;
uc->crc ^= uc->bits;
carry = (uc->bits & 1) != 0;
uc->bits = (1 << 31) | (uc->bits >> 1);
const uint32_t bits = READ_BE_UINT32(uc->src); uc->src -= 4;
uc->crc ^= bits;
bit = (bits & 1) != 0;
uc->bits = (1 << 31) | (bits >> 1);
}
return carry;
return bit;
}
static int getBits(UnpackCtx *uc, int count) { // rdd1bits
int bits = 0;
template<int count>
static uint32_t getBits(UnpackCtx *uc) { // rdd1bits
uint32_t bits = 0;
for (int i = 0; i < count; ++i) {
bits <<= 1;
if (nextBit(uc)) {
bits |= 1;
}
bits |= (nextBit(uc) ? 1 : 0) << (count - 1 - i);
}
return bits;
}
static void copyLiteral(UnpackCtx *uc, int bitsCount, int len) { // getd3chr
int count = getBits(uc, bitsCount) + len + 1;
uc->size -= count;
static void copyLiteral(UnpackCtx *uc, int len) { // getd3chr
uc->size -= len;
if (uc->size < 0) {
count += uc->size;
len += uc->size;
uc->size = 0;
}
for (int i = 0; i < count; ++i) {
*(uc->dst - i) = (uint8_t)getBits(uc, 8);
for (int i = 0; i < len; ++i) {
*(uc->dst - i) = (uint8_t)getBits<8>(uc);
}
uc->dst -= count;
uc->dst -= len;
}
static void copyReference(UnpackCtx *uc, int bitsCount, int count) { // copyd3bytes
uc->size -= count;
static void copyReference(UnpackCtx *uc, int len, int offset) { // copyd3bytes
uc->size -= len;
if (uc->size < 0) {
count += uc->size;
len += uc->size;
uc->size = 0;
}
const int offset = getBits(uc, bitsCount);
for (int i = 0; i < count; ++i) {
for (int i = 0; i < len; ++i) {
*(uc->dst - i) = *(uc->dst - i + offset);
}
uc->dst -= count;
uc->dst -= len;
}
bool bytekiller_unpack(uint8_t *dst, int dstSize, const uint8_t *src, int srcSize) {
@ -79,24 +75,26 @@ bool bytekiller_unpack(uint8_t *dst, int dstSize, const uint8_t *src, int srcSiz
do {
if (!nextBit(&uc)) {
if (!nextBit(&uc)) {
copyLiteral(&uc, 3, 0);
copyLiteral(&uc, getBits<3>(&uc) + 1);
} else {
copyReference(&uc, 8, 2);
copyReference(&uc, 2, getBits<8>(&uc));
}
} else {
const int code = getBits(&uc, 2);
const int code = getBits<2>(&uc);
switch (code) {
case 3:
copyLiteral(&uc, 8, 8);
copyLiteral(&uc, getBits<8>(&uc) + 9);
break;
case 2:
copyReference(&uc, 12, getBits(&uc, 8) + 1);
case 2: {
const int len = getBits<8>(&uc) + 1;
copyReference(&uc, len, getBits<12>(&uc));
}
break;
case 1:
copyReference(&uc, 10, 4);
copyReference(&uc, 4, getBits<10>(&uc));
break;
case 0:
copyReference(&uc, 9, 3);
copyReference(&uc, 3, getBits<9>(&uc));
break;
}
}