Import 0.4.6
This commit is contained in:
parent
86baaa3a9a
commit
2dc61ca627
10
CHANGES.txt
10
CHANGES.txt
|
@ -1,3 +1,7 @@
|
||||||
|
* release 0.4.6
|
||||||
|
- added rewind to automatic saves
|
||||||
|
- fixed passwords and protection codes input
|
||||||
|
|
||||||
* release 0.4.5
|
* release 0.4.5
|
||||||
- added low-pass filtering for in-game music
|
- added low-pass filtering for in-game music
|
||||||
- added support for 3DO background music (tunes/*.Cpc)
|
- added support for 3DO background music (tunes/*.Cpc)
|
||||||
|
@ -72,7 +76,7 @@
|
||||||
|
|
||||||
* release 0.2.2
|
* release 0.2.2
|
||||||
- added support for level background music
|
- added support for level background music
|
||||||
- added italian texts
|
- added Italian texts
|
||||||
- fixed PC-CD SEQ cutscenes numbering
|
- fixed PC-CD SEQ cutscenes numbering
|
||||||
- fixed several issues with Amiga data files
|
- fixed several issues with Amiga data files
|
||||||
|
|
||||||
|
@ -111,7 +115,7 @@
|
||||||
- added input keys recording
|
- added input keys recording
|
||||||
- reduced memory usage
|
- reduced memory usage
|
||||||
|
|
||||||
* release 0.2.0 (2005/04/02)
|
* release 0.1.4 (2005/04/02)
|
||||||
- added screen shaking (level 2)
|
- added screen shaking (level 2)
|
||||||
- added support for Amiga music (experimental)
|
- added support for Amiga music (experimental)
|
||||||
- fixed screen refresh after teleportation
|
- fixed screen refresh after teleportation
|
||||||
|
@ -127,7 +131,7 @@
|
||||||
- added sound effects playback
|
- added sound effects playback
|
||||||
- added support for polygonal cutscenes
|
- added support for polygonal cutscenes
|
||||||
- added support for final credits sequence
|
- 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)
|
* release 0.1.1 (2005/01/29)
|
||||||
- added missing opcodes, game should now be completable
|
- added missing opcodes, game should now be completable
|
||||||
|
|
8
Makefile
8
Makefile
|
@ -2,24 +2,22 @@
|
||||||
SDL_CFLAGS := `sdl2-config --cflags`
|
SDL_CFLAGS := `sdl2-config --cflags`
|
||||||
SDL_LIBS := `sdl2-config --libs`
|
SDL_LIBS := `sdl2-config --libs`
|
||||||
|
|
||||||
DL_LIBS := -ldl
|
|
||||||
MODPLUG_LIBS := -lmodplug
|
MODPLUG_LIBS := -lmodplug
|
||||||
TREMOR_LIBS := -lvorbisidec -logg
|
TREMOR_LIBS := -lvorbisidec -logg
|
||||||
ZLIB_LIBS := -lz
|
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 \
|
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 \
|
resource_mac.cpp scaler.cpp screenshot.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
|
||||||
|
|
||||||
SCALERS := scalers/scaler_nearest.cpp scalers/scaler_tv2x.cpp scalers/scaler_xbr.cpp
|
|
||||||
|
|
||||||
OBJS = $(SRCS:.cpp=.o) $(SCALERS:.cpp=.o)
|
OBJS = $(SRCS:.cpp=.o) $(SCALERS:.cpp=.o)
|
||||||
DEPS = $(SRCS:.cpp=.d) $(SCALERS:.cpp=.d)
|
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)
|
rs: $(OBJS)
|
||||||
$(CXX) $(LDFLAGS) -o $@ $(OBJS) $(LIBS)
|
$(CXX) $(LDFLAGS) -o $@ $(OBJS) $(LIBS)
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
|
|
||||||
REminiscence README
|
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
|
The scaler option specifies the algorithm used to smoothen the image in
|
||||||
addition to a scaling factor. External scalers are also supported, the suffix
|
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
|
shall be used as the name. Eg. If you have scaler_xbr.dll, you can pass
|
||||||
'--scaler xbrz@2' to use that algorithm with a doubled window size (512x448).
|
'--scaler xbr@2' to use that algorithm with a doubled window size (512x448).
|
||||||
|
|
||||||
The widescreen option accepts two modes :
|
The widescreen option accepts two modes :
|
||||||
'adjacent' : left and right rooms bitmaps will be drawn
|
'adjacent' : left and right rooms bitmaps will be drawn
|
||||||
|
@ -69,6 +69,7 @@ In-game hotkeys :
|
||||||
Alt S write screenshot as .tga
|
Alt S write screenshot as .tga
|
||||||
Ctrl S save game state
|
Ctrl S save game state
|
||||||
Ctrl L load game state
|
Ctrl L load game state
|
||||||
|
Ctrl R rewind game state buffer (requires --autosave)
|
||||||
Ctrl + and - change game state slot
|
Ctrl + and - change game state slot
|
||||||
Function Keys change game screen scaler
|
Function Keys change game screen scaler
|
||||||
|
|
||||||
|
|
|
@ -410,8 +410,8 @@ void Cutscene::op_setPalette() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Cutscene::op_drawStringAtBottom() {
|
void Cutscene::op_drawCaptionText() {
|
||||||
debug(DBG_CUT, "Cutscene::op_drawStringAtBottom()");
|
debug(DBG_CUT, "Cutscene::op_drawCaptionText()");
|
||||||
uint16_t strId = fetchNextCmdWord();
|
uint16_t strId = fetchNextCmdWord();
|
||||||
if (!_creditsSequence) {
|
if (!_creditsSequence) {
|
||||||
|
|
||||||
|
@ -1036,7 +1036,7 @@ bool Cutscene::load(uint16_t cutName) {
|
||||||
//
|
//
|
||||||
uint8_t *p = _res->_cmd + 0x322;
|
uint8_t *p = _res->_cmd + 0x322;
|
||||||
if (memcmp(p, "\x00\x18\x00\x3a", 4) == 0) {
|
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[1] = 0x00;
|
||||||
p[2] = 0x3a;
|
p[2] = 0x3a;
|
||||||
p[3] = 0x00; // op_markCurPos
|
p[3] = 0x00; // op_markCurPos
|
||||||
|
|
|
@ -128,7 +128,7 @@ struct Cutscene {
|
||||||
void op_waitForSync();
|
void op_waitForSync();
|
||||||
void op_drawShape();
|
void op_drawShape();
|
||||||
void op_setPalette();
|
void op_setPalette();
|
||||||
void op_drawStringAtBottom();
|
void op_drawCaptionText();
|
||||||
void op_nop();
|
void op_nop();
|
||||||
void op_skip3();
|
void op_skip3();
|
||||||
void op_refreshAll();
|
void op_refreshAll();
|
||||||
|
|
59
dynlib.cpp
59
dynlib.cpp
|
@ -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);
|
|
||||||
}
|
|
16
dynlib.h
16
dynlib.h
|
@ -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__
|
|
61
file.cpp
61
file.cpp
|
@ -197,6 +197,58 @@ struct AssetFile: File_impl {
|
||||||
};
|
};
|
||||||
#endif
|
#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()
|
File::File()
|
||||||
: _impl(0) {
|
: _impl(0) {
|
||||||
}
|
}
|
||||||
|
@ -264,6 +316,15 @@ bool File::open(const char *filename, const char *mode, const char *directory) {
|
||||||
return _impl->open(path, mode);
|
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() {
|
void File::close() {
|
||||||
if (_impl) {
|
if (_impl) {
|
||||||
_impl->close();
|
_impl->close();
|
||||||
|
|
1
file.h
1
file.h
|
@ -20,6 +20,7 @@ struct File {
|
||||||
|
|
||||||
bool open(const char *filename, const char *mode, FileSystem *fs);
|
bool open(const char *filename, const char *mode, FileSystem *fs);
|
||||||
bool open(const char *filename, const char *mode, const char *directory);
|
bool open(const char *filename, const char *mode, const char *directory);
|
||||||
|
void openMemoryBuffer(int initialCapacity);
|
||||||
void close();
|
void close();
|
||||||
bool ioErr() const;
|
bool ioErr() const;
|
||||||
uint32_t size();
|
uint32_t size();
|
||||||
|
|
76
game.cpp
76
game.cpp
|
@ -25,6 +25,8 @@ Game::Game(SystemStub *stub, FileSystem *fs, const char *savePath, int level, Re
|
||||||
_demoBin = -1;
|
_demoBin = -1;
|
||||||
_widescreenMode = widescreenMode;
|
_widescreenMode = widescreenMode;
|
||||||
_autoSave = autoSave;
|
_autoSave = autoSave;
|
||||||
|
_rewindPtr = -1;
|
||||||
|
_rewindLen = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Game::run() {
|
void Game::run() {
|
||||||
|
@ -159,6 +161,7 @@ void Game::run() {
|
||||||
_vid._unkPalSlot1 = 0;
|
_vid._unkPalSlot1 = 0;
|
||||||
_vid._unkPalSlot2 = 0;
|
_vid._unkPalSlot2 = 0;
|
||||||
_score = 0;
|
_score = 0;
|
||||||
|
clearStateRewind();
|
||||||
loadLevelData();
|
loadLevelData();
|
||||||
resetGameState();
|
resetGameState();
|
||||||
_endLoop = false;
|
_endLoop = false;
|
||||||
|
@ -195,7 +198,7 @@ void Game::displayTitleScreenAmiga() {
|
||||||
if (!buf) {
|
if (!buf) {
|
||||||
error("Failed to allocate screen buffer w=%d h=%d", kW, kH);
|
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,
|
0x000, 0x123, 0x012, 0x134, 0x433, 0x453, 0x046, 0x245,
|
||||||
0x751, 0x455, 0x665, 0x268, 0x961, 0x478, 0x677, 0x786,
|
0x751, 0x455, 0x665, 0x268, 0x961, 0x478, 0x677, 0x786,
|
||||||
0x17B, 0x788, 0xB84, 0xC92, 0x49C, 0xF00, 0x9A8, 0x9AA,
|
0x17B, 0x788, 0xB84, 0xC92, 0x49C, 0xF00, 0x9A8, 0x9AA,
|
||||||
|
@ -384,11 +387,12 @@ void Game::mainLoop() {
|
||||||
playCutscene(0x41);
|
playCutscene(0x41);
|
||||||
_endLoop = true;
|
_endLoop = true;
|
||||||
} else {
|
} else {
|
||||||
if (_autoSave && loadGameState(kAutoSaveSlot)) {
|
if (_autoSave && _rewindLen != 0 && loadGameState(kAutoSaveSlot)) {
|
||||||
// autosave
|
// autosave
|
||||||
} else if (_validSaveState && loadGameState(kIngameSaveSlot)) {
|
} else if (_validSaveState && loadGameState(kIngameSaveSlot)) {
|
||||||
// ingame save
|
// ingame save
|
||||||
} else {
|
} else {
|
||||||
|
clearStateRewind();
|
||||||
loadLevelData();
|
loadLevelData();
|
||||||
resetGameState();
|
resetGameState();
|
||||||
}
|
}
|
||||||
|
@ -453,9 +457,9 @@ void Game::mainLoop() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
inp_handleSpecialKeys();
|
inp_handleSpecialKeys();
|
||||||
if (_stub->getTimeStamp() - _saveTimestamp >= kAutoSaveIntervalMs) {
|
if (_autoSave && _stub->getTimeStamp() - _saveTimestamp >= kAutoSaveIntervalMs) {
|
||||||
// do not save if we just died
|
// do not save if we died or about to
|
||||||
if (_pgeLive[0].life > 0) {
|
if (_pgeLive[0].life > 0 && _deathCutsceneCounter == 0) {
|
||||||
saveGameState(kAutoSaveSlot);
|
saveGameState(kAutoSaveSlot);
|
||||||
_saveTimestamp = _stub->getTimeStamp();
|
_saveTimestamp = _stub->getTimeStamp();
|
||||||
}
|
}
|
||||||
|
@ -594,6 +598,14 @@ void Game::inp_handleSpecialKeys() {
|
||||||
}
|
}
|
||||||
_stub->_pi.stateSlot = 0;
|
_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() {
|
void Game::drawCurrentInventoryItem() {
|
||||||
|
@ -1817,6 +1829,7 @@ uint16_t Game::getRandomNumber() {
|
||||||
|
|
||||||
void Game::changeLevel() {
|
void Game::changeLevel() {
|
||||||
_vid.fadeOut();
|
_vid.fadeOut();
|
||||||
|
clearStateRewind();
|
||||||
loadLevelData();
|
loadLevelData();
|
||||||
loadLevelMap();
|
loadLevelMap();
|
||||||
_vid.setPalette0xF();
|
_vid.setPalette0xF();
|
||||||
|
@ -1981,6 +1994,9 @@ void Game::makeGameStateName(uint8_t slot, char *buf) {
|
||||||
static const uint32_t TAG_FBSV = 0x46425356;
|
static const uint32_t TAG_FBSV = 0x46425356;
|
||||||
|
|
||||||
bool Game::saveGameState(uint8_t slot) {
|
bool Game::saveGameState(uint8_t slot) {
|
||||||
|
if (slot == kAutoSaveSlot) {
|
||||||
|
return saveStateRewind();
|
||||||
|
}
|
||||||
bool success = false;
|
bool success = false;
|
||||||
char stateFile[32];
|
char stateFile[32];
|
||||||
makeGameStateName(slot, stateFile);
|
makeGameStateName(slot, stateFile);
|
||||||
|
@ -2008,6 +2024,9 @@ bool Game::saveGameState(uint8_t slot) {
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Game::loadGameState(uint8_t slot) {
|
bool Game::loadGameState(uint8_t slot) {
|
||||||
|
if (slot == kAutoSaveSlot) {
|
||||||
|
return loadStateRewind();
|
||||||
|
}
|
||||||
bool success = false;
|
bool success = false;
|
||||||
char stateFile[32];
|
char stateFile[32];
|
||||||
makeGameStateName(slot, stateFile);
|
makeGameStateName(slot, stateFile);
|
||||||
|
@ -2177,6 +2196,53 @@ void Game::loadState(File *f) {
|
||||||
resetGameState();
|
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) {
|
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);
|
debug(DBG_GAME, "AnimBuffers::addState() stateNum=%d x=%d y=%d dataPtr=%p pge=%p", stateNum, x, y, dataPtr, pge);
|
||||||
assert(stateNum < 4);
|
assert(stateNum < 4);
|
||||||
|
|
8
game.h
8
game.h
|
@ -27,8 +27,9 @@ struct Game {
|
||||||
|
|
||||||
enum {
|
enum {
|
||||||
kIngameSaveSlot = 0,
|
kIngameSaveSlot = 0,
|
||||||
|
kRewindSize = 120, // 10mins (~2MB)
|
||||||
kAutoSaveSlot = 255,
|
kAutoSaveSlot = 255,
|
||||||
kAutoSaveIntervalMs = 120 * 1000
|
kAutoSaveIntervalMs = 5 * 1000
|
||||||
};
|
};
|
||||||
|
|
||||||
enum {
|
enum {
|
||||||
|
@ -68,6 +69,8 @@ struct Game {
|
||||||
SystemStub *_stub;
|
SystemStub *_stub;
|
||||||
FileSystem *_fs;
|
FileSystem *_fs;
|
||||||
const char *_savePath;
|
const char *_savePath;
|
||||||
|
File _rewindBuffer[kRewindSize];
|
||||||
|
int _rewindPtr, _rewindLen;
|
||||||
|
|
||||||
const uint8_t *_stringsTable;
|
const uint8_t *_stringsTable;
|
||||||
const char **_textsTable;
|
const char **_textsTable;
|
||||||
|
@ -384,6 +387,9 @@ struct Game {
|
||||||
bool loadGameState(uint8_t slot);
|
bool loadGameState(uint8_t slot);
|
||||||
void saveState(File *f);
|
void saveState(File *f);
|
||||||
void loadState(File *f);
|
void loadState(File *f);
|
||||||
|
void clearStateRewind();
|
||||||
|
bool saveStateRewind();
|
||||||
|
bool loadStateRewind();
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // GAME_H__
|
#endif // GAME_H__
|
||||||
|
|
41
main.cpp
41
main.cpp
|
@ -156,50 +156,13 @@ static void initOptions() {
|
||||||
}
|
}
|
||||||
|
|
||||||
static void parseScaler(char *name, ScalerParameters *scalerParameters) {
|
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, '@');
|
char *sep = strchr(name, '@');
|
||||||
if (sep) {
|
if (sep) {
|
||||||
*sep = 0;
|
*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);
|
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) {
|
static WidescreenMode parseWidescreen(const char *mode) {
|
||||||
|
|
3
menu.cpp
3
menu.cpp
|
@ -212,9 +212,6 @@ bool Menu::handlePasswordScreen() {
|
||||||
if (c != 0) {
|
if (c != 0) {
|
||||||
_stub->_pi.lastChar = 0;
|
_stub->_pi.lastChar = 0;
|
||||||
if (len < 6) {
|
if (len < 6) {
|
||||||
if (c >= 'a' && c <= 'z') {
|
|
||||||
c &= ~0x20;
|
|
||||||
}
|
|
||||||
if ((c >= 'A' && c <= 'Z') || (c == 0x20)) {
|
if ((c >= 'A' && c <= 'Z') || (c == 0x20)) {
|
||||||
password[len] = c;
|
password[len] = c;
|
||||||
++len;
|
++len;
|
||||||
|
|
|
@ -9,13 +9,11 @@
|
||||||
#include "systemstub.h"
|
#include "systemstub.h"
|
||||||
|
|
||||||
static uint8_t reverseBits(uint8_t ch) {
|
static uint8_t reverseBits(uint8_t ch) {
|
||||||
uint8_t r = 0;
|
static const uint8_t lut[] = {
|
||||||
for (int b = 0; b < 8; ++b) {
|
0x0, 0x8, 0x4, 0xC, 0x2, 0xA, 0x6, 0xE,
|
||||||
if (ch & (1 << b)) {
|
0x1, 0x9, 0x5, 0xD, 0x3, 0xB, 0x7, 0xF
|
||||||
r |= (1 << (7 - b));
|
};
|
||||||
}
|
return (lut[ch & 15] << 4) | lut[ch >> 4];
|
||||||
}
|
|
||||||
return r;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static uint8_t decryptChar(uint8_t ch) {
|
static uint8_t decryptChar(uint8_t ch) {
|
||||||
|
@ -114,9 +112,6 @@ bool Game::handleProtectionScreenShape() {
|
||||||
if (c != 0) {
|
if (c != 0) {
|
||||||
_stub->_pi.lastChar = 0;
|
_stub->_pi.lastChar = 0;
|
||||||
if (len < kCodeLen) {
|
if (len < kCodeLen) {
|
||||||
if (c >= 'a' && c <= 'z') {
|
|
||||||
c &= ~0x20;
|
|
||||||
}
|
|
||||||
if ((c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9')) {
|
if ((c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9')) {
|
||||||
codeText[len] = c;
|
codeText[len] = c;
|
||||||
++len;
|
++len;
|
||||||
|
@ -201,7 +196,7 @@ bool Game::handleProtectionScreenWords() {
|
||||||
const uint8_t code = getRandomNumber() % kWordsCount;
|
const uint8_t code = getRandomNumber() % kWordsCount;
|
||||||
const uint8_t *protectionData = _protectionWordData + code * 18;
|
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);
|
_vid.drawString(kSecurityCodeText, 72 + (114 - strlen(kSecurityCodeText) * 8) / 2, 158, 0xE4);
|
||||||
char buf[16];
|
char buf[16];
|
||||||
snprintf(buf, sizeof(buf), "PAGE %d", protectionData[0]);
|
snprintf(buf, sizeof(buf), "PAGE %d", protectionData[0]);
|
||||||
|
@ -229,9 +224,6 @@ bool Game::handleProtectionScreenWords() {
|
||||||
if (c != 0) {
|
if (c != 0) {
|
||||||
_stub->_pi.lastChar = 0;
|
_stub->_pi.lastChar = 0;
|
||||||
if (len < kCodeLen) {
|
if (len < kCodeLen) {
|
||||||
if (c >= 'a' && c <= 'z') {
|
|
||||||
c &= ~0x20;
|
|
||||||
}
|
|
||||||
if (c >= 'A' && c <= 'Z') {
|
if (c >= 'A' && c <= 'Z') {
|
||||||
codeText[len] = c;
|
codeText[len] = c;
|
||||||
++len;
|
++len;
|
||||||
|
|
|
@ -15,6 +15,10 @@ ResourceAba::~ResourceAba() {
|
||||||
free(_entries);
|
free(_entries);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int compareAbaEntry(const void *a, const void *b) {
|
||||||
|
return strcasecmp(((ResourceAbaEntry *)a)->name, ((ResourceAbaEntry *)b)->name);
|
||||||
|
}
|
||||||
|
|
||||||
void ResourceAba::readEntries() {
|
void ResourceAba::readEntries() {
|
||||||
if (_f.open(FILENAME, "rb", _fs)) {
|
if (_f.open(FILENAME, "rb", _fs)) {
|
||||||
_entriesCount = _f.readUint16BE();
|
_entriesCount = _f.readUint16BE();
|
||||||
|
@ -39,16 +43,14 @@ void ResourceAba::readEntries() {
|
||||||
}
|
}
|
||||||
nextOffset = _entries[i].offset + _entries[i].compressedSize;
|
nextOffset = _entries[i].offset + _entries[i].compressedSize;
|
||||||
}
|
}
|
||||||
|
qsort(_entries, _entriesCount, sizeof(ResourceAbaEntry), compareAbaEntry);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const ResourceAbaEntry *ResourceAba::findEntry(const char *name) const {
|
const ResourceAbaEntry *ResourceAba::findEntry(const char *name) const {
|
||||||
for (int i = 0; i < _entriesCount; ++i) {
|
ResourceAbaEntry tmp;
|
||||||
if (strcasecmp(_entries[i].name, name) == 0) {
|
strcpy(tmp.name, name);
|
||||||
return &_entries[i];
|
return (const ResourceAbaEntry *)bsearch(&tmp, _entries, _entriesCount, sizeof(ResourceAbaEntry), compareAbaEntry);
|
||||||
}
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
uint8_t *ResourceAba::loadEntry(const char *name, uint32_t *size) {
|
uint8_t *ResourceAba::loadEntry(const char *name, uint32_t *size) {
|
||||||
|
|
15
scaler.cpp
15
scaler.cpp
|
@ -5,7 +5,6 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "scaler.h"
|
#include "scaler.h"
|
||||||
#include "dynlib.h"
|
|
||||||
#include "util.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) {
|
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,
|
2, 4,
|
||||||
scaleNx,
|
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;
|
|
||||||
}
|
|
||||||
|
|
|
@ -24,6 +24,7 @@ void SeqDemuxer::close() {
|
||||||
for (int i = 0; i < kBuffersCount; ++i) {
|
for (int i = 0; i < kBuffersCount; ++i) {
|
||||||
free(_buffers[i].data);
|
free(_buffers[i].data);
|
||||||
}
|
}
|
||||||
|
memset(_buffers, 0, sizeof(_buffers));
|
||||||
}
|
}
|
||||||
|
|
||||||
bool SeqDemuxer::readHeader() {
|
bool SeqDemuxer::readHeader() {
|
||||||
|
|
|
@ -17,7 +17,7 @@ const Cutscene::OpcodeStub Cutscene::_opcodeTable[] = {
|
||||||
/* 0x04 */
|
/* 0x04 */
|
||||||
&Cutscene::op_setPalette,
|
&Cutscene::op_setPalette,
|
||||||
&Cutscene::op_markCurPos,
|
&Cutscene::op_markCurPos,
|
||||||
&Cutscene::op_drawStringAtBottom,
|
&Cutscene::op_drawCaptionText,
|
||||||
&Cutscene::op_nop,
|
&Cutscene::op_nop,
|
||||||
/* 0x08 */
|
/* 0x08 */
|
||||||
&Cutscene::op_skip3,
|
&Cutscene::op_skip3,
|
||||||
|
|
|
@ -35,6 +35,7 @@ struct PlayerInput {
|
||||||
bool save;
|
bool save;
|
||||||
bool load;
|
bool load;
|
||||||
int stateSlot;
|
int stateSlot;
|
||||||
|
bool rewind;
|
||||||
|
|
||||||
uint8_t dbgMask;
|
uint8_t dbgMask;
|
||||||
bool quit;
|
bool quit;
|
||||||
|
@ -42,7 +43,7 @@ struct PlayerInput {
|
||||||
|
|
||||||
struct ScalerParameters {
|
struct ScalerParameters {
|
||||||
ScalerType type;
|
ScalerType type;
|
||||||
const Scaler *scaler;
|
char name[32];
|
||||||
int factor;
|
int factor;
|
||||||
|
|
||||||
static ScalerParameters defaults();
|
static ScalerParameters defaults();
|
||||||
|
@ -55,7 +56,7 @@ struct SystemStub {
|
||||||
|
|
||||||
virtual ~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 void destroy() = 0;
|
||||||
|
|
||||||
virtual bool hasWidescreen() const = 0;
|
virtual bool hasWidescreen() const = 0;
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* REminiscence - Flashback interpreter
|
* REminiscence - Flashback interpreter
|
||||||
* Copyright (C) 2005-2019 Gregory Montoir (cyx@users.sourceforge.net)
|
* 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 ScalerParameters::defaults() {
|
||||||
ScalerParameters params;
|
ScalerParameters params;
|
||||||
params.type = kScalerTypeInternal;
|
params.type = kScalerTypeInternal;
|
||||||
params.scaler = &_internalScaler;
|
params.name[0] = 0;
|
||||||
params.factor = _internalScaler.factorMin + (_internalScaler.factorMax - _internalScaler.factorMin) / 2;
|
params.factor = _internalScaler.factorMin + (_internalScaler.factorMax - _internalScaler.factorMin) / 2;
|
||||||
return params;
|
return params;
|
||||||
}
|
}
|
||||||
|
@ -47,15 +46,16 @@ struct SystemStub_SDL : SystemStub {
|
||||||
void *_audioCbData;
|
void *_audioCbData;
|
||||||
int _screenshot;
|
int _screenshot;
|
||||||
ScalerType _scalerType;
|
ScalerType _scalerType;
|
||||||
const Scaler *_scaler;
|
|
||||||
int _scaleFactor;
|
int _scaleFactor;
|
||||||
|
const Scaler *_scaler;
|
||||||
|
void *_scalerSo;
|
||||||
int _widescreenMode;
|
int _widescreenMode;
|
||||||
SDL_Texture *_widescreenTexture;
|
SDL_Texture *_widescreenTexture;
|
||||||
int _wideMargin;
|
int _wideMargin;
|
||||||
bool _enableWidescreen;
|
bool _enableWidescreen;
|
||||||
|
|
||||||
virtual ~SystemStub_SDL() {}
|
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 void destroy();
|
||||||
virtual bool hasWidescreen() const;
|
virtual bool hasWidescreen() const;
|
||||||
virtual void setScreenSize(int w, int h);
|
virtual void setScreenSize(int w, int h);
|
||||||
|
@ -88,7 +88,8 @@ struct SystemStub_SDL : SystemStub {
|
||||||
void prepareGraphics();
|
void prepareGraphics();
|
||||||
void cleanupGraphics();
|
void cleanupGraphics();
|
||||||
void changeGraphics(bool fullscreen, int scaleFactor);
|
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);
|
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();
|
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_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_JOYSTICK);
|
||||||
SDL_ShowCursor(SDL_DISABLE);
|
SDL_ShowCursor(SDL_DISABLE);
|
||||||
_caption = title;
|
_caption = title;
|
||||||
|
@ -108,9 +109,13 @@ void SystemStub_SDL::init(const char *title, int w, int h, bool fullscreen, int
|
||||||
_screenBuffer = 0;
|
_screenBuffer = 0;
|
||||||
_fadeOnUpdateScreen = false;
|
_fadeOnUpdateScreen = false;
|
||||||
_fullscreen = fullscreen;
|
_fullscreen = fullscreen;
|
||||||
_scalerType = scalerParameters->type;
|
_scalerType = kScalerTypeInternal;
|
||||||
_scaler = scalerParameters->scaler;
|
_scaleFactor = 1;
|
||||||
_scaleFactor = _scaler ? CLIP(scalerParameters->factor, _scaler->factorMin, _scaler->factorMax) : 1;
|
_scaler = 0;
|
||||||
|
_scalerSo = 0;
|
||||||
|
if (scalerParameters->name[0]) {
|
||||||
|
setScaler(scalerParameters);
|
||||||
|
}
|
||||||
memset(_rgbPalette, 0, sizeof(_rgbPalette));
|
memset(_rgbPalette, 0, sizeof(_rgbPalette));
|
||||||
memset(_darkPalette, 0, sizeof(_darkPalette));
|
memset(_darkPalette, 0, sizeof(_darkPalette));
|
||||||
_screenW = _screenH = 0;
|
_screenW = _screenH = 0;
|
||||||
|
@ -143,6 +148,10 @@ void SystemStub_SDL::destroy() {
|
||||||
SDL_FreeFormat(_fmt);
|
SDL_FreeFormat(_fmt);
|
||||||
_fmt = 0;
|
_fmt = 0;
|
||||||
}
|
}
|
||||||
|
if (_scalerSo) {
|
||||||
|
SDL_UnloadObject(_scalerSo);
|
||||||
|
_scalerSo = 0;
|
||||||
|
}
|
||||||
if (_controller) {
|
if (_controller) {
|
||||||
SDL_GameControllerClose(_controller);
|
SDL_GameControllerClose(_controller);
|
||||||
_controller = 0;
|
_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 void blur_h(int radius, const uint32_t *src, int srcPitch, int w, int h, const SDL_PixelFormat *fmt, uint32_t *dst, int dstPitch) {
|
||||||
static const uint8_t blurMat[3 * 3] = {
|
|
||||||
2, 4, 2,
|
|
||||||
4, 8, 4,
|
|
||||||
2, 4, 2
|
|
||||||
};
|
|
||||||
static const int blurMatSigma = 32 * 2;
|
|
||||||
|
|
||||||
const uint32_t redBlueMask = fmt->Rmask | fmt->Bmask;
|
const int count = 2 * radius + 1;
|
||||||
const uint32_t greenMask = fmt->Gmask;
|
|
||||||
|
|
||||||
uint32_t redBlueBlurSum = 0;
|
for (int y = 0; y < h; ++y) {
|
||||||
uint32_t greenBlurSum = 0;
|
|
||||||
|
|
||||||
for (int v = 0; v < 3; ++v) {
|
uint32_t r = 0;
|
||||||
const int ym = CLIP(y + v - 1, 0, h - 1);
|
uint32_t g = 0;
|
||||||
for (int u = 0; u < 3; ++u) {
|
uint32_t b = 0;
|
||||||
const int xm = CLIP(x + u - 1, 0, w - 1);
|
|
||||||
const uint32_t color = pal[src[ym * pitch + xm]];
|
uint32_t color;
|
||||||
const int mul = blurMat[v * 3 + u];
|
|
||||||
redBlueBlurSum += (color & redBlueMask) * mul;
|
for (int x = -radius; x <= radius; ++x) {
|
||||||
greenBlurSum += (color & greenMask) * mul;
|
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) {
|
void SystemStub_SDL::copyWidescreenBlur(int w, int h, const uint8_t *buf) {
|
||||||
assert(w == _screenW && h == _screenH);
|
assert(w == _screenW && h == _screenH);
|
||||||
void *dst = 0;
|
void *ptr = 0;
|
||||||
int pitch = 0;
|
int pitch = 0;
|
||||||
if (SDL_LockTexture(_widescreenTexture, 0, &dst, &pitch) == 0) {
|
if (SDL_LockTexture(_widescreenTexture, 0, &ptr, &pitch) == 0) {
|
||||||
assert((pitch & 3) == 0);
|
assert((pitch & 3) == 0);
|
||||||
uint32_t *p = (uint32_t *)dst;
|
|
||||||
for (int y = 0; y < h; ++y) {
|
uint32_t *src = (uint32_t *)malloc(w * h * sizeof(uint32_t));
|
||||||
for (int x = 0; x < w; ++x) {
|
uint32_t *tmp = (uint32_t *)malloc(w * h * sizeof(uint32_t));
|
||||||
p[x] = blurPixel(x, y, buf, _rgbPalette, w, w, h, _fmt);
|
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);
|
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) {
|
void SystemStub_SDL::processEvent(const SDL_Event &ev, bool &paused) {
|
||||||
switch (ev.type) {
|
switch (ev.type) {
|
||||||
case SDL_QUIT:
|
case SDL_QUIT:
|
||||||
|
@ -681,6 +768,9 @@ void SystemStub_SDL::processEvent(const SDL_Event &ev, bool &paused) {
|
||||||
case SDLK_l:
|
case SDLK_l:
|
||||||
_pi.load = true;
|
_pi.load = true;
|
||||||
break;
|
break;
|
||||||
|
case SDLK_r:
|
||||||
|
_pi.rewind = true;
|
||||||
|
break;
|
||||||
case SDLK_KP_PLUS:
|
case SDLK_KP_PLUS:
|
||||||
case SDLK_PAGEUP:
|
case SDLK_PAGEUP:
|
||||||
_pi.stateSlot = 1;
|
_pi.stateSlot = 1;
|
||||||
|
@ -692,7 +782,7 @@ void SystemStub_SDL::processEvent(const SDL_Event &ev, bool &paused) {
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
_pi.lastChar = ev.key.keysym.sym;
|
setAsciiChar(_pi, &ev.key.keysym);
|
||||||
switch (ev.key.keysym.sym) {
|
switch (ev.key.keysym.sym) {
|
||||||
case SDLK_LEFT:
|
case SDLK_LEFT:
|
||||||
_pi.dirMask &= ~PlayerInput::DIR_LEFT;
|
_pi.dirMask &= ~PlayerInput::DIR_LEFT;
|
||||||
|
@ -917,39 +1007,97 @@ void SystemStub_SDL::changeGraphics(bool fullscreen, int scaleFactor) {
|
||||||
prepareGraphics();
|
prepareGraphics();
|
||||||
}
|
}
|
||||||
|
|
||||||
void SystemStub_SDL::changeScaler(int scaler) {
|
void SystemStub_SDL::setScaler(const ScalerParameters *parameters) {
|
||||||
ScalerParameters scalerParameters = ScalerParameters::defaults();
|
static const struct {
|
||||||
switch (scaler) {
|
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:
|
case 0:
|
||||||
scalerParameters.type = kScalerTypePoint;
|
type = kScalerTypePoint;
|
||||||
break;
|
break;
|
||||||
case 1:
|
case 1:
|
||||||
scalerParameters.type = kScalerTypeLinear;
|
type = kScalerTypeLinear;
|
||||||
break;
|
break;
|
||||||
case 2:
|
case 2:
|
||||||
scalerParameters.type = kScalerTypeInternal;
|
type = kScalerTypeInternal;
|
||||||
scalerParameters.scaler = &_internalScaler;
|
scaler = &_internalScaler;
|
||||||
break;
|
break;
|
||||||
#ifdef USE_STATIC_SCALER
|
#ifdef USE_STATIC_SCALER
|
||||||
case 3:
|
case 3:
|
||||||
scalerParameters.type = kScalerTypeInternal;
|
type = kScalerTypeInternal;
|
||||||
scalerParameters.scaler = &scaler_nearest;
|
scaler = &scaler_nearest;
|
||||||
break;
|
break;
|
||||||
case 4:
|
case 4:
|
||||||
scalerParameters.type = kScalerTypeInternal;
|
type = kScalerTypeInternal;
|
||||||
scalerParameters.scaler = &scaler_tv2x;
|
scaler = &scaler_tv2x;
|
||||||
break;
|
break;
|
||||||
case 5:
|
case 5:
|
||||||
scalerParameters.type = kScalerTypeInternal;
|
type = kScalerTypeInternal;
|
||||||
scalerParameters.scaler = &scaler_xbr;
|
scaler = &scaler_xbr;
|
||||||
break;
|
break;
|
||||||
#endif
|
#endif
|
||||||
default:
|
default:
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (_scalerType != scalerParameters.type || scalerParameters.scaler != _scaler) {
|
if (_scalerType != type || scaler != _scaler) {
|
||||||
_scalerType = scalerParameters.type;
|
_scalerType = type;
|
||||||
_scaler = scalerParameters.scaler;
|
_scaler = scaler;
|
||||||
if (_scalerType == kScalerTypeInternal || _scalerType == kScalerTypeExternal) {
|
if (_scalerType == kScalerTypeInternal || _scalerType == kScalerTypeExternal) {
|
||||||
_scaleFactor = CLIP(_scaleFactor, _scaler->factorMin, _scaler->factorMax);
|
_scaleFactor = CLIP(_scaleFactor, _scaler->factorMin, _scaler->factorMax);
|
||||||
} else {
|
} else {
|
||||||
|
|
64
unpack.cpp
64
unpack.cpp
|
@ -16,52 +16,48 @@ struct UnpackCtx {
|
||||||
};
|
};
|
||||||
|
|
||||||
static bool nextBit(UnpackCtx *uc) {
|
static bool nextBit(UnpackCtx *uc) {
|
||||||
bool carry = (uc->bits & 1) != 0;
|
bool bit = (uc->bits & 1) != 0;
|
||||||
uc->bits >>= 1;
|
uc->bits >>= 1;
|
||||||
if (uc->bits == 0) { // getnextlwd
|
if (uc->bits == 0) { // getnextlwd
|
||||||
uc->bits = READ_BE_UINT32(uc->src); uc->src -= 4;
|
const uint32_t bits = READ_BE_UINT32(uc->src); uc->src -= 4;
|
||||||
uc->crc ^= uc->bits;
|
uc->crc ^= bits;
|
||||||
carry = (uc->bits & 1) != 0;
|
bit = (bits & 1) != 0;
|
||||||
uc->bits = (1 << 31) | (uc->bits >> 1);
|
uc->bits = (1 << 31) | (bits >> 1);
|
||||||
}
|
}
|
||||||
return carry;
|
return bit;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int getBits(UnpackCtx *uc, int count) { // rdd1bits
|
template<int count>
|
||||||
int bits = 0;
|
static uint32_t getBits(UnpackCtx *uc) { // rdd1bits
|
||||||
|
uint32_t bits = 0;
|
||||||
for (int i = 0; i < count; ++i) {
|
for (int i = 0; i < count; ++i) {
|
||||||
bits <<= 1;
|
bits |= (nextBit(uc) ? 1 : 0) << (count - 1 - i);
|
||||||
if (nextBit(uc)) {
|
|
||||||
bits |= 1;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return bits;
|
return bits;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void copyLiteral(UnpackCtx *uc, int bitsCount, int len) { // getd3chr
|
static void copyLiteral(UnpackCtx *uc, int len) { // getd3chr
|
||||||
int count = getBits(uc, bitsCount) + len + 1;
|
uc->size -= len;
|
||||||
uc->size -= count;
|
|
||||||
if (uc->size < 0) {
|
if (uc->size < 0) {
|
||||||
count += uc->size;
|
len += uc->size;
|
||||||
uc->size = 0;
|
uc->size = 0;
|
||||||
}
|
}
|
||||||
for (int i = 0; i < count; ++i) {
|
for (int i = 0; i < len; ++i) {
|
||||||
*(uc->dst - i) = (uint8_t)getBits(uc, 8);
|
*(uc->dst - i) = (uint8_t)getBits<8>(uc);
|
||||||
}
|
}
|
||||||
uc->dst -= count;
|
uc->dst -= len;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void copyReference(UnpackCtx *uc, int bitsCount, int count) { // copyd3bytes
|
static void copyReference(UnpackCtx *uc, int len, int offset) { // copyd3bytes
|
||||||
uc->size -= count;
|
uc->size -= len;
|
||||||
if (uc->size < 0) {
|
if (uc->size < 0) {
|
||||||
count += uc->size;
|
len += uc->size;
|
||||||
uc->size = 0;
|
uc->size = 0;
|
||||||
}
|
}
|
||||||
const int offset = getBits(uc, bitsCount);
|
for (int i = 0; i < len; ++i) {
|
||||||
for (int i = 0; i < count; ++i) {
|
|
||||||
*(uc->dst - i) = *(uc->dst - i + offset);
|
*(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) {
|
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 {
|
do {
|
||||||
if (!nextBit(&uc)) {
|
if (!nextBit(&uc)) {
|
||||||
if (!nextBit(&uc)) {
|
if (!nextBit(&uc)) {
|
||||||
copyLiteral(&uc, 3, 0);
|
copyLiteral(&uc, getBits<3>(&uc) + 1);
|
||||||
} else {
|
} else {
|
||||||
copyReference(&uc, 8, 2);
|
copyReference(&uc, 2, getBits<8>(&uc));
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
const int code = getBits(&uc, 2);
|
const int code = getBits<2>(&uc);
|
||||||
switch (code) {
|
switch (code) {
|
||||||
case 3:
|
case 3:
|
||||||
copyLiteral(&uc, 8, 8);
|
copyLiteral(&uc, getBits<8>(&uc) + 9);
|
||||||
break;
|
break;
|
||||||
case 2:
|
case 2: {
|
||||||
copyReference(&uc, 12, getBits(&uc, 8) + 1);
|
const int len = getBits<8>(&uc) + 1;
|
||||||
|
copyReference(&uc, len, getBits<12>(&uc));
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case 1:
|
case 1:
|
||||||
copyReference(&uc, 10, 4);
|
copyReference(&uc, 4, getBits<10>(&uc));
|
||||||
break;
|
break;
|
||||||
case 0:
|
case 0:
|
||||||
copyReference(&uc, 9, 3);
|
copyReference(&uc, 3, getBits<9>(&uc));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue