Import 0.4.3

This commit is contained in:
Gregory Montoir 2018-03-29 00:00:00 +08:00
parent a4c4ed8fef
commit 514785d0a1
21 changed files with 390 additions and 123 deletions

View File

@ -7,15 +7,17 @@ MODPLUG_LIBS := -lmodplug
TREMOR_LIBS := -lvorbisidec -logg
ZLIB_LIBS := -lz
CXXFLAGS += -O2 -Wall -MMD $(SDL_CFLAGS) -DUSE_MODPLUG -DUSE_TREMOR -DUSE_ZLIB
CXXFLAGS += -O2 -Wall -MMD $(SDL_CFLAGS) -DUSE_MODPLUG -DUSE_STATIC_SCALER -DUSE_TREMOR -DUSE_ZLIB
SRCS = collision.cpp cutscene.cpp decode_mac.cpp dynlib.cpp file.cpp fs.cpp game.cpp graphics.cpp main.cpp \
menu.cpp mixer.cpp mod_player.cpp ogg_player.cpp piege.cpp resource.cpp resource_aba.cpp \
resource_mac.cpp scaler.cpp screenshot.cpp seq_player.cpp \
sfx_player.cpp staticres.cpp systemstub_sdl.cpp unpack.cpp util.cpp video.cpp
OBJS = $(SRCS:.cpp=.o)
DEPS = $(SRCS:.cpp=.d)
SCALERS := scalers/scaler_nearest.cpp scalers/scaler_tv2x.cpp scalers/scaler_xbrz.cpp scalers/xbrz/xbrz.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)

View File

@ -1,6 +1,6 @@
REminiscence README
Release version: 0.4.2
Release version: 0.4.3
-------------------------------------------------------------------------------
@ -43,7 +43,7 @@ These paths can be changed using command line switches :
--savepath=PATH Path to save files (default '.')
--levelnum=NUM Level to start from (default '0')
--fullscreen Fullscreen display
--widescreen 16:9 display
--widescreen=MODE 16:9 display
--scaler=NAME@X Graphics scaler (default 'scale@3')
--language=LANG Language (fr,en,de,sp,it,jp)
@ -52,6 +52,10 @@ 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).
The widescreen option accepts two modes :
'adjacent' : left and right rooms bitmaps will be drawn (default)
'mirror' : the current room bitmap will be drawn mirrored
In-game hotkeys :
Arrow Keys move Conrad
@ -60,11 +64,12 @@ In-game hotkeys :
Escape display the options
Backspace display the inventory
Alt Enter toggle windowed/fullscreen mode
Alt + and - change video scaler
Alt + and - increase or decrease game screen scaler factor
Alt S write screenshot as .tga
Ctrl S save game state
Ctrl L load game state
Ctrl + and - change game state slot
Function Keys change game screen scaler
Debug hotkeys :

View File

@ -938,7 +938,12 @@ void Cutscene::op_handleKeys() {
_cmdPtr = getCommandData();
n = READ_BE_UINT16(_cmdPtr + n * 2 + 2);
}
_cmdPtr = _cmdPtrBak = getCommandData() + n + _startOffset;
if (_res->isMac()) {
_cmdPtr = getCommandData();
_baseOffset = READ_BE_UINT16(_cmdPtr + 2 + n * 2);
n = 0;
}
_cmdPtr = _cmdPtrBak = getCommandData() + n + _baseOffset;
}
uint8_t Cutscene::fetchNextCmdByte() {
@ -951,7 +956,7 @@ uint16_t Cutscene::fetchNextCmdWord() {
return i;
}
void Cutscene::mainLoop(uint16_t offset) {
void Cutscene::mainLoop(uint16_t num) {
_frameDelay = 5;
_tstamp = _stub->getTimeStamp();
@ -963,14 +968,20 @@ void Cutscene::mainLoop(uint16_t offset) {
_newPal = false;
_hasAlphaColor = false;
const uint8_t *p = getCommandData();
if (offset != 0) {
offset = READ_BE_UINT16(p + (offset + 1) * 2);
int offset = 0;
if (_res->isMac()) {
// const int count = READ_BE_UINT16(p);
_baseOffset = READ_BE_UINT16(p + 2 + num * 2);
} else {
if (num != 0) {
offset = READ_BE_UINT16(p + 2 + num * 2);
}
_baseOffset = (READ_BE_UINT16(p) + 1) * 2;
}
_startOffset = (READ_BE_UINT16(p) + 1) * 2;
_varKey = 0;
_cmdPtr = _cmdPtrBak = p + _startOffset + offset;
_cmdPtr = _cmdPtrBak = p + _baseOffset + offset;
_polPtr = getPolygonData();
debug(DBG_CUT, "_startOffset = %d offset = %d", _startOffset, offset);
debug(DBG_CUT, "_baseOffset = %d offset = %d", _baseOffset, offset);
while (!_stub->_pi.quit && !_interrupted && !_stop) {
uint8_t op = fetchNextCmdByte();

View File

@ -71,7 +71,7 @@ struct Cutscene {
uint8_t _frameDelay;
bool _newPal;
uint8_t _palBuf[0x20 * 2];
uint16_t _startOffset;
uint16_t _baseOffset;
bool _creditsSequence;
uint32_t _rotMat[4];
uint8_t _primitiveColor;
@ -139,7 +139,7 @@ struct Cutscene {
uint8_t fetchNextCmdByte();
uint16_t fetchNextCmdWord();
void mainLoop(uint16_t offset);
void mainLoop(uint16_t num);
bool load(uint16_t cutName);
void unload();
void prepare();

102
game.cpp
View File

@ -14,7 +14,7 @@
#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, WidescreenMode widescreenMode)
: _cut(&_res, stub, &_vid), _menu(&_res, stub, &_vid),
_mix(fs, stub), _res(fs, ver, lang), _seq(stub, &_mix), _vid(&_res, stub),
_stub(stub), _fs(fs), _savePath(savePath) {
@ -23,6 +23,7 @@ Game::Game(SystemStub *stub, FileSystem *fs, const char *savePath, int level, Re
_skillLevel = _menu._skill = kSkillNormal;
_currentLevel = _menu._level = level;
_demoBin = -1;
_widescreenMode = widescreenMode;
}
void Game::run() {
@ -63,6 +64,12 @@ void Game::run() {
_mix.init();
_mix._mod._isAmiga = _res.isAmiga();
if (_res.isMac()) {
displayTitleScreenMac(Menu::kMacTitleScreen_MacPlay);
if (!_stub->_pi.quit) {
displayTitleScreenMac(Menu::kMacTitleScreen_Presage);
}
}
playCutscene(0x40);
playCutscene(0x0D);
@ -86,7 +93,7 @@ void Game::run() {
break;
}
bool presentMenu = (_res._type == kResourceTypeAmiga) || (_res._type == kResourceTypeDOS && _res.fileExists("MENU1.MAP"));
bool presentMenu = ((_res._type != kResourceTypeDOS) || _res.fileExists("MENU1.MAP"));
while (!_stub->_pi.quit) {
if (presentMenu) {
_mix.playMusic(1);
@ -121,7 +128,7 @@ void Game::run() {
_stub->setScreenSize(Video::GAMESCREEN_W, Video::GAMESCREEN_H);
break;
case kResourceTypeMac:
// TODO:
displayTitleScreenMac(Menu::kMacTitleScreen_Flashback);
break;
}
if (_stub->_pi.quit) {
@ -172,10 +179,10 @@ void Game::run() {
void Game::displayTitleScreenAmiga() {
static const char *FILENAME = "present.cmp";
_res.load_CMP_menu(FILENAME, _res._scratchBuffer);
_res.load_CMP_menu(FILENAME);
static const int kW = 320;
static const int kH = 224;
uint8_t *buf = (uint8_t *)calloc(kW * kH, 1);
uint8_t *buf = (uint8_t *)calloc(1, kW * kH);
if (!buf) {
error("Failed to allocate screen buffer w=%d h=%d", kW, kH);
}
@ -190,13 +197,13 @@ void Game::displayTitleScreenAmiga() {
_stub->setPaletteEntry(i, &c);
}
_stub->setScreenSize(kW, kH);
// fill with black
_stub->copyRect(0, 0, kW, kH, buf, kW);
_stub->updateScreen(0);
_vid.AMIGA_decodeCmp(_res._scratchBuffer + 6, buf);
free(buf);
int h = 0;
while (1) {
if (h < kH / 2) {
if (h <= kH / 2) {
const int y = kH / 2 - h;
_stub->copyRect(0, y, kW, h * 2, buf, kW);
_stub->updateScreen(0);
@ -212,6 +219,72 @@ void Game::displayTitleScreenAmiga() {
}
_stub->sleep(30);
}
free(buf);
}
void Game::displayTitleScreenMac(int num) {
const int w = 512;
int h = 384;
int clutBaseColor = 0;
switch (num) {
case Menu::kMacTitleScreen_MacPlay:
break;
case Menu::kMacTitleScreen_Presage:
clutBaseColor = 12;
break;
case Menu::kMacTitleScreen_Flashback:
case Menu::kMacTitleScreen_LeftEye:
case Menu::kMacTitleScreen_RightEye:
h = 448;
break;
case Menu::kMacTitleScreen_Controls:
break;
}
DecodeBuffer buf;
memset(&buf, 0, sizeof(buf));
buf.ptr = _vid._frontLayer;
buf.pitch = buf.w = _vid._w;
buf.h = _vid._h;
buf.x = (_vid._w - w) / 2;
buf.y = (_vid._h - h) / 2;
buf.setPixel = Video::MAC_drawBuffer;
memset(_vid._frontLayer, 0, _vid._layerSize);
_res.MAC_loadTitleImage(num, &buf);
for (int i = 0; i < 12; ++i) {
Color palette[16];
_res.MAC_copyClut16(palette, 0, clutBaseColor + i);
const int basePaletteColor = i * 16;
for (int j = 0; j < 16; ++j) {
_stub->setPaletteEntry(basePaletteColor + j, &palette[j]);
}
}
if (num == Menu::kMacTitleScreen_MacPlay) {
Color palette[16];
_res.MAC_copyClut16(palette, 0, 56);
for (int i = 12; i < 16; ++i) {
const int basePaletteColor = i * 16;
for (int j = 0; j < 16; ++j) {
_stub->setPaletteEntry(basePaletteColor + j, &palette[j]);
}
}
} else if (num == Menu::kMacTitleScreen_Presage) {
Color c;
c.r = c.g = c.b = 0;
_stub->setPaletteEntry(0, &c);
}
_stub->copyRect(0, 0, _vid._w, _vid._h, _vid._frontLayer, _vid._w);
_stub->updateScreen(0);
while (1) {
_stub->processEvents();
if (_stub->_pi.quit) {
break;
}
if (_stub->_pi.enter) {
_stub->_pi.enter = false;
break;
}
_stub->sleep(30);
}
}
void Game::resetGameState() {
@ -496,8 +569,12 @@ bool Game::handleConfigPanel() {
switch (_res._type) {
case kResourceTypeAmiga:
// TODO
return true;
for (int i = 0; i < h; ++i) {
for (int j = 0; j < w; ++j) {
_vid.fillRect(Video::CHAR_W * (x + j), Video::CHAR_H * (y + i), Video::CHAR_W, Video::CHAR_H, 0xE2);
}
}
break;
case kResourceTypeDOS:
// top-left rounded corner
_vid.PC_drawChar(0x81, y, x, kUseDefaultFont);
@ -543,7 +620,7 @@ bool Game::handleConfigPanel() {
_vid.MAC_drawStringChar(_vid._frontLayer, _vid._w, Video::CHAR_W * x, Video::CHAR_H * (y + i), _res._fnt, _vid._charFrontColor, 0x86);
_vid.MAC_drawStringChar(_vid._frontLayer, _vid._w, Video::CHAR_W * (x + w), Video::CHAR_H * (y + i), _res._fnt, _vid._charFrontColor, 0x87);
for (int j = 1; j < w; ++j) {
_vid.MAC_fillRect(Video::CHAR_W * (x + j), Video::CHAR_H * (y + i), Video::CHAR_W, Video::CHAR_H, 0xE2);
_vid.fillRect(Video::CHAR_W * (x + j), Video::CHAR_H * (y + i), Video::CHAR_W, Video::CHAR_H, 0xE2);
}
}
break;
@ -1523,7 +1600,7 @@ void Game::loadLevelMap() {
_vid.AMIGA_decodeLev(_currentLevel, _currentRoom);
break;
case kResourceTypeDOS:
if (_stub->hasWidescreen()) { // draw adjacent rooms
if (_stub->hasWidescreen() && _widescreenMode == kWidescreenAdjacentRooms) {
const int leftRoom = _res._ctData[CT_LEFT_ROOM + _currentRoom];
if (leftRoom > 0 && hasLevelMap(_currentLevel, leftRoom)) {
_vid.PC_decodeMap(_currentLevel, leftRoom);
@ -1540,6 +1617,9 @@ void Game::loadLevelMap() {
}
}
_vid.PC_decodeMap(_currentLevel, _currentRoom);
if (_stub->hasWidescreen() && _widescreenMode == kWidescreenMirrorRoom) {
_stub->copyRectMirrorBorders(Video::GAMESCREEN_W, Video::GAMESCREEN_H, _vid._backLayer);
}
break;
case kResourceTypeMac:
_vid.MAC_decodeMap(_currentLevel, _currentRoom);

4
game.h
View File

@ -86,11 +86,13 @@ struct Game {
bool _saveStateCompleted;
bool _endLoop;
uint32_t _frameTimestamp;
WidescreenMode _widescreenMode;
Game(SystemStub *, FileSystem *, const char *savePath, int level, ResourceType ver, Language lang);
Game(SystemStub *, FileSystem *, const char *savePath, int level, ResourceType ver, Language lang, WidescreenMode widescreenMode);
void run();
void displayTitleScreenAmiga();
void displayTitleScreenMac(int num);
void resetGameState();
void mainLoop();
void updateTiming();

View File

@ -100,6 +100,12 @@ enum Skill {
kSkillExpert,
};
enum WidescreenMode {
kWidescreenNone,
kWidescreenAdjacentRooms,
kWidescreenMirrorRoom,
};
struct Options {
bool bypass_protection;
bool enable_password_menu;

View File

@ -22,7 +22,7 @@ static const char *USAGE =
" --savepath=PATH Path to save files (default '.')\n"
" --levelnum=NUM Start to level, bypass introduction\n"
" --fullscreen Fullscreen display\n"
" --widescreen 16:9 display\n"
" --widescreen=MODE 16:9 display\n"
" --scaler=NAME@X Graphics scaler (default 'scale@3')\n"
" --language=LANG Language (fr,en,de,sp,it,jp)\n"
;
@ -74,6 +74,7 @@ static Language detectLanguage(FileSystem *fs) {
return table[i].language;
}
}
warning("Unable to detect language, defaults to English");
return LANG_EN;
}
@ -126,12 +127,17 @@ static void initOptions() {
}
if (*p) {
const bool value = (*p == 't' || *p == 'T' || *p == '1');
bool foundOption = false;
for (int i = 0; opts[i].name; ++i) {
if (strncmp(buf, opts[i].name, strlen(opts[i].name)) == 0) {
*opts[i].value = value;
foundOption = true;
break;
}
}
if (!foundOption) {
warning("Unhandled option '%s', ignoring", buf);
}
}
}
}
@ -143,10 +149,16 @@ static void parseScaler(char *name, ScalerParameters *scalerParameters) {
struct {
const char *name;
int type;
const Scaler *scaler;
} scalers[] = {
{ "point", kScalerTypePoint },
{ "linear", kScalerTypeLinear },
{ "scale", kScalerTypeInternal },
{ "point", kScalerTypePoint, 0 },
{ "linear", kScalerTypeLinear, 0 },
{ "scale", kScalerTypeInternal, &_internalScaler },
#ifdef USE_STATIC_SCALER
{ "nearest", kScalerTypeInternal, &scaler_nearest },
{ "tv2x", kScalerTypeInternal, &scaler_tv2x },
{ "xbrz", kScalerTypeInternal, &scaler_xbrz },
#endif
{ 0, -1 }
};
bool found = false;
@ -157,6 +169,7 @@ static void parseScaler(char *name, ScalerParameters *scalerParameters) {
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;
}
@ -179,12 +192,30 @@ static void parseScaler(char *name, ScalerParameters *scalerParameters) {
}
}
static WidescreenMode parseWidescreen(const char *mode) {
static const struct {
const char *name;
WidescreenMode mode;
} modes[] = {
{ "adjacent", kWidescreenAdjacentRooms },
{ "mirror", kWidescreenMirrorRoom },
{ 0, kWidescreenNone },
};
for (int i = 0; modes[i].name; ++i) {
if (strcasecmp(modes[i].name, mode) == 0) {
return modes[i].mode;
}
}
warning("Unhandled widecreen mode '%s', defaults to adjacent rooms", mode);
return kWidescreenAdjacentRooms; // default value
}
int main(int argc, char *argv[]) {
const char *dataPath = "DATA";
const char *savePath = ".";
int levelNum = 0;
bool fullscreen = false;
bool widescreen = false;
WidescreenMode widescreen = kWidescreenNone;
ScalerParameters scalerParameters = ScalerParameters::defaults();
int forcedLanguage = -1;
if (argc == 2) {
@ -202,7 +233,7 @@ int main(int argc, char *argv[]) {
{ "fullscreen", no_argument, 0, 4 },
{ "scaler", required_argument, 0, 5 },
{ "language", required_argument, 0, 6 },
{ "widescreen", no_argument, 0, 7 },
{ "widescreen", required_argument, 0, 7 },
{ 0, 0, 0, 0 }
};
int index;
@ -248,7 +279,7 @@ int main(int argc, char *argv[]) {
}
break;
case 7:
widescreen = true;
widescreen = parseWidescreen(optarg);
break;
default:
printf(USAGE, argv[0]);
@ -265,8 +296,8 @@ int main(int argc, char *argv[]) {
}
const Language language = (forcedLanguage == -1) ? detectLanguage(&fs) : (Language)forcedLanguage;
SystemStub *stub = SystemStub_SDL_create();
Game *g = new Game(stub, &fs, savePath, levelNum, (ResourceType)version, language);
stub->init(g_caption, g->_vid._w, g->_vid._h, fullscreen, widescreen, &scalerParameters);
Game *g = new Game(stub, &fs, savePath, levelNum, (ResourceType)version, language, widescreen);
stub->init(g_caption, g->_vid._w, g->_vid._h, fullscreen, widescreen != kWidescreenNone, &scalerParameters);
g->run();
delete g;
stub->destroy();

View File

@ -68,6 +68,11 @@ void Menu::drawString2(const char *str, int16_t y, int16_t x) {
int h = Video::CHAR_H;
int len = 0;
switch (_res->_type) {
case kResourceTypeAmiga:
for (; str[len]; ++len) {
_vid->AMIGA_drawStringChar(_vid->_frontLayer, _vid->_w, Video::CHAR_W * (x + len), Video::CHAR_H * y, _res->_fnt, _vid->_charFrontColor, (uint8_t)str[len]);
}
break;
case kResourceTypeDOS:
for (; str[len]; ++len) {
_vid->PC_drawChar((uint8_t)str[len], y, x + len, true);
@ -77,8 +82,6 @@ void Menu::drawString2(const char *str, int16_t y, int16_t x) {
for (; str[len]; ++len) {
_vid->MAC_drawStringChar(_vid->_frontLayer, _vid->_w, Video::CHAR_W * (x + len), Video::CHAR_H * y, _res->_fnt, _vid->_charFrontColor, (uint8_t)str[len]);
}
w *= _vid->_layerScale;
h *= _vid->_layerScale;
break;
}
_vid->markBlockAsDirty(x * w, y * h, len * w, h);

8
menu.h
View File

@ -30,6 +30,14 @@ struct Menu {
SCREEN_LEVEL,
SCREEN_INFO
};
enum {
kMacTitleScreen_MacPlay = 1,
kMacTitleScreen_Presage = 2,
kMacTitleScreen_Flashback = 3,
kMacTitleScreen_LeftEye = 4,
kMacTitleScreen_RightEye = 5,
kMacTitleScreen_Controls = 6
};
enum {
EVENTS_DELAY = 80

View File

@ -21,7 +21,7 @@ Resource::Resource(FileSystem *fs, ResourceType ver, Language lang) {
_mac = 0;
_readUint16 = (_type == kResourceTypeDOS) ? READ_LE_UINT16 : READ_BE_UINT16;
_readUint32 = (_type == kResourceTypeDOS) ? READ_LE_UINT32 : READ_BE_UINT32;
_scratchBuffer = (uint8_t *)malloc(320 * 224 + 1024);
_scratchBuffer = (uint8_t *)malloc(kScratchBufferSize);
if (!_scratchBuffer) {
error("Unable to allocate temporary memory buffer");
}
@ -36,9 +36,9 @@ Resource::Resource(FileSystem *fs, ResourceType ver, Language lang) {
Resource::~Resource() {
clearLevelRes();
MAC_unloadLevelData();
free(_fnt);
free(_icn); _icn = 0;
_icnLen = 0;
free(_icn);
free(_tab);
free(_spc);
free(_spr1);
@ -268,7 +268,7 @@ void Resource::load_PAL_menu(const char *fileName, uint8_t *dstPtr) {
error("Cannot load '%s'", _entryName);
}
void Resource::load_CMP_menu(const char *fileName, uint8_t *dstPtr) {
void Resource::load_CMP_menu(const char *fileName) {
File f;
if (f.open(fileName, "rb", _fs)) {
const uint32_t size = f.readUint32BE();
@ -277,7 +277,7 @@ void Resource::load_CMP_menu(const char *fileName, uint8_t *dstPtr) {
error("Failed to allocate CMP temporary buffer");
}
f.read(tmp, size);
if (!delphine_unpack(dstPtr, tmp, size)) {
if (!delphine_unpack(_scratchBuffer, kScratchBufferSize, tmp, size)) {
error("Bad CRC for %s", fileName);
}
free(tmp);
@ -668,7 +668,7 @@ void Resource::load(const char *objName, int objType, const char *ext) {
_pal = dat;
break;
case OT_CT:
if (!delphine_unpack((uint8_t *)_ctData, dat, size)) {
if (!delphine_unpack((uint8_t *)_ctData, sizeof(_ctData), dat, size)) {
error("Bad CRC for '%s'", _entryName);
}
free(dat);
@ -736,7 +736,7 @@ void Resource::load_CT(File *pf) {
error("Unable to allocate CT buffer");
} else {
pf->read(tmp, len);
if (!delphine_unpack((uint8_t *)_ctData, tmp, len)) {
if (!delphine_unpack((uint8_t *)_ctData, sizeof(_ctData), tmp, len)) {
error("Bad CRC for collision data");
}
free(tmp);
@ -936,7 +936,7 @@ void Resource::load_OBC(File *f) {
}
f->seek(4);
f->read(packedData, packedSize);
if (!delphine_unpack(tmp, packedData, packedSize)) {
if (!delphine_unpack(tmp, unpackedSize, packedData, packedSize)) {
error("Bad CRC for compressed object data");
}
free(packedData);
@ -1153,7 +1153,7 @@ void Resource::load_CMP(File *pf) {
}
if (data[0].packedSize == data[0].size) {
memcpy(_pol, tmp + data[0].offset, data[0].packedSize);
} else if (!delphine_unpack(_pol, tmp + data[0].offset, data[0].packedSize)) {
} else if (!delphine_unpack(_pol, data[0].size, tmp + data[0].offset, data[0].packedSize)) {
error("Bad CRC for cutscene polygon data");
}
_cmd = (uint8_t *)malloc(data[1].size);
@ -1162,7 +1162,7 @@ void Resource::load_CMP(File *pf) {
}
if (data[1].packedSize == data[1].size) {
memcpy(_cmd, tmp + data[1].offset, data[1].packedSize);
} else if (!delphine_unpack(_cmd, tmp + data[1].offset, data[1].packedSize)) {
} else if (!delphine_unpack(_cmd, data[1].size, tmp + data[1].offset, data[1].packedSize)) {
error("Bad CRC for cutscene command data");
}
free(tmp);
@ -1278,7 +1278,7 @@ void Resource::load_SGD(File *f) {
if (!_sgd) {
error("Unable to allocate SGD buffer");
}
if (!delphine_unpack(_sgd, tmp, len)) {
if (!delphine_unpack(_sgd, size, tmp, len)) {
error("Bad CRC for SGD data");
}
free(tmp);
@ -1310,12 +1310,12 @@ void Resource::load_SPM(File *f) {
if (!_spr1) {
error("Unable to allocate SPR1 buffer");
}
if (!delphine_unpack(_spr1, tmp, len)) {
if (!delphine_unpack(_spr1, size, tmp, len)) {
error("Bad CRC for SPM data");
}
} else {
assert(size <= sizeof(_sprm));
if (!delphine_unpack(_sprm, tmp, len)) {
if (!delphine_unpack(_sprm, sizeof(_sprm), tmp, len)) {
error("Bad CRC for SPM data");
}
}
@ -1391,7 +1391,7 @@ uint8_t *Resource::loadBankData(uint16_t num) {
} else {
assert(dataOffset > 4);
assert(size == (int)READ_BE_UINT32(data - 4));
if (!delphine_unpack(_bankDataHead, data, 0)) {
if (!delphine_unpack(_bankDataHead, _bankDataTail - _bankDataHead, data, 0)) {
error("Bad CRC for bank data %d", num);
}
}

View File

@ -112,7 +112,8 @@ struct Resource {
enum {
kPaulaFreq = 3546897,
kClutSize = 1024
kClutSize = 1024,
kScratchBufferSize = 320 * 224 + 1024
};
static const uint16_t _voicesOffsetsTable[];
@ -187,6 +188,7 @@ struct Resource {
bool isDOS() const { return _type == kResourceTypeDOS; }
bool isAmiga() const { return _type == kResourceTypeAmiga; }
bool isMac() const { return _type == kResourceTypeMac; }
bool fileExists(const char *filename);
@ -196,7 +198,7 @@ struct Resource {
void load_SPL_demo();
void load_MAP_menu(const char *fileName, uint8_t *dstPtr);
void load_PAL_menu(const char *fileName, uint8_t *dstPtr);
void load_CMP_menu(const char *fileName, uint8_t *dstPtr);
void load_CMP_menu(const char *fileName);
void load_SPR_OFF(const char *fileName, uint8_t *sprData);
void load_CINE();
void free_CINE();

View File

@ -74,7 +74,7 @@ uint8_t *ResourceAba::loadEntry(const char *name, uint32_t *size) {
free(tmp);
return 0;
}
const bool ret = delphine_unpack(dst, tmp, e->compressedSize);
const bool ret = delphine_unpack(dst, e->size, tmp, e->compressedSize);
if (!ret) {
error("Bad CRC for '%s'", name);
}

2
rs.cfg
View File

@ -16,7 +16,7 @@ use_tiledata=false
# display text instead of playing the polygon cutscenes
use_text_cutscenes=false
# enable playback of .SEQ cutscenes (use polygonal if false)
# enable playback of .SEQ cutscenes (always use polygon cutscenes if false)
use_seq_cutscenes=true
# enable playback of 'ASC' cutscene

View File

@ -31,4 +31,10 @@ extern const Scaler _internalScaler;
const Scaler *findScaler(const char *name);
#ifdef USE_STATIC_SCALER
extern const Scaler scaler_nearest;
extern const Scaler scaler_tv2x;
extern const Scaler scaler_xbrz;
#endif
#endif // SCALER_H__

View File

@ -68,6 +68,7 @@ struct SystemStub {
virtual void copyRectRgb24(int x, int y, int w, int h, const uint8_t *rgb) = 0;
virtual void copyRectLeftBorder(int w, int h, const uint8_t *buf) = 0;
virtual void copyRectRightBorder(int w, int h, const uint8_t *buf) = 0;
virtual void copyRectMirrorBorders(int w, int h, const uint8_t *buf) = 0;
virtual void fadeScreen() = 0;
virtual void updateScreen(int shakeOffset) = 0;

View File

@ -66,6 +66,7 @@ struct SystemStub_SDL : SystemStub {
virtual void copyRectRgb24(int x, int y, int w, int h, const uint8_t *rgb);
virtual void copyRectLeftBorder(int w, int h, const uint8_t *buf);
virtual void copyRectRightBorder(int w, int h, const uint8_t *buf);
virtual void copyRectMirrorBorders(int w, int h, const uint8_t *buf);
virtual void fadeScreen();
virtual void updateScreen(int shakeOffset);
virtual void processEvents();
@ -82,6 +83,7 @@ struct SystemStub_SDL : SystemStub {
void prepareGraphics();
void cleanupGraphics();
void changeGraphics(bool fullscreen, int scaleFactor);
void changeScaler(int scaler);
void drawRect(int x, int y, int w, int h, uint8_t color);
};
@ -97,13 +99,13 @@ void SystemStub_SDL::init(const char *title, int w, int h, bool fullscreen, bool
_window = 0;
_renderer = 0;
_texture = 0;
_fmt = 0;
_fmt = SDL_AllocFormat(kPixelFormat);
_screenBuffer = 0;
_fadeOnUpdateScreen = false;
_fullscreen = fullscreen;
_scalerType = scalerParameters->type;
_scaler = scalerParameters->scaler;
_scaleFactor = CLIP(scalerParameters->factor, _scaler->factorMin, _scaler->factorMax);
_scaleFactor = _scaler ? CLIP(scalerParameters->factor, _scaler->factorMin, _scaler->factorMax) : 1;
memset(_rgbPalette, 0, sizeof(_rgbPalette));
memset(_darkPalette, 0, sizeof(_darkPalette));
_screenW = _screenH = 0;
@ -127,6 +129,14 @@ void SystemStub_SDL::init(const char *title, int w, int h, bool fullscreen, bool
void SystemStub_SDL::destroy() {
cleanupGraphics();
if (_screenBuffer) {
free(_screenBuffer);
_screenBuffer = 0;
}
if (_fmt) {
SDL_FreeFormat(_fmt);
_fmt = 0;
}
if (_controller) {
SDL_GameControllerClose(_controller);
_controller = 0;
@ -147,6 +157,10 @@ void SystemStub_SDL::setScreenSize(int w, int h) {
return;
}
cleanupGraphics();
if (_screenBuffer) {
free(_screenBuffer);
_screenBuffer = 0;
}
const int screenBufferSize = w * h * sizeof(uint32_t);
_screenBuffer = (uint32_t *)calloc(1, screenBufferSize);
if (!_screenBuffer) {
@ -232,6 +246,19 @@ void SystemStub_SDL::copyRectRgb24(int x, int y, int w, int h, const uint8_t *rg
}
}
static void clearTexture(SDL_Texture *texture, int h, SDL_PixelFormat *fmt) {
void *dst = 0;
int pitch = 0;
if (SDL_LockTexture(texture, 0, &dst, &pitch) == 0) {
assert((pitch & 3) == 0);
const uint32_t color = SDL_MapRGB(fmt, 0, 0, 0);
for (uint32_t i = 0; i < h * pitch / sizeof(uint32_t); ++i) {
((uint32_t *)dst)[i] = color;
}
SDL_UnlockTexture(texture);
}
}
void SystemStub_SDL::copyRectLeftBorder(int w, int h, const uint8_t *buf) {
assert(w >= _wideMargin);
uint32_t *rgb = (uint32_t *)malloc(w * h * sizeof(uint32_t));
@ -241,7 +268,10 @@ void SystemStub_SDL::copyRectLeftBorder(int w, int h, const uint8_t *buf) {
rgb[i] = _darkPalette[buf[i]];
}
} else {
memset(rgb, 0, w * h * sizeof(uint32_t));
const uint32_t color = SDL_MapRGB(_fmt, 0, 0, 0);
for (int i = 0; i < w * h; ++i) {
rgb[i] = color;
}
}
const int xOffset = w - _wideMargin;
SDL_Rect r;
@ -263,7 +293,10 @@ void SystemStub_SDL::copyRectRightBorder(int w, int h, const uint8_t *buf) {
rgb[i] = _darkPalette[buf[i]];
}
} else {
memset(rgb, 0, w * h * sizeof(uint32_t));
const uint32_t color = SDL_MapRGB(_fmt, 0, 0, 0);
for (int i = 0; i < w * h; ++i) {
rgb[i] = color;
}
}
const int xOffset = 0;
SDL_Rect r;
@ -276,6 +309,35 @@ void SystemStub_SDL::copyRectRightBorder(int w, int h, const uint8_t *buf) {
}
}
void SystemStub_SDL::copyRectMirrorBorders(int w, int h, const uint8_t *buf) {
assert(w >= _wideMargin);
uint32_t *rgb = (uint32_t *)malloc(w * h * sizeof(uint32_t));
if (rgb) {
for (int i = 0; i < w * h; ++i) {
rgb[i] = _darkPalette[buf[i]];
}
void *dst = 0;
int pitch = 0;
if (SDL_LockTexture(_wideTexture, 0, &dst, &pitch) == 0) {
assert((pitch & 3) == 0);
uint32_t *p = (uint32_t *)dst;
for (int y = 0; y < h; ++y) {
for (int x = 0; x < _wideMargin; ++x) {
// left side
const int xLeft = _wideMargin - 1 - x;
p[x] = rgb[y * w + xLeft];
// right side
const int xRight = w - 1 - x;
p[_wideMargin + _screenW + x] = rgb[y * w + xRight];
}
p += pitch / sizeof(uint32_t);
}
SDL_UnlockTexture(_wideTexture);
}
free(rgb);
}
}
void SystemStub_SDL::fadeScreen() {
_fadeOnUpdateScreen = true;
}
@ -511,11 +573,15 @@ void SystemStub_SDL::processEvent(const SDL_Event &ev, bool &paused) {
break;
case SDLK_KP_PLUS:
case SDLK_PAGEUP:
if (_scalerType == kScalerTypeInternal || _scalerType == kScalerTypeExternal) {
changeGraphics(_fullscreen, _scaleFactor + 1);
}
break;
case SDLK_KP_MINUS:
case SDLK_PAGEDOWN:
if (_scalerType == kScalerTypeInternal || _scalerType == kScalerTypeExternal) {
changeGraphics(_fullscreen, _scaleFactor - 1);
}
break;
case SDLK_s: {
char name[32];
@ -585,6 +651,16 @@ void SystemStub_SDL::processEvent(const SDL_Event &ev, bool &paused) {
case SDLK_ESCAPE:
_pi.escape = false;
break;
case SDLK_F1:
case SDLK_F2:
case SDLK_F3:
case SDLK_F4:
case SDLK_F5:
case SDLK_F6:
case SDLK_F7:
case SDLK_F8:
changeScaler(ev.key.keysym.sym - SDLK_F1);
break;
default:
break;
}
@ -717,21 +793,18 @@ void SystemStub_SDL::prepareGraphics() {
_renderer = SDL_CreateRenderer(_window, -1, SDL_RENDERER_ACCELERATED);
SDL_RenderSetLogicalSize(_renderer, windowW, windowH);
_texture = SDL_CreateTexture(_renderer, kPixelFormat, SDL_TEXTUREACCESS_STREAMING, _texW, _texH);
_fmt = SDL_AllocFormat(kPixelFormat);
if (_widescreen) {
const int w = _screenH * 16 / 9;
const int h = _screenH;
_wideTexture = SDL_CreateTexture(_renderer, kPixelFormat, SDL_TEXTUREACCESS_STREAMING, w, h);
clearTexture(_wideTexture, _screenH, _fmt);
// left and right borders
_wideMargin = (w - _screenW) / 2;
}
}
void SystemStub_SDL::cleanupGraphics() {
if (_screenBuffer) {
free(_screenBuffer);
_screenBuffer = 0;
}
if (_texture) {
SDL_DestroyTexture(_texture);
_texture = 0;
@ -748,10 +821,6 @@ void SystemStub_SDL::cleanupGraphics() {
SDL_DestroyWindow(_window);
_window = 0;
}
if (_fmt) {
SDL_FreeFormat(_fmt);
_fmt = 0;
}
}
void SystemStub_SDL::changeGraphics(bool fullscreen, int scaleFactor) {
@ -762,29 +831,53 @@ void SystemStub_SDL::changeGraphics(bool fullscreen, int scaleFactor) {
}
_fullscreen = fullscreen;
_scaleFactor = factor;
if (_texture) {
SDL_DestroyTexture(_texture);
_texture = 0;
}
if (_wideTexture) {
SDL_DestroyTexture(_wideTexture);
_wideTexture = 0;
}
if (_renderer) {
SDL_DestroyRenderer(_renderer);
_renderer = 0;
}
if (_window) {
SDL_DestroyWindow(_window);
_window = 0;
}
if (_fmt) {
SDL_FreeFormat(_fmt);
_fmt = 0;
}
cleanupGraphics();
prepareGraphics();
}
void SystemStub_SDL::changeScaler(int scaler) {
ScalerParameters scalerParameters = ScalerParameters::defaults();
switch (scaler) {
case 0:
scalerParameters.type = kScalerTypePoint;
break;
case 1:
scalerParameters.type = kScalerTypeLinear;
break;
case 2:
scalerParameters.type = kScalerTypeInternal;
scalerParameters.scaler = &_internalScaler;
break;
#ifdef USE_STATIC_SCALER
case 3:
scalerParameters.type = kScalerTypeInternal;
scalerParameters.scaler = &scaler_nearest;
break;
case 4:
scalerParameters.type = kScalerTypeInternal;
scalerParameters.scaler = &scaler_tv2x;
break;
case 5:
scalerParameters.type = kScalerTypeInternal;
scalerParameters.scaler = &scaler_xbrz;
break;
#endif
default:
return;
}
if (_scalerType != scalerParameters.type || scalerParameters.scaler != _scaler) {
_scalerType = scalerParameters.type;
_scaler = scalerParameters.scaler;
const int scaleFactor = CLIP(_scaleFactor, _scaler->factorMin, _scaler->factorMax);
// only recreate the window if dimensions actually changed
if (scaleFactor != _scaleFactor) {
cleanupGraphics();
_scaleFactor = scaleFactor;
prepareGraphics();
}
}
}
void SystemStub_SDL::drawRect(int x, int y, int w, int h, uint8_t color) {
const int x1 = x;
const int y1 = y;

View File

@ -5,9 +5,10 @@
*/
#include "unpack.h"
#include "util.h"
struct UnpackCtx {
int datasize;
int size;
uint32_t crc;
uint32_t bits;
uint8_t *dst;
@ -26,40 +27,52 @@ static bool nextBit(UnpackCtx *uc) {
return carry;
}
static uint16_t getBits(UnpackCtx *uc, int bitsCount) {
uint16_t c = 0;
for (int i = 0; i < bitsCount; ++i) {
c <<= 1;
static int getBits(UnpackCtx *uc, int count) {
int bits = 0;
for (int i = 0; i < count; ++i) {
bits <<= 1;
if (nextBit(uc)) {
c |= 1;
bits |= 1;
}
}
return c;
return bits;
}
static void copyLiteral(UnpackCtx *uc, int bitsCount, int len) {
const int count = getBits(uc, bitsCount) + len + 1;
for (int i = 0; i < count; ++i) {
*uc->dst = (uint8_t)getBits(uc, 8);
--uc->dst;
int count = getBits(uc, bitsCount) + len + 1;
uc->size -= count;
if (uc->size < 0) {
count += uc->size;
uc->size = 0;
}
uc->datasize -= count;
for (int i = 0; i < count; ++i) {
*(uc->dst - i) = (uint8_t)getBits(uc, 8);
}
uc->dst -= count;
}
static void copyReference(UnpackCtx *uc, int bitsCount, int count) {
const uint16_t offset = getBits(uc, bitsCount);
for (int i = 0; i < count; ++i) {
*uc->dst = *(uc->dst + offset);
--uc->dst;
uc->size -= count;
if (uc->size < 0) {
count += uc->size;
uc->size = 0;
}
uc->datasize -= count;
const int offset = getBits(uc, bitsCount);
for (int i = 0; i < count; ++i) {
*(uc->dst - i) = *(uc->dst - i + offset);
}
uc->dst -= count;
}
bool delphine_unpack(uint8_t *dst, const uint8_t *src, int len) {
bool delphine_unpack(uint8_t *dst, int dstSize, const uint8_t *src, int srcSize) {
UnpackCtx uc;
uc.src = src + len - 4;
uc.datasize = READ_BE_UINT32(uc.src); uc.src -= 4;
uc.dst = dst + uc.datasize - 1;
uc.src = src + srcSize - 4;
uc.size = READ_BE_UINT32(uc.src); uc.src -= 4;
if (uc.size > dstSize) {
warning("Unexpected unpack size %d, buffer size %d", uc.size, dstSize);
return false;
}
uc.dst = dst + uc.size - 1;
uc.crc = READ_BE_UINT32(uc.src); uc.src -= 4;
uc.bits = READ_BE_UINT32(uc.src); uc.src -= 4;
uc.crc ^= uc.bits;
@ -87,6 +100,7 @@ bool delphine_unpack(uint8_t *dst, const uint8_t *src, int len) {
break;
}
}
} while (uc.datasize > 0);
} while (uc.size > 0);
assert(uc.size == 0);
return uc.crc == 0;
}

View File

@ -9,6 +9,6 @@
#include "intern.h"
extern bool delphine_unpack(uint8_t *dst, const uint8_t *src, int len);
extern bool delphine_unpack(uint8_t *dst, int dstSize, const uint8_t *src, int srcSize);
#endif // UNPACK_H__

View File

@ -629,8 +629,9 @@ static void decodeLevHelper(uint8_t *dst, const uint8_t *src, int offset10, int
void Video::AMIGA_decodeLev(int level, int room) {
uint8_t *tmp = _res->_scratchBuffer;
const int offset = READ_BE_UINT32(_res->_lev + room * 4);
if (!delphine_unpack(tmp, _res->_lev, offset)) {
error("Bad CRC for level %d room %d", level, room);
if (!delphine_unpack(tmp, Resource::kScratchBufferSize, _res->_lev, offset)) {
warning("Bad CRC for level %d room %d", level, room);
return;
}
uint16_t offset10 = READ_BE_UINT16(tmp + 10);
const uint16_t offset12 = READ_BE_UINT16(tmp + 12);
@ -900,6 +901,9 @@ void Video::PC_drawStringChar(uint8_t *dst, int pitch, int x, int y, const uint8
}
}
static uint8_t _MAC_fontFrontColor;
static uint8_t _MAC_fontShadowColor;
void Video::MAC_drawStringChar(uint8_t *dst, int pitch, int x, int y, const uint8_t *src, uint8_t color, uint8_t chr) {
DecodeBuffer buf;
memset(&buf, 0, sizeof(buf));
@ -909,8 +913,8 @@ void Video::MAC_drawStringChar(uint8_t *dst, int pitch, int x, int y, const uint
buf.x = x * _layerScale;
buf.y = y * _layerScale;
buf.setPixel = Video::MAC_drawBufferFont;
_charFrontColor = color;
buf.dataPtr = this;
_MAC_fontFrontColor = color;
_MAC_fontShadowColor = _charShadowColor;
assert(chr >= 32);
_res->MAC_decodeImageData(_res->_fnt, chr - 32, &buf);
}
@ -1004,21 +1008,20 @@ void Video::MAC_drawBufferFont(DecodeBuffer *buf, int src_x, int src_y, int src_
if (y >= 0 && y < buf->h) {
const int x = buf->x + src_x;
if (x >= 0 && x < buf->w) {
const Video *vid = (Video *)buf->dataPtr;
const int offset = y * buf->pitch + x;
switch (color) {
case 0xC0:
buf->ptr[offset] = vid->_charShadowColor;
buf->ptr[offset] = _MAC_fontShadowColor;
break;
case 0xC1:
buf->ptr[offset] = vid->_charFrontColor;
buf->ptr[offset] = _MAC_fontFrontColor;
break;
}
}
}
}
void Video::MAC_fillRect(int x, int y, int w, int h, uint8_t color) {
void Video::fillRect(int x, int y, int w, int h, uint8_t color) {
uint8_t *p = _frontLayer + y * _layerScale * _w + x * _layerScale;
for (int j = 0; j < h * _layerScale; ++j) {
memset(p, color, w * _layerScale);

View File

@ -90,7 +90,7 @@ struct Video {
static void MAC_drawBuffer(DecodeBuffer *buf, int src_x, int src_y, int src_w, int src_h, uint8_t color);
static void MAC_drawBufferMask(DecodeBuffer *buf, int src_x, int src_y, int src_w, int src_h, uint8_t color);
static void MAC_drawBufferFont(DecodeBuffer *buf, int src_x, int src_y, int src_w, int src_h, uint8_t color);
void MAC_fillRect(int x, int y, int w, int h, uint8_t color);
void fillRect(int x, int y, int w, int h, uint8_t color);
void MAC_drawSprite(int x, int y, const uint8_t *data, int frame, bool xflip, bool eraseBackground);
};