Import 0.4.9
This commit is contained in:
parent
315bb9bcff
commit
419cf91dfe
|
@ -1,3 +1,10 @@
|
|||
* release 0.4.9
|
||||
- added option to match original inventory items order
|
||||
- added zoom from DOS version
|
||||
- added Sega CD tracks playback based on stb_vorbis (OGG 22khz)
|
||||
- fixed piege opcode 0x57
|
||||
- fixed repeating sounds volume
|
||||
|
||||
* release 0.4.8
|
||||
- added detection for DOS version with .ABA files
|
||||
- added Macintosh credits
|
||||
|
|
4
Makefile
4
Makefile
|
@ -3,10 +3,10 @@ SDL_CFLAGS := `sdl2-config --cflags`
|
|||
SDL_LIBS := `sdl2-config --libs`
|
||||
|
||||
MODPLUG_LIBS := -lmodplug
|
||||
TREMOR_LIBS := -lvorbisidec -logg
|
||||
TREMOR_LIBS := #-lvorbisidec -logg
|
||||
ZLIB_LIBS := -lz
|
||||
|
||||
CXXFLAGS += -Wall -Wpedantic -MMD $(SDL_CFLAGS) -DUSE_MODPLUG -DUSE_TREMOR -DUSE_ZLIB
|
||||
CXXFLAGS += -Wall -Wpedantic -MMD $(SDL_CFLAGS) -DUSE_MODPLUG -DUSE_STB_VORBIS -DUSE_ZLIB
|
||||
|
||||
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 \
|
||||
|
|
15
README.txt
15
README.txt
|
@ -1,6 +1,6 @@
|
|||
|
||||
REminiscence README
|
||||
Release version: 0.4.8
|
||||
Release version: 0.4.9
|
||||
-------------------------------------------------------------------------------
|
||||
|
||||
|
||||
|
@ -62,23 +62,18 @@ In-game hotkeys :
|
|||
Arrow Keys move Conrad
|
||||
Enter use the current inventory object
|
||||
Shift talk / use / run / shoot
|
||||
Escape display the options
|
||||
Backspace display the inventory
|
||||
Escape display options
|
||||
Backspace / Tab display inventory / skip cutscene
|
||||
Alt Enter toggle windowed / fullscreen mode
|
||||
Alt + and - increase or decrease game screen scaler factor
|
||||
Alt S write screenshot as .tga
|
||||
Alt S take screenshot
|
||||
Ctrl G toggle auto zoom (DOS version only)
|
||||
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
|
||||
|
||||
Debug hotkeys :
|
||||
|
||||
Ctrl F toggle fast mode
|
||||
Ctrl I Conrad 'infinite' life
|
||||
Ctrl B toggle display of updated dirty blocks
|
||||
|
||||
|
||||
Credits:
|
||||
--------
|
||||
|
|
|
@ -42,13 +42,13 @@ void Game::col_clearState() {
|
|||
void Game::col_preparePiegeState(LivePGE *pge) {
|
||||
debug(DBG_COL, "Game::col_preparePiegeState() pge_num=%ld", pge - &_pgeLive[0]);
|
||||
CollisionSlot *ct_slot1, *ct_slot2;
|
||||
if (pge->init_PGE->unk1C == 0) {
|
||||
if (pge->init_PGE->collision_data_len == 0) {
|
||||
pge->collision_slot = 0xFF;
|
||||
return;
|
||||
}
|
||||
int i = 0;
|
||||
ct_slot1 = 0;
|
||||
for (int c = 0; c < pge->init_PGE->unk1C; ++c) {
|
||||
for (int c = 0; c < pge->init_PGE->collision_data_len; ++c) {
|
||||
ct_slot2 = _col_curSlot;
|
||||
if (ct_slot2 + 1 > &_col_slots[255])
|
||||
return;
|
||||
|
@ -231,7 +231,7 @@ int16_t Game::col_detectHit(LivePGE *pge, int16_t arg2, int16_t arg4, col_Callba
|
|||
if (pge_room < 0 || pge_room >= 0x40) {
|
||||
return 0;
|
||||
}
|
||||
int16_t thr = pge->init_PGE->counter_values[0];
|
||||
int16_t thr = pge->init_PGE->data[0];
|
||||
if (thr > 0) {
|
||||
pos_dx = -1;
|
||||
pos_dy = -1;
|
||||
|
@ -452,9 +452,9 @@ int Game::col_detectGunHit(LivePGE *pge, int16_t arg2, int16_t arg4, col_Callbac
|
|||
if (pge_room < 0 || pge_room >= 0x40) return 0;
|
||||
int16_t thr, pos_dx, pos_dy;
|
||||
if (argC == -1) {
|
||||
thr = pge->init_PGE->counter_values[0];
|
||||
thr = pge->init_PGE->data[0];
|
||||
} else {
|
||||
thr = pge->init_PGE->counter_values[3];
|
||||
thr = pge->init_PGE->data[3];
|
||||
}
|
||||
if (thr > 0) {
|
||||
pos_dx = -1;
|
||||
|
|
|
@ -108,6 +108,7 @@ int8_t CpcPlayer::readSampleData() {
|
|||
// rewind
|
||||
_f.seek(_restartPos);
|
||||
nextChunk();
|
||||
_sampleL = _sampleR = 0;
|
||||
}
|
||||
}
|
||||
const int8_t data = _f.readByte();
|
||||
|
|
24
cutscene.cpp
24
cutscene.cpp
|
@ -147,16 +147,12 @@ uint16_t Cutscene::findTextSeparators(const uint8_t *p, int len) {
|
|||
void Cutscene::drawText(int16_t x, int16_t y, const uint8_t *p, uint16_t color, uint8_t *page, int textJustify) {
|
||||
debug(DBG_CUT, "Cutscene::drawText(x=%d, y=%d, c=%d, justify=%d)", x, y, color, textJustify);
|
||||
int len = 0;
|
||||
if (_res->isMac()) {
|
||||
if (p == _textBuf) {
|
||||
while (p[len] != 0xA) {
|
||||
if (p != _textBuf && _res->isMac()) {
|
||||
len = *p++;
|
||||
} else {
|
||||
while (p[len] != 0xA && p[len]) {
|
||||
++len;
|
||||
}
|
||||
} else {
|
||||
len = *p++;
|
||||
}
|
||||
} else {
|
||||
len = strlen((const char *)p);
|
||||
}
|
||||
Video::drawCharFunc dcf = _vid->_drawChar;
|
||||
const uint8_t *fnt = (_res->_lang == LANG_JP) ? Video::_font8Jp : _res->_fnt;
|
||||
|
@ -202,7 +198,6 @@ void Cutscene::clearBackPage() {
|
|||
}
|
||||
|
||||
void Cutscene::drawCreditsText() {
|
||||
if (_creditsSequence) {
|
||||
if (_creditsKeepText) {
|
||||
if (_creditsSlowText) {
|
||||
return;
|
||||
|
@ -267,7 +262,6 @@ void Cutscene::drawCreditsText() {
|
|||
}
|
||||
drawText((_creditsTextPosX - 1) * 8, _creditsTextPosY * 8, _textBuf, 0xEF, _backPage, kTextJustifyLeft);
|
||||
}
|
||||
}
|
||||
|
||||
void Cutscene::drawProtectionShape(uint8_t shapeNum, int16_t zoom) {
|
||||
debug(DBG_CUT, "Cutscene::drawProtectionShape() shapeNum = %d", shapeNum);
|
||||
|
@ -307,8 +301,16 @@ void Cutscene::drawProtectionShape(uint8_t shapeNum, int16_t zoom) {
|
|||
void Cutscene::op_markCurPos() {
|
||||
debug(DBG_CUT, "Cutscene::op_markCurPos()");
|
||||
_cmdPtrBak = _cmdPtr;
|
||||
drawCreditsText();
|
||||
_frameDelay = 5;
|
||||
if (!_creditsSequence) {
|
||||
if (_id == kCineDebut) {
|
||||
_frameDelay = 7;
|
||||
} else if (_id == kCineChute) {
|
||||
_frameDelay = 6;
|
||||
}
|
||||
} else {
|
||||
drawCreditsText();
|
||||
}
|
||||
updateScreen();
|
||||
clearBackPage();
|
||||
_creditsSlowText = false;
|
||||
|
|
|
@ -30,6 +30,8 @@ struct Cutscene {
|
|||
};
|
||||
|
||||
enum {
|
||||
kCineDebut = 0,
|
||||
kCineChute = 47,
|
||||
kCineMemo = 48,
|
||||
kCineVoyage = 52,
|
||||
kCineEspions = 57
|
||||
|
|
123
game.cpp
123
game.cpp
|
@ -13,7 +13,7 @@
|
|||
#include "systemstub.h"
|
||||
#include "util.h"
|
||||
|
||||
Game::Game(SystemStub *stub, FileSystem *fs, const char *savePath, int level, ResourceType ver, Language lang, WidescreenMode widescreenMode, bool autoSave)
|
||||
Game::Game(SystemStub *stub, FileSystem *fs, const char *savePath, int level, ResourceType ver, Language lang, WidescreenMode widescreenMode, bool autoSave, uint32_t cheats)
|
||||
: _cut(&_res, stub, &_vid), _menu(&_res, stub, &_vid),
|
||||
_mix(fs, stub), _res(fs, ver, lang), _seq(stub, &_mix), _vid(&_res, stub, widescreenMode),
|
||||
_stub(stub), _fs(fs), _savePath(savePath) {
|
||||
|
@ -26,6 +26,7 @@ Game::Game(SystemStub *stub, FileSystem *fs, const char *savePath, int level, Re
|
|||
_autoSave = autoSave;
|
||||
_rewindPtr = -1;
|
||||
_rewindLen = 0;
|
||||
_cheats = cheats;
|
||||
}
|
||||
|
||||
void Game::run() {
|
||||
|
@ -56,7 +57,7 @@ void Game::run() {
|
|||
break;
|
||||
}
|
||||
|
||||
if (!g_options.bypass_protection && !g_options.use_words_protection && !_res.isMac()) {
|
||||
if (!g_options.bypass_protection && !g_options.use_words_protection && (_res.isAmiga() || _res.isDOS())) {
|
||||
while (!handleProtectionScreenShape()) {
|
||||
if (_stub->_pi.quit) {
|
||||
return;
|
||||
|
@ -126,12 +127,11 @@ void Game::run() {
|
|||
_skillLevel = kSkillNormal;
|
||||
_currentLevel = _demoInputs[_demoBin].level;
|
||||
_randSeed = 0;
|
||||
_mix.stopMusic();
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
_demoBin = -1;
|
||||
_skillLevel = _menu._skill;
|
||||
_currentLevel = _menu._level;
|
||||
}
|
||||
_mix.stopMusic();
|
||||
break;
|
||||
case kResourceTypeAmiga:
|
||||
|
@ -171,7 +171,6 @@ void Game::run() {
|
|||
if (_demoBin != -1 && _inp_demPos >= _res._demLen) {
|
||||
debug(DBG_DEMO, "End of demo");
|
||||
// exit level
|
||||
_demoBin = -1;
|
||||
_endLoop = true;
|
||||
}
|
||||
}
|
||||
|
@ -360,15 +359,17 @@ void Game::resetGameState() {
|
|||
_animBuffers._curPos[3] = 0xFF;
|
||||
_currentRoom = _res._pgeInit[0].init_room;
|
||||
_cut._deathCutsceneId = 0xFFFF;
|
||||
_pge_opTempVar2 = 0xFFFF;
|
||||
_deathCutsceneCounter = 0;
|
||||
_saveStateCompleted = false;
|
||||
_loadMap = true;
|
||||
pge_resetMessages();
|
||||
_blinkingConradCounter = 0;
|
||||
_pge_processOBJ = false;
|
||||
_pge_opTempVar1 = 0;
|
||||
_pge_opGunVar = 0;
|
||||
_textToDisplay = 0xFFFF;
|
||||
_pge_zoomPiegeNum = 0;
|
||||
_pge_zoomCounter = 0;
|
||||
_pge_zoomX = _pge_zoomY = 0;
|
||||
}
|
||||
|
||||
void Game::mainLoop() {
|
||||
|
@ -417,7 +418,11 @@ void Game::mainLoop() {
|
|||
_currentLevel = oldLevel;
|
||||
}
|
||||
changeLevel();
|
||||
_pge_opTempVar1 = 0;
|
||||
_pge_opGunVar = 0;
|
||||
return;
|
||||
}
|
||||
if (_currentLevel == 3 && _cut._id == 50) {
|
||||
// do not draw next room when boarding taxi
|
||||
return;
|
||||
}
|
||||
if (_loadMap) {
|
||||
|
@ -431,6 +436,9 @@ void Game::mainLoop() {
|
|||
_vid.fullRefresh();
|
||||
}
|
||||
}
|
||||
if (_res.isDOS() && (_stub->_pi.dbgMask & PlayerInput::DF_AUTOZOOM) != 0) {
|
||||
pge_updateZoom();
|
||||
}
|
||||
prepareAnims();
|
||||
drawAnims();
|
||||
drawCurrentInventoryItem();
|
||||
|
@ -481,9 +489,7 @@ void Game::playCutscene(int id) {
|
|||
_cut._id = id;
|
||||
}
|
||||
if (_cut._id != 0xFFFF) {
|
||||
if (_stub->hasWidescreen()) {
|
||||
_stub->enableWidescreen(false);
|
||||
}
|
||||
ToggleWidescreenStack tws(_stub, false);
|
||||
_mix.stopMusic();
|
||||
if (_res._hasSeqData) {
|
||||
int num = 0;
|
||||
|
@ -532,22 +538,20 @@ void Game::playCutscene(int id) {
|
|||
} else {
|
||||
_cut._id = 0xFFFF;
|
||||
}
|
||||
_mix.stopMusic();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (_cut._id != 0x4A) {
|
||||
_mix.playMusic(Cutscene::_musicTable[_cut._id]);
|
||||
}
|
||||
_cut.play();
|
||||
if (id == 0xD && !_cut._interrupted) {
|
||||
const bool extendedIntroduction = (_res._type == kResourceTypeDOS || _res._type == kResourceTypeMac);
|
||||
if (extendedIntroduction) {
|
||||
_cut._id = 0x4A;
|
||||
if (!_res.isAmiga()) {
|
||||
_cut._id = 0x4A; // second part of the introduction cutscene
|
||||
_cut.play();
|
||||
}
|
||||
}
|
||||
if (_res._type == kResourceTypeMac && !(id == 0x48 || id == 0x49)) { // continue or score screens
|
||||
if (_res.isMac() && !(id == 0x48 || id == 0x49)) { // continue or score screens
|
||||
// restore palette entries modified by the cutscene player (0xC and 0xD)
|
||||
Color palette[32];
|
||||
_res.MAC_copyClut16(palette, 0, 0x37);
|
||||
|
@ -561,9 +565,6 @@ void Game::playCutscene(int id) {
|
|||
_cut.playCredits();
|
||||
}
|
||||
_mix.stopMusic();
|
||||
if (_stub->hasWidescreen()) {
|
||||
_stub->enableWidescreen(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -639,10 +640,10 @@ void Game::showFinalScore() {
|
|||
}
|
||||
|
||||
bool Game::handleConfigPanel() {
|
||||
const int x = 7;
|
||||
const int y = 10;
|
||||
const int w = 17;
|
||||
const int h = 12;
|
||||
static const int x = 7;
|
||||
static const int y = 10;
|
||||
static const int w = 17;
|
||||
static const int h = 12;
|
||||
|
||||
_vid._charShadowColor = 0xE2;
|
||||
_vid._charFrontColor = 0xEE;
|
||||
|
@ -1027,7 +1028,7 @@ void Game::drawString(const uint8_t *p, int x, int y, uint8_t color, bool hcente
|
|||
}
|
||||
|
||||
void Game::prepareAnims() {
|
||||
if (!(_currentRoom & 0x80) && _currentRoom < 0x40) {
|
||||
if (_currentRoom < 0x40) {
|
||||
int8_t pge_room;
|
||||
LivePGE *pge = _pge_liveTable1[_currentRoom];
|
||||
while (pge) {
|
||||
|
@ -1540,6 +1541,10 @@ bool Game::hasLevelMap(int level, int room) const {
|
|||
return false;
|
||||
}
|
||||
|
||||
static bool isMetro(int level, int room) {
|
||||
return level == 1 && (room == 0 || room == 13 || room == 38 || room == 51);
|
||||
}
|
||||
|
||||
void Game::loadLevelMap() {
|
||||
debug(DBG_GAME, "Game::loadLevelMap() room=%d", _currentRoom);
|
||||
bool widescreenUpdated = false;
|
||||
|
@ -1574,14 +1579,14 @@ void Game::loadLevelMap() {
|
|||
case kResourceTypeDOS:
|
||||
if (_stub->hasWidescreen() && _widescreenMode == kWidescreenAdjacentRooms) {
|
||||
const int leftRoom = _res._ctData[CT_LEFT_ROOM + _currentRoom];
|
||||
if (leftRoom > 0 && hasLevelMap(_currentLevel, leftRoom)) {
|
||||
if (leftRoom >= 0 && hasLevelMap(_currentLevel, leftRoom) && !isMetro(_currentLevel, leftRoom)) {
|
||||
_vid.PC_decodeMap(_currentLevel, leftRoom);
|
||||
_stub->copyWidescreenLeft(Video::GAMESCREEN_W, Video::GAMESCREEN_H, _vid._backLayer);
|
||||
} else {
|
||||
_stub->copyWidescreenLeft(Video::GAMESCREEN_W, Video::GAMESCREEN_H, 0);
|
||||
}
|
||||
const int rightRoom = _res._ctData[CT_RIGHT_ROOM + _currentRoom];
|
||||
if (rightRoom > 0 && hasLevelMap(_currentLevel, rightRoom)) {
|
||||
if (rightRoom >= 0 && hasLevelMap(_currentLevel, rightRoom) && !isMetro(_currentLevel, rightRoom)) {
|
||||
_vid.PC_decodeMap(_currentLevel, rightRoom);
|
||||
_stub->copyWidescreenRight(Video::GAMESCREEN_W, Video::GAMESCREEN_H, _vid._backLayer);
|
||||
} else {
|
||||
|
@ -1594,14 +1599,14 @@ void Game::loadLevelMap() {
|
|||
case kResourceTypeMac:
|
||||
if (_stub->hasWidescreen() && _widescreenMode == kWidescreenAdjacentRooms) {
|
||||
const int leftRoom = _res._ctData[CT_LEFT_ROOM + _currentRoom];
|
||||
if (leftRoom > 0 && hasLevelMap(_currentLevel, leftRoom)) {
|
||||
if (leftRoom >= 0 && hasLevelMap(_currentLevel, leftRoom)) {
|
||||
_vid.MAC_decodeMap(_currentLevel, leftRoom);
|
||||
_stub->copyWidescreenLeft(_vid._w, _vid._h, _vid._backLayer);
|
||||
} else {
|
||||
_stub->copyWidescreenLeft(_vid._w, _vid._h, 0);
|
||||
}
|
||||
const int rightRoom = _res._ctData[CT_RIGHT_ROOM + _currentRoom];
|
||||
if (rightRoom > 0 && hasLevelMap(_currentLevel, rightRoom)) {
|
||||
if (rightRoom >= 0 && hasLevelMap(_currentLevel, rightRoom)) {
|
||||
_vid.MAC_decodeMap(_currentLevel, rightRoom);
|
||||
_stub->copyWidescreenRight(_vid._w, _vid._h, _vid._backLayer);
|
||||
} else {
|
||||
|
@ -1910,24 +1915,50 @@ void Game::handleInventory() {
|
|||
}
|
||||
icon_x_pos += 32;
|
||||
}
|
||||
if (current_line != 0) {
|
||||
drawIcon(78, 120, 176, 0xA); // down arrow
|
||||
}
|
||||
if (current_line != num_lines - 1) {
|
||||
if (current_line != (g_options.order_inventory_original ? 0 : (num_lines - 1))) {
|
||||
drawIcon(77, 120, 143, 0xA); // up arrow
|
||||
}
|
||||
if (current_line != (g_options.order_inventory_original ? (num_lines - 1) : 0)) {
|
||||
drawIcon(78, 120, 176, 0xA); // down arrow
|
||||
}
|
||||
} else {
|
||||
char buf[50];
|
||||
snprintf(buf, sizeof(buf), "SCORE %08u", _score);
|
||||
_vid.drawString(buf, (114 - strlen(buf) * Video::CHAR_W) / 2 + 72, 158, 0xE5);
|
||||
snprintf(buf, sizeof(buf), "%s:%s", _res.getMenuString(LocaleData::LI_06_LEVEL), _res.getMenuString(LocaleData::LI_13_EASY + _skillLevel));
|
||||
_vid.drawString(buf, (114 - strlen(buf) * Video::CHAR_W) / 2 + 72, 166, 0xE5);
|
||||
if (0) { // if the protection screen code was not properly cracked...
|
||||
static const uint8_t kCrackerText[17] = {
|
||||
0x19, 0x08, 0x1B, 0x19, 0x11, 0x1F, 0x08, 0x67, 0x18,
|
||||
0x16, 0x1B, 0x13, 0x08, 0x1F, 0x1B, 0x0F, 0x5A
|
||||
};
|
||||
for (int i = 0; i < 17; ++i) {
|
||||
buf[i] = kCrackerText[i] ^ 0x5A;
|
||||
}
|
||||
_vid.drawString(buf, 65, 193, 0xE4);
|
||||
}
|
||||
}
|
||||
|
||||
_vid.updateScreen();
|
||||
_stub->sleep(80);
|
||||
inp_update();
|
||||
|
||||
if (g_options.order_inventory_original) {
|
||||
if (_stub->_pi.dirMask & PlayerInput::DIR_DOWN) {
|
||||
_stub->_pi.dirMask &= ~PlayerInput::DIR_DOWN;
|
||||
if (current_line < num_lines - 1) {
|
||||
++current_line;
|
||||
current_item = current_line * 4;
|
||||
}
|
||||
}
|
||||
if (_stub->_pi.dirMask & PlayerInput::DIR_UP) {
|
||||
_stub->_pi.dirMask &= ~PlayerInput::DIR_UP;
|
||||
if (current_line > 0) {
|
||||
--current_line;
|
||||
current_item = current_line * 4;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (_stub->_pi.dirMask & PlayerInput::DIR_UP) {
|
||||
_stub->_pi.dirMask &= ~PlayerInput::DIR_UP;
|
||||
if (current_line < num_lines - 1) {
|
||||
|
@ -1942,6 +1973,7 @@ void Game::handleInventory() {
|
|||
current_item = current_line * 4;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (_stub->_pi.dirMask & PlayerInput::DIR_LEFT) {
|
||||
_stub->_pi.dirMask &= ~PlayerInput::DIR_LEFT;
|
||||
if (current_item > 0) {
|
||||
|
@ -1990,6 +2022,9 @@ void Game::makeGameStateName(uint8_t slot, char *buf) {
|
|||
sprintf(buf, "rs-level%d-%02d.state", _currentLevel + 1, slot);
|
||||
}
|
||||
|
||||
// 3: persist _pge_opGunVar
|
||||
static const int kSaveVersion = 3;
|
||||
|
||||
static const uint32_t TAG_FBSV = 0x46425356;
|
||||
|
||||
bool Game::saveGameState(uint8_t slot) {
|
||||
|
@ -2005,7 +2040,7 @@ bool Game::saveGameState(uint8_t slot) {
|
|||
} else {
|
||||
// header
|
||||
f.writeUint32BE(TAG_FBSV);
|
||||
f.writeUint16BE(2);
|
||||
f.writeUint16BE(kSaveVersion);
|
||||
char buf[32];
|
||||
memset(buf, 0, sizeof(buf));
|
||||
snprintf(buf, sizeof(buf), "level=%d room=%d", _currentLevel + 1, _currentRoom);
|
||||
|
@ -2037,15 +2072,15 @@ bool Game::loadGameState(uint8_t slot) {
|
|||
if (id != TAG_FBSV) {
|
||||
warning("Bad save state format");
|
||||
} else {
|
||||
uint16_t ver = f.readUint16BE();
|
||||
if (ver != 2) {
|
||||
const uint16_t version = f.readUint16BE();
|
||||
if (version < 2) {
|
||||
warning("Invalid save state version");
|
||||
} else {
|
||||
// header
|
||||
char buf[32];
|
||||
f.read(buf, sizeof(buf));
|
||||
// contents
|
||||
loadState(&f);
|
||||
loadState(&f, version);
|
||||
if (f.ioErr()) {
|
||||
warning("I/O error when loading game state");
|
||||
} else {
|
||||
|
@ -2083,7 +2118,7 @@ void Game::saveState(File *f) {
|
|||
f->writeByte(pge->collision_slot);
|
||||
f->writeByte(pge->next_inventory_PGE);
|
||||
f->writeByte(pge->current_inventory_PGE);
|
||||
f->writeByte(pge->unkF);
|
||||
f->writeByte(pge->ref_inventory_PGE);
|
||||
f->writeUint16BE(pge->anim_number);
|
||||
f->writeByte(pge->flags);
|
||||
f->writeByte(pge->index);
|
||||
|
@ -2114,9 +2149,10 @@ void Game::saveState(File *f) {
|
|||
f->writeByte(cs2->data_size);
|
||||
f->write(cs2->data_buf, 0x10);
|
||||
}
|
||||
f->writeUint16BE(_pge_opGunVar);
|
||||
}
|
||||
|
||||
void Game::loadState(File *f) {
|
||||
void Game::loadState(File *f, int version) {
|
||||
uint16_t i;
|
||||
uint32_t off;
|
||||
_skillLevel = f->readByte();
|
||||
|
@ -2147,7 +2183,7 @@ void Game::loadState(File *f) {
|
|||
pge->collision_slot = f->readByte();
|
||||
pge->next_inventory_PGE = f->readByte();
|
||||
pge->current_inventory_PGE = f->readByte();
|
||||
pge->unkF = f->readByte();
|
||||
pge->ref_inventory_PGE = f->readByte();
|
||||
pge->anim_number = f->readUint16BE();
|
||||
pge->flags = f->readByte();
|
||||
pge->index = f->readByte();
|
||||
|
@ -2193,6 +2229,9 @@ void Game::loadState(File *f) {
|
|||
}
|
||||
}
|
||||
resetGameState();
|
||||
if (version >= 3) {
|
||||
_pge_opGunVar = f->readUint16BE();
|
||||
}
|
||||
}
|
||||
|
||||
void Game::clearStateRewind() {
|
||||
|
@ -2234,7 +2273,7 @@ bool Game::loadStateRewind() {
|
|||
}
|
||||
File &f = _rewindBuffer[ptr];
|
||||
f.seek(0);
|
||||
loadState(&f);
|
||||
loadState(&f, kSaveVersion);
|
||||
if (_rewindLen > 0) {
|
||||
--_rewindLen;
|
||||
}
|
||||
|
|
30
game.h
30
game.h
|
@ -19,6 +19,12 @@ struct File;
|
|||
struct FileSystem;
|
||||
struct SystemStub;
|
||||
|
||||
enum {
|
||||
kCheatOneHitKill = 1 << 0,
|
||||
kCheatNoHit = 1 << 1,
|
||||
kCheatLifeCounter = 1 << 2
|
||||
};
|
||||
|
||||
struct Game {
|
||||
typedef int (Game::*pge_OpcodeProc)(ObjectOpcodeArgs *args);
|
||||
typedef int (Game::*pge_ZOrderCallback)(LivePGE *, LivePGE *, uint8_t, uint8_t);
|
||||
|
@ -71,6 +77,7 @@ struct Game {
|
|||
const char *_savePath;
|
||||
File _rewindBuffer[kRewindSize];
|
||||
int _rewindPtr, _rewindLen;
|
||||
uint32_t _cheats;
|
||||
|
||||
const uint8_t *_stringsTable;
|
||||
const char **_textsTable;
|
||||
|
@ -102,7 +109,7 @@ struct Game {
|
|||
bool _autoSave;
|
||||
uint32_t _saveTimestamp;
|
||||
|
||||
Game(SystemStub *, FileSystem *, const char *savePath, int level, ResourceType ver, Language lang, WidescreenMode widescreenMode, bool autoSave);
|
||||
Game(SystemStub *, FileSystem *, const char *savePath, int level, ResourceType ver, Language lang, WidescreenMode widescreenMode, bool autoSave, uint32_t cheats);
|
||||
|
||||
void run();
|
||||
void displayTitleScreenAmiga();
|
||||
|
@ -155,10 +162,12 @@ struct Game {
|
|||
bool _pge_currentPiegeFacingDir; // (false == left)
|
||||
bool _pge_processOBJ;
|
||||
uint8_t _pge_inpKeysMask;
|
||||
uint16_t _pge_opTempVar1;
|
||||
uint16_t _pge_opTempVar2;
|
||||
uint16_t _pge_opGunVar;
|
||||
uint16_t _pge_compareVar1;
|
||||
uint16_t _pge_compareVar2;
|
||||
uint8_t _pge_zoomPiegeNum;
|
||||
uint8_t _pge_zoomCounter;
|
||||
int _pge_zoomX, _pge_zoomY;
|
||||
|
||||
void pge_resetMessages();
|
||||
void pge_clearMessages(uint8_t pge_index);
|
||||
|
@ -248,7 +257,7 @@ struct Game {
|
|||
int pge_o_unk0x47(ObjectOpcodeArgs *args);
|
||||
int pge_o_unk0x48(ObjectOpcodeArgs *args);
|
||||
int pge_o_unk0x49(ObjectOpcodeArgs *args);
|
||||
int pge_o_unk0x4A(ObjectOpcodeArgs *args);
|
||||
int pge_op_killInventoryPiege(ObjectOpcodeArgs *args);
|
||||
int pge_op_killPiege(ObjectOpcodeArgs *args);
|
||||
int pge_op_isInCurrentRoom(ObjectOpcodeArgs *args);
|
||||
int pge_op_isNotInCurrentRoom(ObjectOpcodeArgs *args);
|
||||
|
@ -264,7 +273,7 @@ struct Game {
|
|||
int pge_op_setLifeCounter(ObjectOpcodeArgs *args);
|
||||
int pge_op_decLifeCounter(ObjectOpcodeArgs *args);
|
||||
int pge_op_playCutscene(ObjectOpcodeArgs *args);
|
||||
int pge_op_isTempVar2Set(ObjectOpcodeArgs *args);
|
||||
int pge_op_compareUnkVar(ObjectOpcodeArgs *args);
|
||||
int pge_op_playDeathCutscene(ObjectOpcodeArgs *args);
|
||||
int pge_o_unk0x5D(ObjectOpcodeArgs *args);
|
||||
int pge_o_unk0x5E(ObjectOpcodeArgs *args);
|
||||
|
@ -310,14 +319,14 @@ struct Game {
|
|||
int pge_o_unk0x86(ObjectOpcodeArgs *args);
|
||||
int pge_op_playSoundGroup(ObjectOpcodeArgs *args);
|
||||
int pge_op_adjustPos(ObjectOpcodeArgs *args);
|
||||
int pge_op_setTempVar1(ObjectOpcodeArgs *args);
|
||||
int pge_op_isTempVar1Set(ObjectOpcodeArgs *args);
|
||||
int pge_op_setGunVar(ObjectOpcodeArgs *args);
|
||||
int pge_op_compareGunVar(ObjectOpcodeArgs *args);
|
||||
int pge_setCurrentInventoryObject(LivePGE *pge);
|
||||
void pge_updateInventory(LivePGE *pge1, LivePGE *pge2);
|
||||
void pge_reorderInventory(LivePGE *pge);
|
||||
LivePGE *pge_getInventoryItemBefore(LivePGE *pge, LivePGE *last_pge);
|
||||
LivePGE *pge_getPreviousInventoryItem(LivePGE *pge, LivePGE *last_pge);
|
||||
void pge_addToInventory(LivePGE *pge1, LivePGE *pge2, LivePGE *pge3);
|
||||
int pge_updateCollisionState(LivePGE *pge, int16_t pge_dy, uint8_t var8);
|
||||
int pge_updateCollisionState(LivePGE *pge, int16_t pge_dy, uint8_t value);
|
||||
int pge_ZOrder(LivePGE *pge, int16_t num, pge_ZOrderCallback compare, uint16_t unk);
|
||||
void pge_sendMessage(uint8_t src_pge_index, uint8_t dst_pge_index, int16_t num);
|
||||
void pge_removeFromInventory(LivePGE *pge1, LivePGE *pge2, LivePGE *pge3);
|
||||
|
@ -331,6 +340,7 @@ struct Game {
|
|||
int pge_ZOrderIfTypeAndSameDirection(LivePGE *pge1, LivePGE *pge2, uint8_t comp, uint8_t comp2);
|
||||
int pge_ZOrderIfTypeAndDifferentDirection(LivePGE *pge1, LivePGE *pge2, uint8_t comp, uint8_t comp2);
|
||||
int pge_ZOrderByNumber(LivePGE *pge1, LivePGE *pge2, uint8_t comp, uint8_t comp2);
|
||||
void pge_updateZoom();
|
||||
|
||||
|
||||
// collision
|
||||
|
@ -386,7 +396,7 @@ struct Game {
|
|||
bool saveGameState(uint8_t slot);
|
||||
bool loadGameState(uint8_t slot);
|
||||
void saveState(File *f);
|
||||
void loadState(File *f);
|
||||
void loadState(File *f, int version);
|
||||
void clearStateRewind();
|
||||
bool saveStateRewind();
|
||||
bool loadStateRewind();
|
||||
|
|
17
intern.h
17
intern.h
|
@ -137,6 +137,7 @@ struct Options {
|
|||
bool play_carte_cutscene;
|
||||
bool play_gamesaved_sound;
|
||||
bool restore_memo_cutscene;
|
||||
bool order_inventory_original;
|
||||
};
|
||||
|
||||
struct Color {
|
||||
|
@ -172,8 +173,8 @@ struct InitPGE {
|
|||
int16_t pos_y;
|
||||
uint16_t obj_node_number;
|
||||
uint16_t life;
|
||||
int16_t counter_values[4]; // data
|
||||
uint8_t object_type;
|
||||
int16_t data[4];
|
||||
uint8_t object_type; // 1:conrad, 10:monster
|
||||
uint8_t init_room;
|
||||
uint8_t room_location;
|
||||
uint8_t init_flags;
|
||||
|
@ -183,7 +184,7 @@ struct InitPGE {
|
|||
uint8_t skill;
|
||||
uint8_t mirror_x;
|
||||
uint8_t flags; // 1:xflip 4:active
|
||||
uint8_t unk1C; // collidable, collision_data_len
|
||||
uint8_t collision_data_len;
|
||||
uint16_t text_num;
|
||||
};
|
||||
|
||||
|
@ -194,11 +195,11 @@ struct LivePGE {
|
|||
uint8_t anim_seq;
|
||||
uint8_t room_location;
|
||||
int16_t life;
|
||||
int16_t counter_value; // msg
|
||||
int16_t counter_value;
|
||||
uint8_t collision_slot;
|
||||
uint8_t next_inventory_PGE;
|
||||
uint8_t current_inventory_PGE;
|
||||
uint8_t unkF; // unk_inventory_PGE
|
||||
uint8_t ref_inventory_PGE;
|
||||
uint16_t anim_number;
|
||||
uint8_t flags;
|
||||
uint8_t index;
|
||||
|
@ -209,7 +210,7 @@ struct LivePGE {
|
|||
|
||||
struct MessagePGE {
|
||||
MessagePGE *next_entry;
|
||||
uint16_t index; // src_pge
|
||||
uint16_t src_pge;
|
||||
uint16_t msg_num;
|
||||
};
|
||||
|
||||
|
@ -268,9 +269,9 @@ struct BankSlot {
|
|||
|
||||
struct CollisionSlot2 {
|
||||
CollisionSlot2 *next_slot;
|
||||
int8_t *unk2;
|
||||
int8_t *unk2; // grid_data_pos
|
||||
uint8_t data_size;
|
||||
uint8_t data_buf[0x10]; // XXX check size
|
||||
uint8_t data_buf[0x10]; // <= InitPGE.collision_data_len
|
||||
};
|
||||
|
||||
struct InventoryItem {
|
||||
|
|
9
main.cpp
9
main.cpp
|
@ -102,6 +102,7 @@ static void initOptions() {
|
|||
g_options.play_carte_cutscene = false;
|
||||
g_options.play_gamesaved_sound = false;
|
||||
g_options.restore_memo_cutscene = true;
|
||||
g_options.order_inventory_original = false;
|
||||
// read configuration file
|
||||
struct {
|
||||
const char *name;
|
||||
|
@ -123,6 +124,7 @@ static void initOptions() {
|
|||
{ "play_carte_cutscene", &g_options.play_carte_cutscene },
|
||||
{ "play_gamesaved_sound", &g_options.play_gamesaved_sound },
|
||||
{ "restore_memo_cutscene", &g_options.restore_memo_cutscene },
|
||||
{ "order_inventory_original", &g_options.order_inventory_original },
|
||||
{ 0, 0 }
|
||||
};
|
||||
static const char *filename = "rs.cfg";
|
||||
|
@ -194,6 +196,7 @@ int main(int argc, char *argv[]) {
|
|||
int levelNum = 0;
|
||||
bool fullscreen = false;
|
||||
bool autoSave = false;
|
||||
uint32_t cheats = 0;
|
||||
WidescreenMode widescreen = kWidescreenNone;
|
||||
ScalerParameters scalerParameters = ScalerParameters::defaults();
|
||||
int forcedLanguage = -1;
|
||||
|
@ -214,6 +217,7 @@ int main(int argc, char *argv[]) {
|
|||
{ "language", required_argument, 0, 6 },
|
||||
{ "widescreen", required_argument, 0, 7 },
|
||||
{ "autosave", no_argument, 0, 8 },
|
||||
{ "cheats", required_argument, 0, 9 },
|
||||
{ 0, 0, 0, 0 }
|
||||
};
|
||||
int index;
|
||||
|
@ -264,6 +268,9 @@ int main(int argc, char *argv[]) {
|
|||
case 8:
|
||||
autoSave = true;
|
||||
break;
|
||||
case 9:
|
||||
cheats = atoi(optarg);
|
||||
break;
|
||||
default:
|
||||
printf(USAGE, argv[0]);
|
||||
return 0;
|
||||
|
@ -279,7 +286,7 @@ 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, widescreen, autoSave);
|
||||
Game *g = new Game(stub, &fs, savePath, levelNum, (ResourceType)version, language, widescreen, autoSave, cheats);
|
||||
stub->init(g_caption, g->_vid._w, g->_vid._h, fullscreen, widescreen, &scalerParameters);
|
||||
g->run();
|
||||
delete g;
|
||||
|
|
26
menu.cpp
26
menu.cpp
|
@ -361,10 +361,8 @@ void Menu::handleTitleScreen() {
|
|||
++menuItemsCount;
|
||||
|
||||
_selectedOption = -1;
|
||||
_currentScreen = -1;
|
||||
_nextScreen = SCREEN_TITLE;
|
||||
|
||||
bool quitLoop = false;
|
||||
int currentEntry = 0;
|
||||
|
||||
static const struct {
|
||||
|
@ -386,7 +384,7 @@ void Menu::handleTitleScreen() {
|
|||
}
|
||||
}
|
||||
|
||||
while (!quitLoop && !_stub->_pi.quit) {
|
||||
while (!_stub->_pi.quit) {
|
||||
|
||||
int selectedItem = -1;
|
||||
int previousLanguage = currentLanguage;
|
||||
|
@ -398,7 +396,6 @@ void Menu::handleTitleScreen() {
|
|||
_charVar3 = 1;
|
||||
_charVar4 = 2;
|
||||
currentEntry = 0;
|
||||
_currentScreen = _nextScreen;
|
||||
_nextScreen = -1;
|
||||
}
|
||||
|
||||
|
@ -444,30 +441,27 @@ void Menu::handleTitleScreen() {
|
|||
_selectedOption = menuItems[selectedItem].opt;
|
||||
switch (_selectedOption) {
|
||||
case MENU_OPTION_ITEM_START:
|
||||
quitLoop = true;
|
||||
break;
|
||||
return;
|
||||
case MENU_OPTION_ITEM_SKILL:
|
||||
_currentScreen = SCREEN_SKILL;
|
||||
handleSkillScreen();
|
||||
break;
|
||||
case MENU_OPTION_ITEM_PASSWORD:
|
||||
_currentScreen = SCREEN_PASSWORD;
|
||||
quitLoop = handlePasswordScreen();
|
||||
if (handlePasswordScreen()) {
|
||||
return;
|
||||
}
|
||||
break;
|
||||
case MENU_OPTION_ITEM_LEVEL:
|
||||
_currentScreen = SCREEN_LEVEL;
|
||||
quitLoop = handleLevelScreen();
|
||||
if (handleLevelScreen()) {
|
||||
return;
|
||||
}
|
||||
break;
|
||||
case MENU_OPTION_ITEM_INFO:
|
||||
_currentScreen = SCREEN_INFO;
|
||||
handleInfoScreen();
|
||||
break;
|
||||
case MENU_OPTION_ITEM_DEMO:
|
||||
quitLoop = true;
|
||||
break;
|
||||
return;
|
||||
case MENU_OPTION_ITEM_QUIT:
|
||||
quitLoop = true;
|
||||
break;
|
||||
return;
|
||||
}
|
||||
_nextScreen = SCREEN_TITLE;
|
||||
continue;
|
||||
|
|
28
mixer.cpp
28
mixer.cpp
|
@ -42,6 +42,7 @@ void Mixer::play(const uint8_t *data, uint32_t len, uint16_t freq, uint8_t volum
|
|||
if (cur->active) {
|
||||
if (cur->chunk.data == data) {
|
||||
cur->chunkPos = 0;
|
||||
cur->volume = volume;
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
|
@ -89,22 +90,21 @@ static bool isMusicSfx(int num) {
|
|||
|
||||
void Mixer::playMusic(int num) {
|
||||
debug(DBG_SND, "Mixer::playMusic(%d)", num);
|
||||
if (num > MUSIC_TRACK && num != _musicTrack) {
|
||||
if (_ogg.playTrack(num - MUSIC_TRACK)) {
|
||||
_backgroundMusicType = _musicType = MT_OGG;
|
||||
_musicTrack = num;
|
||||
return;
|
||||
}
|
||||
if (_cpc.playTrack(num - MUSIC_TRACK)) {
|
||||
_backgroundMusicType = _musicType = MT_CPC;
|
||||
_musicTrack = num;
|
||||
return;
|
||||
}
|
||||
}
|
||||
int trackNum = -1;
|
||||
if (num == 1) { // menu screen
|
||||
if (_cpc.playTrack(2) || _ogg.playTrack(2)) {
|
||||
trackNum = 2;
|
||||
} else if (num > MUSIC_TRACK) {
|
||||
trackNum = num - MUSIC_TRACK;
|
||||
}
|
||||
if (trackNum != -1 && trackNum != _musicTrack) {
|
||||
if (_ogg.playTrack(trackNum)) {
|
||||
_backgroundMusicType = _musicType = MT_OGG;
|
||||
_musicTrack = 2;
|
||||
_musicTrack = trackNum;
|
||||
return;
|
||||
}
|
||||
if (_cpc.playTrack(trackNum)) {
|
||||
_backgroundMusicType = _musicType = MT_CPC;
|
||||
_musicTrack = trackNum;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
|
153
ogg_player.cpp
153
ogg_player.cpp
|
@ -7,6 +7,9 @@
|
|||
#ifdef USE_TREMOR
|
||||
#include <tremor/ivorbisfile.h>
|
||||
#endif
|
||||
#ifdef USE_STB_VORBIS
|
||||
#include "stb_vorbis.c"
|
||||
#endif
|
||||
#include "file.h"
|
||||
#include "mixer.h"
|
||||
#include "ogg_player.h"
|
||||
|
@ -67,13 +70,17 @@ struct OggDecoder_impl {
|
|||
}
|
||||
}
|
||||
|
||||
bool load(VorbisFile *f, int mixerSampleRate) {
|
||||
bool load(const char *name, FileSystem *fs, int mixerSampleRate) {
|
||||
if (!_f.open(name, "rb", fs)) {
|
||||
return false;
|
||||
}
|
||||
_f.offset = 0;
|
||||
ov_callbacks ovcb;
|
||||
ovcb.read_func = VorbisFile::readHelper;
|
||||
ovcb.seek_func = VorbisFile::seekHelper;
|
||||
ovcb.close_func = VorbisFile::closeHelper;
|
||||
ovcb.tell_func = VorbisFile::tellHelper;
|
||||
if (ov_open_callbacks(f, &_ovf, 0, 0, ovcb) < 0) {
|
||||
if (ov_open_callbacks(&_f, &_ovf, 0, 0, ovcb) < 0) {
|
||||
warning("Invalid .ogg file");
|
||||
return false;
|
||||
}
|
||||
|
@ -131,6 +138,7 @@ struct OggDecoder_impl {
|
|||
return count;
|
||||
}
|
||||
|
||||
VorbisFile _f;
|
||||
OggVorbis_File _ovf;
|
||||
int _channels;
|
||||
bool _open;
|
||||
|
@ -139,66 +147,163 @@ struct OggDecoder_impl {
|
|||
};
|
||||
#endif
|
||||
|
||||
#ifdef USE_STB_VORBIS
|
||||
static const int kMusicVolume = 192;
|
||||
|
||||
struct OggDecoder_impl {
|
||||
OggDecoder_impl()
|
||||
: _v(0) {
|
||||
}
|
||||
~OggDecoder_impl() {
|
||||
if (_v) {
|
||||
stb_vorbis_close(_v);
|
||||
_v = 0;
|
||||
}
|
||||
}
|
||||
bool load(const char *name, FileSystem *fs, int mixerSampleRate) {
|
||||
if (!_f.open(name, "rb", fs)) {
|
||||
return false;
|
||||
}
|
||||
_count = _f.read(_buffer, sizeof(_buffer));
|
||||
if (_count > 0) {
|
||||
int bytes = 0;
|
||||
int error = 0;
|
||||
_v = stb_vorbis_open_pushdata(_buffer, _count, &bytes, &error, 0);
|
||||
if (_v) {
|
||||
_offset = bytes;
|
||||
stb_vorbis_info info = stb_vorbis_get_info(_v);
|
||||
if (info.channels != 2 || (int)info.sample_rate != mixerSampleRate) {
|
||||
warning("Unhandled ogg/pcm format ch %d rate %d", info.channels, info.sample_rate);
|
||||
return false;
|
||||
}
|
||||
_decodedSamplesLen = 0;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
int read(int16_t *dst, int samples) {
|
||||
int total = 0;
|
||||
if (_decodedSamplesLen != 0) {
|
||||
const int len = MIN(_decodedSamplesLen, samples);
|
||||
for (int i = 0; i < len; ++i) {
|
||||
const int sample = (_decodedSamples[0][i] + _decodedSamples[1][i]) / 2;
|
||||
*dst = ADDC_S16(*dst, ((sample * kMusicVolume) >> 8));
|
||||
++dst;
|
||||
}
|
||||
total += len;
|
||||
_decodedSamplesLen -= len;
|
||||
}
|
||||
while (total < samples) {
|
||||
int channels = 0;
|
||||
float **outputs;
|
||||
int count;
|
||||
int bytes = stb_vorbis_decode_frame_pushdata(_v, _buffer + _offset, _count - _offset, &channels, &outputs, &count);
|
||||
if (bytes == 0) {
|
||||
if (_offset != _count) {
|
||||
memmove(_buffer, _buffer + _offset, _count - _offset);
|
||||
_offset = _count - _offset;
|
||||
} else {
|
||||
_offset = 0;
|
||||
}
|
||||
_count = sizeof(_buffer) - _offset;
|
||||
bytes = _f.read(_buffer + _offset, _count);
|
||||
if (bytes < 0) {
|
||||
break;
|
||||
}
|
||||
if (bytes == 0) {
|
||||
// rewind
|
||||
_f.seek(0);
|
||||
_count = _f.read(_buffer, sizeof(_buffer));
|
||||
stb_vorbis_flush_pushdata(_v);
|
||||
} else {
|
||||
_count = _offset + bytes;
|
||||
}
|
||||
_offset = 0;
|
||||
continue;
|
||||
}
|
||||
_offset += bytes;
|
||||
if (channels == 2) {
|
||||
const int remain = samples - total;
|
||||
const int len = MIN(count, remain);
|
||||
for (int i = 0; i < len; ++i) {
|
||||
const int l = int(outputs[0][i] * 32768 + .5);
|
||||
const int r = int(outputs[1][i] * 32768 + .5);
|
||||
const int sample = (l + r) / 2;
|
||||
*dst = ADDC_S16(*dst, ((sample * kMusicVolume) >> 8));
|
||||
++dst;
|
||||
}
|
||||
if (count > remain) {
|
||||
_decodedSamplesLen = count - remain;
|
||||
assert(_decodedSamplesLen < 1024);
|
||||
for (int i = 0; i < _decodedSamplesLen; ++i) {
|
||||
_decodedSamples[0][i] = int(outputs[0][len + i] * 32768 + .5);
|
||||
_decodedSamples[1][i] = int(outputs[1][len + i] * 32768 + .5);
|
||||
}
|
||||
total = samples;
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
warning("Invalid decoded data channels %d count %d", channels, count);
|
||||
}
|
||||
total += count;
|
||||
}
|
||||
return total;
|
||||
}
|
||||
|
||||
uint8_t _buffer[8192];
|
||||
int16_t _decodedSamples[2][1024];
|
||||
int _decodedSamplesLen;
|
||||
uint32_t _offset, _count;
|
||||
stb_vorbis *_v;
|
||||
File _f;
|
||||
};
|
||||
#endif
|
||||
|
||||
OggPlayer::OggPlayer(Mixer *mixer, FileSystem *fs)
|
||||
: _mix(mixer), _fs(fs), _impl(0) {
|
||||
: _mix(mixer), _fs(fs) {
|
||||
_impl = new OggDecoder_impl;
|
||||
}
|
||||
|
||||
OggPlayer::~OggPlayer() {
|
||||
#ifdef USE_TREMOR
|
||||
delete _impl;
|
||||
#endif
|
||||
_impl = 0;
|
||||
}
|
||||
|
||||
bool OggPlayer::playTrack(int num) {
|
||||
#ifdef USE_TREMOR
|
||||
stopTrack();
|
||||
char buf[16];
|
||||
snprintf(buf, sizeof(buf), "track%02d.ogg", num);
|
||||
VorbisFile *vf = new VorbisFile;
|
||||
if (vf->open(buf, "rb", _fs)) {
|
||||
vf->offset = 0;
|
||||
_impl = new OggDecoder_impl();
|
||||
if (_impl->load(vf, _mix->getSampleRate())) {
|
||||
if (_impl->load(buf, _fs, _mix->getSampleRate())) {
|
||||
debug(DBG_INFO, "Playing '%s'", buf);
|
||||
_mix->setPremixHook(mixCallback, this);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
delete vf;
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
|
||||
void OggPlayer::stopTrack() {
|
||||
#ifdef USE_TREMOR
|
||||
if (_impl) {
|
||||
_mix->setPremixHook(0, 0);
|
||||
delete _impl;
|
||||
_impl = 0;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
void OggPlayer::pauseTrack() {
|
||||
#ifdef USE_TREMOR
|
||||
if (_impl) {
|
||||
_mix->setPremixHook(0, 0);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void OggPlayer::resumeTrack() {
|
||||
#ifdef USE_TREMOR
|
||||
if (_impl) {
|
||||
_mix->setPremixHook(mixCallback, this);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
bool OggPlayer::mix(int16_t *buf, int len) {
|
||||
#ifdef USE_TREMOR
|
||||
if (_impl) {
|
||||
return _impl->read(buf, len) != 0;
|
||||
}
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
242
piege.cpp
242
piege.cpp
|
@ -16,12 +16,12 @@ void Game::pge_resetMessages() {
|
|||
int n = 0xFF;
|
||||
while (n--) {
|
||||
le->next_entry = le + 1;
|
||||
le->index = 0;
|
||||
le->src_pge = 0;
|
||||
le->msg_num = 0;
|
||||
++le;
|
||||
}
|
||||
le->next_entry = 0;
|
||||
le->index = 0;
|
||||
le->src_pge = 0;
|
||||
le->msg_num = 0;
|
||||
}
|
||||
|
||||
|
@ -33,7 +33,7 @@ void Game::pge_clearMessages(uint8_t pge_index) {
|
|||
while (le) {
|
||||
MessagePGE *cur = le->next_entry;
|
||||
le->next_entry = next;
|
||||
le->index = 0;
|
||||
le->src_pge = 0;
|
||||
le->msg_num = 0;
|
||||
next = le;
|
||||
le = cur;
|
||||
|
@ -44,10 +44,10 @@ void Game::pge_clearMessages(uint8_t pge_index) {
|
|||
|
||||
int Game::pge_hasMessageData(LivePGE *pge, uint16_t msg_num, uint16_t counter) const {
|
||||
assert(counter >= 1 && counter <= 4);
|
||||
uint16_t pge_src_index = pge->init_PGE->counter_values[counter - 1];
|
||||
uint16_t pge_src_index = pge->init_PGE->data[counter - 1];
|
||||
const MessagePGE *le = _pge_messagesTable[pge->index];
|
||||
while (le) {
|
||||
if (le->msg_num == msg_num && le->index == pge_src_index) {
|
||||
if (le->msg_num == msg_num && le->src_pge == pge_src_index) {
|
||||
return 1;
|
||||
}
|
||||
le = le->next_entry;
|
||||
|
@ -76,7 +76,7 @@ void Game::pge_loadForCurrentLevel(uint16_t idx) {
|
|||
live_pge->collision_slot = 0xFF;
|
||||
live_pge->next_inventory_PGE = 0xFF;
|
||||
live_pge->current_inventory_PGE = 0xFF;
|
||||
live_pge->unkF = 0xFF;
|
||||
live_pge->ref_inventory_PGE = 0xFF;
|
||||
live_pge->anim_number = 0;
|
||||
live_pge->index = idx;
|
||||
live_pge->next_PGE_in_room = 0;
|
||||
|
@ -317,8 +317,14 @@ int Game::pge_execute(LivePGE *live_pge, InitPGE *init_pge, const Object *obj) {
|
|||
--live_pge->life;
|
||||
if (init_pge->object_type == 1) {
|
||||
_pge_processOBJ = true;
|
||||
if (_cheats & kCheatLifeCounter) {
|
||||
++live_pge->life;
|
||||
}
|
||||
} else if (init_pge->object_type == 10) {
|
||||
_score += 100;
|
||||
if (_cheats & kCheatOneHitKill) {
|
||||
live_pge->life = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (obj->flags & 4) {
|
||||
|
@ -369,7 +375,7 @@ void Game::pge_prepare() {
|
|||
|
||||
void Game::pge_setupDefaultAnim(LivePGE *pge) {
|
||||
const uint8_t *anim_data = _res.getAniData(pge->obj_type);
|
||||
if (pge->anim_seq < _res._readUint16(anim_data)) {
|
||||
if (1 || pge->anim_seq < _res._readUint16(anim_data)) { /* matches disassembly but should probably be >= */
|
||||
pge->anim_seq = 0;
|
||||
}
|
||||
const uint8_t *anim_frame = anim_data + 6 + pge->anim_seq * 4;
|
||||
|
@ -435,7 +441,7 @@ void Game::pge_setupOtherPieges(LivePGE *pge, InitPGE *init_pge) {
|
|||
_currentRoom = room;
|
||||
col_prepareRoomState();
|
||||
_loadMap = true;
|
||||
if (!(_currentRoom & 0x80) && _currentRoom < 0x40) {
|
||||
if (_currentRoom < 0x40) {
|
||||
LivePGE *pge_it = _pge_liveTable1[_currentRoom];
|
||||
while (pge_it) {
|
||||
if (pge_it->init_PGE->flags & 4) {
|
||||
|
@ -809,25 +815,25 @@ int Game::pge_hasPiegeSentMessage(ObjectOpcodeArgs *args) {
|
|||
|
||||
int Game::pge_op_sendMessageData0(ObjectOpcodeArgs *args) {
|
||||
LivePGE *pge = args->pge;
|
||||
pge_sendMessage(pge->index, pge->init_PGE->counter_values[0], args->a);
|
||||
pge_sendMessage(pge->index, pge->init_PGE->data[0], args->a);
|
||||
return 0xFFFF;
|
||||
}
|
||||
|
||||
int Game::pge_op_sendMessageData1(ObjectOpcodeArgs *args) {
|
||||
LivePGE *pge = args->pge;
|
||||
pge_sendMessage(pge->index, pge->init_PGE->counter_values[1], args->a);
|
||||
pge_sendMessage(pge->index, pge->init_PGE->data[1], args->a);
|
||||
return 0xFFFF;
|
||||
}
|
||||
|
||||
int Game::pge_op_sendMessageData2(ObjectOpcodeArgs *args) {
|
||||
LivePGE *pge = args->pge;
|
||||
pge_sendMessage(pge->index, pge->init_PGE->counter_values[2], args->a);
|
||||
pge_sendMessage(pge->index, pge->init_PGE->data[2], args->a);
|
||||
return 0xFFFF;
|
||||
}
|
||||
|
||||
int Game::pge_op_sendMessageData3(ObjectOpcodeArgs *args) {
|
||||
LivePGE *pge = args->pge;
|
||||
pge_sendMessage(pge->index, pge->init_PGE->counter_values[3], args->a);
|
||||
pge_sendMessage(pge->index, pge->init_PGE->data[3], args->a);
|
||||
return 0xFFFF;
|
||||
}
|
||||
|
||||
|
@ -1102,7 +1108,7 @@ int Game::pge_o_unk0x40(ObjectOpcodeArgs *args) {
|
|||
|
||||
int Game::pge_op_wakeUpPiege(ObjectOpcodeArgs *args) {
|
||||
if (args->a <= 3) {
|
||||
int16_t num = args->pge->init_PGE->counter_values[args->a];
|
||||
int16_t num = args->pge->init_PGE->data[args->a];
|
||||
if (num >= 0) {
|
||||
LivePGE *pge = &_pgeLive[num];
|
||||
pge->flags |= 4;
|
||||
|
@ -1114,7 +1120,7 @@ int Game::pge_op_wakeUpPiege(ObjectOpcodeArgs *args) {
|
|||
|
||||
int Game::pge_op_removePiege(ObjectOpcodeArgs *args) {
|
||||
if (args->a <= 3) {
|
||||
int16_t num = args->pge->init_PGE->counter_values[args->a];
|
||||
int16_t num = args->pge->init_PGE->data[args->a];
|
||||
if (num >= 0) {
|
||||
_pge_liveTable2[num] = 0;
|
||||
_pgeLive[num].flags &= ~4;
|
||||
|
@ -1127,8 +1133,7 @@ int Game::pge_op_removePiegeIfNotNear(ObjectOpcodeArgs *args) {
|
|||
LivePGE *pge = args->pge;
|
||||
if (!(pge->init_PGE->flags & 4)) goto kill_pge;
|
||||
if (_currentRoom & 0x80) goto skip_pge;
|
||||
if (pge->room_location & 0x80) goto kill_pge;
|
||||
if (pge->room_location > 0x3F) goto kill_pge;
|
||||
if (!(pge->room_location < 0x40)) goto kill_pge;
|
||||
if (pge->room_location == _currentRoom) goto skip_pge;
|
||||
if (pge->room_location == _res._ctData[CT_UP_ROOM + _currentRoom]) goto skip_pge;
|
||||
if (pge->room_location == _res._ctData[CT_DOWN_ROOM + _currentRoom]) goto skip_pge;
|
||||
|
@ -1146,7 +1151,7 @@ skip_pge:
|
|||
}
|
||||
|
||||
int Game::pge_op_loadPiegeCounter(ObjectOpcodeArgs *args) {
|
||||
args->pge->counter_value = args->pge->init_PGE->counter_values[args->a];
|
||||
args->pge->counter_value = args->pge->init_PGE->data[args->a];
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
@ -1168,7 +1173,7 @@ int Game::pge_o_unk0x47(ObjectOpcodeArgs *args) {
|
|||
|
||||
// used with Ian in level2
|
||||
int Game::pge_o_unk0x48(ObjectOpcodeArgs *args) {
|
||||
LivePGE *pge = col_findPiege(&_pgeLive[0], args->pge->init_PGE->counter_values[0]);
|
||||
LivePGE *pge = col_findPiege(&_pgeLive[0], args->pge->init_PGE->data[0]);
|
||||
if (pge && pge->life == args->pge->life) {
|
||||
pge_sendMessage(args->pge->index, pge->index, args->a);
|
||||
return 1;
|
||||
|
@ -1177,15 +1182,15 @@ int Game::pge_o_unk0x48(ObjectOpcodeArgs *args) {
|
|||
}
|
||||
|
||||
int Game::pge_o_unk0x49(ObjectOpcodeArgs *args) {
|
||||
return pge_ZOrder(&_pgeLive[0], args->a, &Game::pge_ZOrderIfIndex, args->pge->init_PGE->counter_values[0]);
|
||||
return pge_ZOrder(&_pgeLive[0], args->a, &Game::pge_ZOrderIfIndex, args->pge->init_PGE->data[0]);
|
||||
}
|
||||
|
||||
int Game::pge_o_unk0x4A(ObjectOpcodeArgs *args) {
|
||||
int Game::pge_op_killInventoryPiege(ObjectOpcodeArgs *args) {
|
||||
LivePGE *pge = args->pge;
|
||||
pge->room_location = 0xFE;
|
||||
pge->flags &= ~4;
|
||||
_pge_liveTable2[pge->index] = 0;
|
||||
LivePGE *inv_pge = pge_getInventoryItemBefore(&_pgeLive[args->a], pge);
|
||||
LivePGE *inv_pge = pge_getPreviousInventoryItem(&_pgeLive[args->a], pge);
|
||||
if (inv_pge == &_pgeLive[args->a]) {
|
||||
if (pge->index != inv_pge->current_inventory_PGE) {
|
||||
return 1;
|
||||
|
@ -1266,14 +1271,11 @@ int Game::pge_op_incLife(ObjectOpcodeArgs *args) {
|
|||
return 1;
|
||||
}
|
||||
|
||||
// level2, Ian
|
||||
int Game::pge_op_setPiegeDefaultAnim(ObjectOpcodeArgs *args) {
|
||||
assert(args->a >= 0 && args->a < 4);
|
||||
int16_t r = args->pge->init_PGE->counter_values[args->a];
|
||||
args->pge->room_location = r;
|
||||
if (r == 1) {
|
||||
// this happens after death tower, on earth, when Conrad passes
|
||||
// by the first policeman who's about to shoot him in the back
|
||||
InitPGE *init_pge = args->pge->init_PGE;
|
||||
args->pge->room_location = init_pge->data[args->a];
|
||||
if (init_pge->object_type == 1) {
|
||||
_loadMap = true;
|
||||
}
|
||||
pge_setupDefaultAnim(args->pge);
|
||||
|
@ -1281,7 +1283,7 @@ int Game::pge_op_setPiegeDefaultAnim(ObjectOpcodeArgs *args) {
|
|||
}
|
||||
|
||||
int Game::pge_op_setLifeCounter(ObjectOpcodeArgs *args) {
|
||||
_pgeLive[args->a].life = args->pge->init_PGE->counter_values[0];
|
||||
_pgeLive[args->a].life = args->pge->init_PGE->data[0];
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
@ -1297,8 +1299,9 @@ int Game::pge_op_playCutscene(ObjectOpcodeArgs *args) {
|
|||
return 1;
|
||||
}
|
||||
|
||||
int Game::pge_op_isTempVar2Set(ObjectOpcodeArgs *args) {
|
||||
if (_pge_opTempVar2 == args->a) {
|
||||
// unused
|
||||
int Game::pge_op_compareUnkVar(ObjectOpcodeArgs *args) {
|
||||
if (args->a == -1) {
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
|
@ -1306,7 +1309,7 @@ int Game::pge_op_isTempVar2Set(ObjectOpcodeArgs *args) {
|
|||
|
||||
int Game::pge_op_playDeathCutscene(ObjectOpcodeArgs *args) {
|
||||
if (_deathCutsceneCounter == 0) {
|
||||
_deathCutsceneCounter = args->pge->init_PGE->counter_values[3] + 1;
|
||||
_deathCutsceneCounter = args->pge->init_PGE->data[3] + 1;
|
||||
_cut._deathCutsceneId = args->a;
|
||||
}
|
||||
return 1;
|
||||
|
@ -1327,7 +1330,7 @@ int Game::pge_o_unk0x5F(ObjectOpcodeArgs *args) {
|
|||
if (pge_room < 0 || pge_room >= 0x40) return 0;
|
||||
|
||||
int16_t dx;
|
||||
int16_t _cx = pge->init_PGE->counter_values[0];
|
||||
int16_t _cx = pge->init_PGE->data[0];
|
||||
if (_cx <= 0) {
|
||||
dx = 1;
|
||||
_cx = -_cx;
|
||||
|
@ -1367,7 +1370,7 @@ int Game::pge_op_findAndCopyPiege(ObjectOpcodeArgs *args) {
|
|||
MessagePGE *le = _pge_messagesTable[args->pge->index];
|
||||
while (le) {
|
||||
if (le->msg_num == args->a) {
|
||||
args->a = le->index;
|
||||
args->a = le->src_pge;
|
||||
args->b = 0;
|
||||
pge_op_copyPiege(args);
|
||||
return 1;
|
||||
|
@ -1401,16 +1404,16 @@ int Game::pge_o_unk0x64(ObjectOpcodeArgs *args) {
|
|||
|
||||
int Game::pge_op_addToCredits(ObjectOpcodeArgs *args) {
|
||||
assert(args->a >= 0 && args->a < 3);
|
||||
uint8_t pge = args->pge->init_PGE->counter_values[args->a];
|
||||
int16_t val = args->pge->init_PGE->counter_values[args->a + 1];
|
||||
uint8_t pge = args->pge->init_PGE->data[args->a];
|
||||
int16_t val = args->pge->init_PGE->data[args->a + 1];
|
||||
_pgeLive[pge].life += val;
|
||||
return 1;
|
||||
}
|
||||
|
||||
int Game::pge_op_subFromCredits(ObjectOpcodeArgs *args) {
|
||||
assert(args->a >= 0 && args->a < 3);
|
||||
uint8_t pge = args->pge->init_PGE->counter_values[args->a];
|
||||
int16_t val = args->pge->init_PGE->counter_values[args->a + 1];
|
||||
uint8_t pge = args->pge->init_PGE->data[args->a];
|
||||
int16_t val = args->pge->init_PGE->data[args->a + 1];
|
||||
_pgeLive[pge].life -= val;
|
||||
return 1;
|
||||
}
|
||||
|
@ -1568,7 +1571,7 @@ int Game::pge_isToggleable(ObjectOpcodeArgs *args) {
|
|||
}
|
||||
|
||||
int Game::pge_o_unk0x6C(ObjectOpcodeArgs *args) {
|
||||
LivePGE *pge = col_findPiege(&_pgeLive[0], args->pge->init_PGE->counter_values[0]);
|
||||
LivePGE *pge = col_findPiege(&_pgeLive[0], args->pge->init_PGE->data[0]);
|
||||
if (pge) {
|
||||
if (pge->life <= args->pge->life) {
|
||||
pge_sendMessage(args->pge->index, pge->index, args->a);
|
||||
|
@ -1592,7 +1595,7 @@ int Game::pge_o_unk0x6E(ObjectOpcodeArgs *args) {
|
|||
MessagePGE *le = _pge_messagesTable[args->pge->index];
|
||||
while (le) {
|
||||
if (args->a == le->msg_num) {
|
||||
pge_updateInventory(&_pgeLive[le->index], args->pge);
|
||||
pge_updateInventory(&_pgeLive[le->src_pge], args->pge);
|
||||
return 0xFFFF;
|
||||
}
|
||||
le = le->next_entry;
|
||||
|
@ -1606,7 +1609,7 @@ int Game::pge_o_unk0x6F(ObjectOpcodeArgs *args) {
|
|||
MessagePGE *le = _pge_messagesTable[pge->index];
|
||||
while (le) {
|
||||
if (args->a == le->msg_num) {
|
||||
pge_sendMessage(pge->index, le->index, 0xC);
|
||||
pge_sendMessage(pge->index, le->src_pge, 0xC);
|
||||
return 1;
|
||||
}
|
||||
le = le->next_entry;
|
||||
|
@ -1685,7 +1688,7 @@ int Game::pge_op_isBelowConrad(ObjectOpcodeArgs *args) {
|
|||
if ((pge_conrad->pos_y - 8) / 72 < _si->pos_y / 72) {
|
||||
return 0xFFFF;
|
||||
}
|
||||
} else if (!(_si->room_location & 0x80) && _si->room_location < 0x40) {
|
||||
} else if (_si->room_location < 0x40) {
|
||||
if (pge_conrad->room_location == _res._ctData[CT_UP_ROOM + _si->room_location]) {
|
||||
return 0xFFFF;
|
||||
}
|
||||
|
@ -1700,7 +1703,7 @@ int Game::pge_op_isAboveConrad(ObjectOpcodeArgs *args) {
|
|||
if ((pge_conrad->pos_y - 8) / 72 > _si->pos_y / 72) {
|
||||
return 0xFFFF;
|
||||
}
|
||||
} else if (!(_si->room_location & 0x80) && _si->room_location < 0x40) {
|
||||
} else if (_si->room_location < 0x40) {
|
||||
if (pge_conrad->room_location == _res._ctData[CT_DOWN_ROOM + _si->room_location]) {
|
||||
return 0xFFFF;
|
||||
}
|
||||
|
@ -1735,7 +1738,7 @@ int Game::pge_op_isNotFacingConrad(ObjectOpcodeArgs *args) {
|
|||
}
|
||||
}
|
||||
} else if (args->a == 0) {
|
||||
if (!(pge->room_location & 0x80) && pge->room_location < 0x40) {
|
||||
if (pge->room_location < 0x40) {
|
||||
if (_pge_currentPiegeFacingDir) {
|
||||
if (pge_conrad->room_location == _res._ctData[CT_RIGHT_ROOM + pge->room_location])
|
||||
return 0xFFFF;
|
||||
|
@ -1776,7 +1779,7 @@ int Game::pge_op_isFacingConrad(ObjectOpcodeArgs *args) {
|
|||
}
|
||||
}
|
||||
} else if (args->a == 0) {
|
||||
if (!(pge->room_location & 0x80) && pge->room_location < 0x40) {
|
||||
if (pge->room_location < 0x40) {
|
||||
if (_pge_currentPiegeFacingDir) {
|
||||
if (pge_conrad->room_location == _res._ctData[CT_LEFT_ROOM + pge->room_location])
|
||||
return 0xFFFF;
|
||||
|
@ -1842,7 +1845,7 @@ int Game::pge_o_unk0x7F(ObjectOpcodeArgs *args) {
|
|||
CollisionSlot *slot = _col_slotsTable[var4];
|
||||
while (slot) {
|
||||
if (slot->live_pge != args->pge) {
|
||||
if (slot->live_pge->init_PGE->object_type == 3 && var2 != slot->live_pge->unkF) {
|
||||
if (slot->live_pge->init_PGE->object_type == 3 && var2 != slot->live_pge->ref_inventory_PGE) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
@ -1856,7 +1859,7 @@ int Game::pge_o_unk0x7F(ObjectOpcodeArgs *args) {
|
|||
}
|
||||
|
||||
int Game::pge_op_setPiegePosX(ObjectOpcodeArgs *args) {
|
||||
uint8_t pge_num = args->pge->unkF;
|
||||
uint8_t pge_num = args->pge->ref_inventory_PGE;
|
||||
if (pge_num != 0xFF) {
|
||||
args->pge->pos_x = _pgeLive[pge_num].pos_x;
|
||||
}
|
||||
|
@ -1864,7 +1867,7 @@ int Game::pge_op_setPiegePosX(ObjectOpcodeArgs *args) {
|
|||
}
|
||||
|
||||
int Game::pge_op_setPiegePosModX(ObjectOpcodeArgs *args) {
|
||||
uint8_t pge_num = args->pge->unkF;
|
||||
uint8_t pge_num = args->pge->ref_inventory_PGE;
|
||||
if (pge_num != 0xFF) {
|
||||
int16_t dx = _pgeLive[pge_num].pos_x % 256;
|
||||
if (dx >= args->pge->pos_x) {
|
||||
|
@ -1877,30 +1880,27 @@ int Game::pge_op_setPiegePosModX(ObjectOpcodeArgs *args) {
|
|||
|
||||
// taxi and teleporter
|
||||
int Game::pge_op_changeRoom(ObjectOpcodeArgs *args) {
|
||||
// pge_op_protectionScreen
|
||||
InitPGE *init_pge_1 = args->pge->init_PGE;
|
||||
assert(args->a >= 0 && args->a < 3);
|
||||
const int16_t _ax = init_pge_1->counter_values[args->a];
|
||||
if (_ax == 0 && !g_options.bypass_protection && !g_options.use_words_protection && !_res.isMac()) {
|
||||
const int16_t _ax = init_pge_1->data[args->a];
|
||||
if (_ax == 0 && !g_options.bypass_protection && !g_options.use_words_protection && (_res.isAmiga() || _res.isDOS())) {
|
||||
if (!handleProtectionScreenShape()) {
|
||||
warning("Game::pge_op_changeRoom() protection check failed");
|
||||
// when protection check fails, the changeRoom opcode is disabled,
|
||||
// rendering the teleporter unusable.
|
||||
//
|
||||
// _pge_opcodeTable[0x82] = &Game::pge_op_nop;
|
||||
// _pge_opTempVar1 = 0xFFFF;
|
||||
// _pge_opGunVar = 0;
|
||||
// return;
|
||||
}
|
||||
}
|
||||
const int16_t _bx = init_pge_1->counter_values[args->a + 1];
|
||||
// pge_op_changeRoom
|
||||
const int16_t _bx = init_pge_1->data[args->a + 1];
|
||||
LivePGE *live_pge_1 = &_pgeLive[_bx];
|
||||
LivePGE *live_pge_2 = &_pgeLive[_ax];
|
||||
int8_t pge_room = live_pge_1->room_location;
|
||||
if (pge_room >= 0 && pge_room < 0x40) {
|
||||
int8_t _al = live_pge_2->room_location;
|
||||
const int8_t room = live_pge_2->room_location;
|
||||
live_pge_2->pos_x = live_pge_1->pos_x;
|
||||
live_pge_2->pos_y = live_pge_1->pos_y;
|
||||
live_pge_2->room_location = live_pge_1->room_location;
|
||||
pge_addToCurrentRoomList(live_pge_2, _al);
|
||||
pge_addToCurrentRoomList(live_pge_2, room);
|
||||
InitPGE *init_pge_2 = live_pge_2->init_PGE;
|
||||
init_pge_1 = live_pge_1->init_PGE;
|
||||
if (init_pge_2->obj_node_number == init_pge_1->obj_node_number) {
|
||||
|
@ -1962,7 +1962,7 @@ int Game::pge_o_unk0x86(ObjectOpcodeArgs *args) {
|
|||
|
||||
int Game::pge_op_playSoundGroup(ObjectOpcodeArgs *args) {
|
||||
assert(args->a < 4);
|
||||
uint16_t c = args->pge->init_PGE->counter_values[args->a];
|
||||
uint16_t c = args->pge->init_PGE->data[args->a];
|
||||
uint8_t sfxId = c & 0xFF;
|
||||
uint8_t softVol = c >> 8;
|
||||
playSound(sfxId, softVol);
|
||||
|
@ -1978,13 +1978,13 @@ int Game::pge_op_adjustPos(ObjectOpcodeArgs *args) {
|
|||
return 0xFFFF;
|
||||
}
|
||||
|
||||
int Game::pge_op_setTempVar1(ObjectOpcodeArgs *args) {
|
||||
_pge_opTempVar1 = args->a;
|
||||
int Game::pge_op_setGunVar(ObjectOpcodeArgs *args) {
|
||||
_pge_opGunVar = args->a;
|
||||
return 0xFFFF;
|
||||
}
|
||||
|
||||
int Game::pge_op_isTempVar1Set(ObjectOpcodeArgs *args) {
|
||||
if (_pge_opTempVar1 != args->a) {
|
||||
int Game::pge_op_compareGunVar(ObjectOpcodeArgs *args) {
|
||||
if (_pge_opGunVar != args->a) {
|
||||
return 0;
|
||||
} else {
|
||||
return 0xFFFF;
|
||||
|
@ -1992,7 +1992,7 @@ int Game::pge_op_isTempVar1Set(ObjectOpcodeArgs *args) {
|
|||
}
|
||||
|
||||
int Game::pge_setCurrentInventoryObject(LivePGE *pge) {
|
||||
LivePGE *_bx = pge_getInventoryItemBefore(&_pgeLive[0], pge);
|
||||
LivePGE *_bx = pge_getPreviousInventoryItem(&_pgeLive[0], pge);
|
||||
if (_bx == &_pgeLive[0]) {
|
||||
if (_bx->current_inventory_PGE != pge->index) {
|
||||
return 0;
|
||||
|
@ -2008,17 +2008,17 @@ int Game::pge_setCurrentInventoryObject(LivePGE *pge) {
|
|||
}
|
||||
|
||||
void Game::pge_updateInventory(LivePGE *pge1, LivePGE *pge2) {
|
||||
if (pge2->unkF != 0xFF) {
|
||||
if (pge2->ref_inventory_PGE != 0xFF) {
|
||||
pge_reorderInventory(pge2);
|
||||
}
|
||||
LivePGE *_ax = pge_getInventoryItemBefore(pge1, 0);
|
||||
LivePGE *_ax = pge_getPreviousInventoryItem(pge1, 0);
|
||||
pge_addToInventory(_ax, pge2, pge1);
|
||||
}
|
||||
|
||||
void Game::pge_reorderInventory(LivePGE *pge) {
|
||||
if (pge->unkF != 0xFF) {
|
||||
LivePGE *_bx = &_pgeLive[pge->unkF];
|
||||
LivePGE *_di = pge_getInventoryItemBefore(_bx, pge);
|
||||
if (pge->ref_inventory_PGE != 0xFF) {
|
||||
LivePGE *_bx = &_pgeLive[pge->ref_inventory_PGE];
|
||||
LivePGE *_di = pge_getPreviousInventoryItem(_bx, pge);
|
||||
if (_di == _bx) {
|
||||
if (_di->current_inventory_PGE == pge->index) {
|
||||
pge_removeFromInventory(_di, pge, _bx);
|
||||
|
@ -2031,7 +2031,7 @@ void Game::pge_reorderInventory(LivePGE *pge) {
|
|||
}
|
||||
}
|
||||
|
||||
LivePGE *Game::pge_getInventoryItemBefore(LivePGE *pge, LivePGE *last_pge) {
|
||||
LivePGE *Game::pge_getPreviousInventoryItem(LivePGE *pge, LivePGE *last_pge) {
|
||||
LivePGE *_di = pge;
|
||||
uint8_t n = _di->current_inventory_PGE;
|
||||
while (n != 0xFF) {
|
||||
|
@ -2047,7 +2047,7 @@ LivePGE *Game::pge_getInventoryItemBefore(LivePGE *pge, LivePGE *last_pge) {
|
|||
}
|
||||
|
||||
void Game::pge_addToInventory(LivePGE *pge1, LivePGE *pge2, LivePGE *pge3) {
|
||||
pge2->unkF = pge3->index;
|
||||
pge2->ref_inventory_PGE = pge3->index;
|
||||
if (pge1 == pge3) {
|
||||
pge2->next_inventory_PGE = pge1->current_inventory_PGE;
|
||||
pge1->current_inventory_PGE = pge2->index;
|
||||
|
@ -2057,9 +2057,9 @@ void Game::pge_addToInventory(LivePGE *pge1, LivePGE *pge2, LivePGE *pge3) {
|
|||
}
|
||||
}
|
||||
|
||||
int Game::pge_updateCollisionState(LivePGE *pge, int16_t pge_dy, uint8_t var8) {
|
||||
uint8_t pge_unk1C = pge->init_PGE->unk1C;
|
||||
if (!(pge->room_location & 0x80) && pge->room_location < 0x40) {
|
||||
int Game::pge_updateCollisionState(LivePGE *pge, int16_t pge_dy, uint8_t value) {
|
||||
const uint8_t pge_collision_data_len = pge->init_PGE->collision_data_len;
|
||||
if (pge->room_location < 0x40) {
|
||||
int8_t *grid_data = &_res._ctData[0x100] + 0x70 * pge->room_location;
|
||||
int16_t pge_pos_y = ((pge->pos_y / 36) & ~1) + pge_dy;
|
||||
int16_t pge_pos_x = (pge->pos_x + 8) >> 4;
|
||||
|
@ -2069,14 +2069,14 @@ int Game::pge_updateCollisionState(LivePGE *pge, int16_t pge_dy, uint8_t var8) {
|
|||
CollisionSlot2 *slot1 = _col_slots2Next;
|
||||
int16_t i = 255;
|
||||
if (_pge_currentPiegeFacingDir) {
|
||||
i = pge_unk1C - 1;
|
||||
i = pge_collision_data_len - 1;
|
||||
grid_data -= i;
|
||||
}
|
||||
while (slot1) {
|
||||
if (slot1->unk2 == grid_data) {
|
||||
slot1->data_size = pge_unk1C - 1;
|
||||
assert(pge_unk1C < 0x70);
|
||||
memset(grid_data, var8, pge_unk1C);
|
||||
slot1->data_size = pge_collision_data_len - 1;
|
||||
assert(pge_collision_data_len < 0x70);
|
||||
memset(grid_data, value, pge_collision_data_len);
|
||||
return 1;
|
||||
} else {
|
||||
++i;
|
||||
|
@ -2089,14 +2089,14 @@ int Game::pge_updateCollisionState(LivePGE *pge, int16_t pge_dy, uint8_t var8) {
|
|||
if (_col_slots2Cur < &_col_slots2[255]) {
|
||||
slot1 = _col_slots2Cur;
|
||||
slot1->unk2 = grid_data;
|
||||
slot1->data_size = pge_unk1C - 1;
|
||||
slot1->data_size = pge_collision_data_len - 1;
|
||||
uint8_t *dst = &slot1->data_buf[0];
|
||||
int8_t *src = grid_data;
|
||||
int n = pge_unk1C;
|
||||
int n = pge_collision_data_len;
|
||||
assert(n < 0x10);
|
||||
while (n--) {
|
||||
*dst++ = *src;
|
||||
*src++ = var8;
|
||||
*src++ = value;
|
||||
}
|
||||
++_col_slots2Cur;
|
||||
slot1->next_slot = _col_slots2Next;
|
||||
|
@ -2147,10 +2147,16 @@ void Game::pge_sendMessage(uint8_t src_pge_index, uint8_t dst_pge_index, int16_t
|
|||
if (pge_room != pge->room_location) {
|
||||
return;
|
||||
}
|
||||
if (dst_pge_index == 0 && _blinkingConradCounter != 0) {
|
||||
if (dst_pge_index == 0 && (_blinkingConradCounter != 0 || (_cheats & kCheatNoHit) != 0)) {
|
||||
return;
|
||||
}
|
||||
// XXX
|
||||
if (_stub->_pi.dbgMask & PlayerInput::DF_AUTOZOOM) {
|
||||
const int type = _pgeLive[dst_pge_index].init_PGE->object_type;
|
||||
if (type == 1 || type == 10) {
|
||||
_pge_zoomPiegeNum = dst_pge_index;
|
||||
_pge_zoomCounter = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
MessagePGE *le = _pge_nextFreeMessage;
|
||||
if (le) {
|
||||
|
@ -2159,13 +2165,13 @@ void Game::pge_sendMessage(uint8_t src_pge_index, uint8_t dst_pge_index, int16_t
|
|||
MessagePGE *next = _pge_messagesTable[dst_pge_index];
|
||||
_pge_messagesTable[dst_pge_index] = le;
|
||||
le->next_entry = next;
|
||||
le->index = src_pge_index;
|
||||
le->src_pge = src_pge_index;
|
||||
le->msg_num = num;
|
||||
}
|
||||
}
|
||||
|
||||
void Game::pge_removeFromInventory(LivePGE *pge1, LivePGE *pge2, LivePGE *pge3) {
|
||||
pge2->unkF = 0xFF;
|
||||
pge2->ref_inventory_PGE = 0xFF;
|
||||
if (pge3 == pge1) {
|
||||
pge3->current_inventory_PGE = pge2->next_inventory_PGE;
|
||||
pge2->next_inventory_PGE = 0xFF;
|
||||
|
@ -2269,3 +2275,69 @@ int Game::pge_ZOrderIfTypeAndDifferentDirection(LivePGE *pge1, LivePGE *pge2, ui
|
|||
int Game::pge_ZOrderByNumber(LivePGE *pge1, LivePGE *pge2, uint8_t comp, uint8_t comp2) {
|
||||
return pge1 - pge2;
|
||||
}
|
||||
|
||||
static int pge_zoomDx(int prev_x, int cur_x) {
|
||||
int dx = ABS(cur_x - prev_x);
|
||||
if (dx < 4) {
|
||||
dx = 1;
|
||||
} else if (dx < 8) {
|
||||
dx = 2;
|
||||
} else if (dx < 16) {
|
||||
dx = 4;
|
||||
} else {
|
||||
dx = 8;
|
||||
}
|
||||
return (prev_x < cur_x) ? dx : -dx;
|
||||
}
|
||||
|
||||
static int pge_zoomDy(int prev_y, int cur_y, bool flag) {
|
||||
int dy = ABS(cur_y - prev_y);
|
||||
if (flag) {
|
||||
if (dy < 2) {
|
||||
return 0;
|
||||
}
|
||||
} else {
|
||||
if (dy < 4) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
if (dy < 8) {
|
||||
dy = 2;
|
||||
} else if (dy < 16) {
|
||||
dy = 4;
|
||||
} else {
|
||||
dy = 8;
|
||||
}
|
||||
return (prev_y < cur_y) ? dy : -dy;
|
||||
}
|
||||
|
||||
void Game::pge_updateZoom() {
|
||||
static const int kZoomW = Video::GAMESCREEN_W / 2;
|
||||
static const int kZoomH = Video::GAMESCREEN_H / 2;
|
||||
if (_pge_zoomPiegeNum != 0) {
|
||||
LivePGE *pge = &_pgeLive[_pge_zoomPiegeNum];
|
||||
if (pge->room_location != _currentRoom) {
|
||||
_pge_zoomPiegeNum = 0;
|
||||
} else if (_pge_zoomCounter < 30) {
|
||||
int x = pge->pos_x + ((_pgeLive[0].flags & 1) ? 22 - kZoomW : -12);
|
||||
x = CLIP(x, 0, Video::GAMESCREEN_W - kZoomW);
|
||||
if (_pge_zoomCounter != 0 && _pge_zoomX != x) {
|
||||
const int dx = pge_zoomDx(_pge_zoomX, x);
|
||||
x = _pge_zoomX + dx;
|
||||
}
|
||||
_pge_zoomX = x;
|
||||
int y = pge->pos_y - 24 - kZoomH / 2;
|
||||
y = CLIP(y, 0, Video::GAMESCREEN_H - kZoomH);
|
||||
if (_pge_zoomCounter != 0 && _pge_zoomY != y) {
|
||||
const int dy = pge_zoomDy(_pge_zoomY, y, (pge->ref_inventory_PGE != 0xFF));
|
||||
y = _pge_zoomY + dy;
|
||||
}
|
||||
_pge_zoomY = y;
|
||||
_stub->zoomRect(x, y, kZoomW, kZoomH);
|
||||
}
|
||||
++_pge_zoomCounter;
|
||||
if (_pge_zoomCounter == 40) {
|
||||
_pge_zoomPiegeNum = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
12
resource.cpp
12
resource.cpp
|
@ -1064,7 +1064,7 @@ void Resource::load_PGE(File *f) {
|
|||
pge->obj_node_number = f->readUint16LE();
|
||||
pge->life = f->readUint16LE();
|
||||
for (int lc = 0; lc < 4; ++lc) {
|
||||
pge->counter_values[lc] = f->readUint16LE();
|
||||
pge->data[lc] = f->readUint16LE();
|
||||
}
|
||||
pge->object_type = f->readByte();
|
||||
pge->init_room = f->readByte();
|
||||
|
@ -1076,7 +1076,7 @@ void Resource::load_PGE(File *f) {
|
|||
pge->skill = f->readByte();
|
||||
pge->mirror_x = f->readByte();
|
||||
pge->flags = f->readByte();
|
||||
pge->unk1C = f->readByte();
|
||||
pge->collision_data_len = f->readByte();
|
||||
f->readByte();
|
||||
pge->text_num = f->readUint16LE();
|
||||
}
|
||||
|
@ -1095,7 +1095,7 @@ void Resource::decodePGE(const uint8_t *p, int size) {
|
|||
pge->obj_node_number = _readUint16(p); p += 2;
|
||||
pge->life = _readUint16(p); p += 2;
|
||||
for (int lc = 0; lc < 4; ++lc) {
|
||||
pge->counter_values[lc] = _readUint16(p); p += 2;
|
||||
pge->data[lc] = _readUint16(p); p += 2;
|
||||
}
|
||||
pge->object_type = *p++;
|
||||
pge->init_room = *p++;
|
||||
|
@ -1107,7 +1107,7 @@ void Resource::decodePGE(const uint8_t *p, int size) {
|
|||
pge->skill = *p++;
|
||||
pge->mirror_x = *p++;
|
||||
pge->flags = *p++;
|
||||
pge->unk1C = *p++;
|
||||
pge->collision_data_len = *p++;
|
||||
++p;
|
||||
pge->text_num = _readUint16(p); p += 2;
|
||||
}
|
||||
|
@ -1417,6 +1417,10 @@ uint8_t *Resource::loadBankData(uint16_t num) {
|
|||
dataOffset &= 0xFFFF;
|
||||
}
|
||||
const int size = getBankDataSize(num);
|
||||
if (size == 0) {
|
||||
warning("Invalid bank data %d", num);
|
||||
return _bankDataHead;
|
||||
}
|
||||
const int avail = _bankDataTail - _bankDataHead;
|
||||
if (avail < size) {
|
||||
clearBankData();
|
||||
|
|
|
@ -15,7 +15,7 @@ ResourceAba::~ResourceAba() {
|
|||
}
|
||||
|
||||
static int compareAbaEntry(const void *a, const void *b) {
|
||||
return strcasecmp(((ResourceAbaEntry *)a)->name, ((ResourceAbaEntry *)b)->name);
|
||||
return strcasecmp(((const ResourceAbaEntry *)a)->name, ((const ResourceAbaEntry *)b)->name);
|
||||
}
|
||||
|
||||
void ResourceAba::readEntries(const char *aba) {
|
||||
|
|
7
rs.cfg
7
rs.cfg
|
@ -19,14 +19,14 @@ use_text_cutscenes=false
|
|||
# enable playback of PC CD .SEQ cutscenes (use polygon cutscenes if false)
|
||||
use_seq_cutscenes=true
|
||||
|
||||
# if copy protection is enabled, display the words manual lookup screen (as in DOS SSI version).
|
||||
# if copy protection is enabled, display the words manual lookup screen (as in DOS SSI version)
|
||||
use_words_protection=false
|
||||
|
||||
# white t-shirt for Conrad
|
||||
use_white_tshirt=false
|
||||
|
||||
# enable playback of 'ASC' cutscene
|
||||
play_asc_cutscene=true
|
||||
play_asc_cutscene=false
|
||||
|
||||
# enable playback of 'CAILLOU-F.SET' cutscene
|
||||
play_caillou_cutscene=true
|
||||
|
@ -45,3 +45,6 @@ play_gamesaved_sound=true
|
|||
|
||||
# restore content from 'MEMO' cutscene
|
||||
restore_memo_cutscene=true
|
||||
|
||||
# order inventory items as the original game, last grabbed item is at the bottom of the inventory
|
||||
order_inventory_original=false
|
||||
|
|
|
@ -2,83 +2,29 @@
|
|||
#include "screenshot.h"
|
||||
#include "file.h"
|
||||
|
||||
#define kTgaImageTypeUncompressedTrueColor 2
|
||||
#define kTgaImageTypeRunLengthEncodedTrueColor 10
|
||||
#define kTgaDirectionTop (1 << 5)
|
||||
|
||||
void saveTGA(const char *filename, const uint8_t *rgba, int w, int h) {
|
||||
static const uint8_t kImageType = kTgaImageTypeRunLengthEncodedTrueColor;
|
||||
File f;
|
||||
if (f.open(filename, "wb", ".")) {
|
||||
|
||||
f.writeByte(0); // ID Length
|
||||
f.writeByte(0); // ColorMap Type
|
||||
f.writeByte(kImageType);
|
||||
f.writeUint16LE(0); // ColorMap Start
|
||||
f.writeUint16LE(0); // ColorMap Length
|
||||
f.writeByte(0); // ColorMap Bits
|
||||
f.writeUint16LE(0); // X-origin
|
||||
f.writeUint16LE(0); // Y-origin
|
||||
f.writeUint16LE(w); // Image Width
|
||||
f.writeUint16LE(h); // Image Height
|
||||
f.writeByte(24); // Pixel Depth
|
||||
f.writeByte(kTgaDirectionTop); // Descriptor
|
||||
|
||||
if (kImageType == kTgaImageTypeUncompressedTrueColor) {
|
||||
for (int i = 0; i < w * h; ++i) {
|
||||
f.writeByte(rgba[0]);
|
||||
f.writeByte(rgba[1]);
|
||||
f.writeByte(rgba[2]);
|
||||
rgba += 4;
|
||||
}
|
||||
} else {
|
||||
assert(kImageType == kTgaImageTypeRunLengthEncodedTrueColor);
|
||||
int prevColor = rgba[2] + (rgba[1] << 8) + (rgba[0] << 16); rgba += 4;
|
||||
int count = 0;
|
||||
for (int i = 1; i < w * h; ++i) {
|
||||
int color = rgba[2] + (rgba[1] << 8) + (rgba[0] << 16); rgba += 4;
|
||||
if (prevColor == color && count < 127) {
|
||||
++count;
|
||||
continue;
|
||||
}
|
||||
f.writeByte(count | 0x80);
|
||||
f.writeByte((prevColor >> 16) & 255);
|
||||
f.writeByte((prevColor >> 8) & 255);
|
||||
f.writeByte( prevColor & 255);
|
||||
count = 0;
|
||||
prevColor = color;
|
||||
}
|
||||
if (count != 0) {
|
||||
f.writeByte(count | 0x80);
|
||||
f.writeByte((prevColor >> 16) & 255);
|
||||
f.writeByte((prevColor >> 8) & 255);
|
||||
f.writeByte( prevColor & 255);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static const uint16_t TAG_BM = 0x4D42;
|
||||
|
||||
void saveBMP(const char *filename, const uint8_t *bits, const uint8_t *pal, int w, int h) {
|
||||
File f;
|
||||
if (f.open(filename, "wb", ".")) {
|
||||
const int alignWidth = (w + 3) & ~3;
|
||||
const int paletteSize = pal ? 4 * 256 : 0;
|
||||
const int bitsCount = pal ? 8 : 32;
|
||||
const int alignWidth = ((w * bitsCount / 8) + 3) & ~3;
|
||||
const int imageSize = alignWidth * h;
|
||||
|
||||
// Write file header
|
||||
f.writeUint16LE(TAG_BM);
|
||||
f.writeUint32LE(14 + 40 + 4 * 256 + imageSize);
|
||||
f.writeUint32LE(14 + 40 + paletteSize + imageSize);
|
||||
f.writeUint16LE(0); // reserved1
|
||||
f.writeUint16LE(0); // reserved2
|
||||
f.writeUint32LE(14 + 40 + 4 * 256);
|
||||
f.writeUint32LE(14 + 40 + paletteSize);
|
||||
|
||||
// Write info header
|
||||
f.writeUint32LE(40);
|
||||
f.writeUint32LE(w);
|
||||
f.writeUint32LE(h);
|
||||
f.writeUint16LE(1); // planes
|
||||
f.writeUint16LE(8); // bit_count
|
||||
f.writeUint16LE(bitsCount); // bit_count
|
||||
f.writeUint32LE(0); // compression
|
||||
f.writeUint32LE(imageSize); // size_image
|
||||
f.writeUint32LE(0); // x_pels_per_meter
|
||||
|
@ -86,6 +32,7 @@ void saveBMP(const char *filename, const uint8_t *bits, const uint8_t *pal, int
|
|||
f.writeUint32LE(0); // num_colors_used
|
||||
f.writeUint32LE(0); // num_colors_important
|
||||
|
||||
if (pal) {
|
||||
// Write palette data
|
||||
for (int i = 0; i < 256; ++i) {
|
||||
f.writeByte(pal[2]);
|
||||
|
@ -94,8 +41,10 @@ void saveBMP(const char *filename, const uint8_t *bits, const uint8_t *pal, int
|
|||
f.writeByte(0);
|
||||
pal += 3;
|
||||
}
|
||||
}
|
||||
|
||||
// Write bitmap data
|
||||
w *= bitsCount / 8;
|
||||
const int pitch = w;
|
||||
bits += h * pitch;
|
||||
for (int i = 0; i < h; ++i) {
|
||||
|
|
|
@ -4,7 +4,6 @@
|
|||
|
||||
#include <stdint.h>
|
||||
|
||||
void saveTGA(const char *filename, const uint8_t *rgb, int w, int h);
|
||||
void saveBMP(const char *filename, const uint8_t *bits, const uint8_t *pal, int w, int h);
|
||||
|
||||
#endif
|
||||
|
|
|
@ -57,9 +57,7 @@ bool SeqDemuxer::readFrameData() {
|
|||
}
|
||||
_f->seek(_frameOffset);
|
||||
_audioDataOffset = _f->readUint16LE();
|
||||
_audioDataSize = (_audioDataOffset != 0) ? kAudioBufferSize * 2 : 0;
|
||||
_paletteDataOffset = _f->readUint16LE();
|
||||
_paletteDataSize = (_paletteDataOffset != 0) ? 768 : 0;
|
||||
uint8_t num[4];
|
||||
for (int i = 0; i < 4; ++i) {
|
||||
num[i] = _f->readByte();
|
||||
|
@ -220,6 +218,7 @@ SeqPlayer::SeqPlayer(SystemStub *stub, Mixer *mixer)
|
|||
: _stub(stub), _buf(0), _mix(mixer) {
|
||||
_soundQueuePreloadSize = 0;
|
||||
_soundQueue = 0;
|
||||
_soundQueueTail = 0;
|
||||
}
|
||||
|
||||
SeqPlayer::~SeqPlayer() {
|
||||
|
@ -242,7 +241,7 @@ void SeqPlayer::play(File *f) {
|
|||
if (!_demux.readFrameData()) {
|
||||
break;
|
||||
}
|
||||
if (_demux._audioDataSize != 0) {
|
||||
if (_demux._audioDataOffset != 0) {
|
||||
SoundBufferQueue *sbq = (SoundBufferQueue *)malloc(sizeof(SoundBufferQueue));
|
||||
if (sbq) {
|
||||
sbq->data = (int16_t *)calloc(SeqDemuxer::kAudioBufferSize, sizeof(int16_t));
|
||||
|
@ -258,25 +257,23 @@ void SeqPlayer::play(File *f) {
|
|||
}
|
||||
if (sbq) {
|
||||
LockAudioStack las(_stub);
|
||||
if (!_soundQueue) {
|
||||
_soundQueue = sbq;
|
||||
if (_soundQueueTail) {
|
||||
_soundQueueTail->next = sbq;
|
||||
} else {
|
||||
SoundBufferQueue *p = _soundQueue;
|
||||
while (p->next) {
|
||||
p = p->next;
|
||||
}
|
||||
p->next = sbq;
|
||||
assert(!_soundQueue);
|
||||
_soundQueue = sbq;
|
||||
}
|
||||
_soundQueueTail = sbq;
|
||||
if (_soundQueuePreloadSize < kSoundPreloadSize) {
|
||||
++_soundQueuePreloadSize;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (_demux._paletteDataSize != 0) {
|
||||
if (_demux._paletteDataOffset != 0) {
|
||||
uint8_t buf[256 * 3];
|
||||
_demux.readPalette(buf);
|
||||
for (int i = 0; i < 256 * 3; ++i) {
|
||||
buf[i] = (buf[i] << 2) | (buf[i] & 3);
|
||||
buf[i] = (buf[i] << 2) | (buf[i] >> 4);
|
||||
}
|
||||
_stub->setPalette(buf, 256);
|
||||
}
|
||||
|
@ -326,6 +323,7 @@ void SeqPlayer::play(File *f) {
|
|||
free(_soundQueue);
|
||||
_soundQueue = next;
|
||||
}
|
||||
_soundQueueTail = 0;
|
||||
_soundQueuePreloadSize = 0;
|
||||
}
|
||||
}
|
||||
|
@ -335,15 +333,20 @@ bool SeqPlayer::mix(int16_t *buf, int samples) {
|
|||
return true;
|
||||
}
|
||||
while (_soundQueue && samples > 0) {
|
||||
*buf++ = _soundQueue->data[_soundQueue->read];
|
||||
++_soundQueue->read;
|
||||
const int count = MIN(samples, _soundQueue->size - _soundQueue->read);
|
||||
memcpy(buf, _soundQueue->data + _soundQueue->read, count * sizeof(int16_t));
|
||||
buf += count;
|
||||
_soundQueue->read += count;
|
||||
if (_soundQueue->read == _soundQueue->size) {
|
||||
SoundBufferQueue *next = _soundQueue->next;
|
||||
free(_soundQueue->data);
|
||||
free(_soundQueue);
|
||||
_soundQueue = next;
|
||||
}
|
||||
--samples;
|
||||
samples -= count;
|
||||
}
|
||||
if (!_soundQueue) {
|
||||
_soundQueueTail = 0;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -32,9 +32,7 @@ struct SeqDemuxer {
|
|||
|
||||
int _frameOffset;
|
||||
int _audioDataOffset;
|
||||
int _audioDataSize;
|
||||
int _paletteDataOffset;
|
||||
int _paletteDataSize;
|
||||
int _videoData;
|
||||
struct {
|
||||
int size;
|
||||
|
@ -74,7 +72,7 @@ struct SeqPlayer {
|
|||
Mixer *_mix;
|
||||
SeqDemuxer _demux;
|
||||
int _soundQueuePreloadSize;
|
||||
SoundBufferQueue *_soundQueue;
|
||||
SoundBufferQueue *_soundQueue, *_soundQueueTail;
|
||||
};
|
||||
|
||||
#endif // SEQ_PLAYER_H__
|
||||
|
|
|
@ -3585,7 +3585,7 @@ const Game::pge_OpcodeProc Game::_pge_opcodeTable[] = {
|
|||
/* 0x48 */
|
||||
&Game::pge_o_unk0x48,
|
||||
&Game::pge_o_unk0x49,
|
||||
&Game::pge_o_unk0x4A,
|
||||
&Game::pge_op_killInventoryPiege,
|
||||
&Game::pge_op_killPiege,
|
||||
/* 0x4C */
|
||||
&Game::pge_op_isInCurrentRoom,
|
||||
|
@ -3606,7 +3606,7 @@ const Game::pge_OpcodeProc Game::_pge_opcodeTable[] = {
|
|||
&Game::pge_op_setLifeCounter,
|
||||
&Game::pge_op_decLifeCounter,
|
||||
&Game::pge_op_playCutscene,
|
||||
&Game::pge_op_isTempVar2Set,
|
||||
&Game::pge_op_compareUnkVar,
|
||||
/* 0x5C */
|
||||
&Game::pge_op_playDeathCutscene,
|
||||
&Game::pge_o_unk0x5D,
|
||||
|
@ -3665,8 +3665,8 @@ const Game::pge_OpcodeProc Game::_pge_opcodeTable[] = {
|
|||
/* 0x88 */
|
||||
&Game::pge_op_adjustPos,
|
||||
0,
|
||||
&Game::pge_op_setTempVar1,
|
||||
&Game::pge_op_isTempVar1Set
|
||||
&Game::pge_op_setGunVar,
|
||||
&Game::pge_op_compareGunVar
|
||||
};
|
||||
|
||||
const uint8_t Game::_pge_modKeysTable[] = {
|
||||
|
|
20
systemstub.h
20
systemstub.h
|
@ -20,7 +20,8 @@ struct PlayerInput {
|
|||
enum {
|
||||
DF_FASTMODE = 1 << 0,
|
||||
DF_DBLOCKS = 1 << 1,
|
||||
DF_SETLIFE = 1 << 2
|
||||
DF_SETLIFE = 1 << 2,
|
||||
DF_AUTOZOOM = 1 << 3
|
||||
};
|
||||
|
||||
uint8_t dirMask;
|
||||
|
@ -68,6 +69,7 @@ struct SystemStub {
|
|||
virtual void setOverscanColor(int i) = 0;
|
||||
virtual void copyRect(int x, int y, int w, int h, const uint8_t *buf, int pitch) = 0;
|
||||
virtual void copyRectRgb24(int x, int y, int w, int h, const uint8_t *rgb) = 0;
|
||||
virtual void zoomRect(int x, int y, int h, int w) = 0;
|
||||
virtual void copyWidescreenLeft(int w, int h, const uint8_t *buf) = 0;
|
||||
virtual void copyWidescreenRight(int w, int h, const uint8_t *buf) = 0;
|
||||
virtual void copyWidescreenMirror(int w, int h, const uint8_t *buf) = 0;
|
||||
|
@ -99,6 +101,22 @@ struct LockAudioStack {
|
|||
SystemStub *_stub;
|
||||
};
|
||||
|
||||
struct ToggleWidescreenStack {
|
||||
ToggleWidescreenStack(SystemStub *stub, bool state)
|
||||
: _stub(stub), _state(state) {
|
||||
if (_stub->hasWidescreen()) {
|
||||
_stub->enableWidescreen(_state);
|
||||
}
|
||||
}
|
||||
~ToggleWidescreenStack() {
|
||||
if (_stub->hasWidescreen()) {
|
||||
_stub->enableWidescreen(!_state);
|
||||
}
|
||||
}
|
||||
SystemStub *_stub;
|
||||
bool _state;
|
||||
};
|
||||
|
||||
extern SystemStub *SystemStub_SDL_create();
|
||||
|
||||
#endif // SYSTEMSTUB_H__
|
||||
|
|
|
@ -31,6 +31,7 @@ struct SystemStub_SDL : SystemStub {
|
|||
SDL_Renderer *_renderer;
|
||||
SDL_Texture *_texture;
|
||||
int _texW, _texH;
|
||||
SDL_Rect _texRect;
|
||||
SDL_GameController *_controller;
|
||||
SDL_PixelFormat *_fmt;
|
||||
const char *_caption;
|
||||
|
@ -66,6 +67,7 @@ struct SystemStub_SDL : SystemStub {
|
|||
virtual void setOverscanColor(int i);
|
||||
virtual void copyRect(int x, int y, int w, int h, const uint8_t *buf, int pitch);
|
||||
virtual void copyRectRgb24(int x, int y, int w, int h, const uint8_t *rgb);
|
||||
virtual void zoomRect(int x, int y, int w, int h);
|
||||
virtual void copyWidescreenLeft(int w, int h, const uint8_t *buf);
|
||||
virtual void copyWidescreenRight(int w, int h, const uint8_t *buf);
|
||||
virtual void copyWidescreenMirror(int w, int h, const uint8_t *buf);
|
||||
|
@ -98,7 +100,7 @@ SystemStub *SystemStub_SDL_create() {
|
|||
}
|
||||
|
||||
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_INIT_GAMECONTROLLER);
|
||||
SDL_ShowCursor(SDL_DISABLE);
|
||||
_caption = title;
|
||||
memset(&_pi, 0, sizeof(_pi));
|
||||
|
@ -269,6 +271,16 @@ void SystemStub_SDL::copyRectRgb24(int x, int y, int w, int h, const uint8_t *rg
|
|||
}
|
||||
}
|
||||
|
||||
void SystemStub_SDL::zoomRect(int x, int y, int w, int h) {
|
||||
if (_pi.dbgMask & PlayerInput::DF_DBLOCKS) {
|
||||
drawRect(x, y, w, h, 0xE7);
|
||||
}
|
||||
_texRect.x = x * _texW / _screenW;
|
||||
_texRect.y = y * _texH / _screenH;
|
||||
_texRect.w = w * _texW / _screenW;
|
||||
_texRect.h = h * _texH / _screenH;
|
||||
}
|
||||
|
||||
static void clearTexture(SDL_Texture *texture, int h, SDL_PixelFormat *fmt) {
|
||||
void *dst = 0;
|
||||
int pitch = 0;
|
||||
|
@ -502,7 +514,7 @@ void SystemStub_SDL::updateScreen(int shakeOffset) {
|
|||
SDL_RenderGetLogicalSize(_renderer, &r.w, &r.h);
|
||||
r.x = (r.w - _texW) / 2;
|
||||
r.w = _texW;
|
||||
SDL_RenderCopy(_renderer, _texture, 0, &r);
|
||||
SDL_RenderCopy(_renderer, _texture, &_texRect, &r);
|
||||
} else {
|
||||
if (_fadeOnUpdateScreen) {
|
||||
SDL_SetRenderDrawBlendMode(_renderer, SDL_BLENDMODE_BLEND);
|
||||
|
@ -524,9 +536,13 @@ void SystemStub_SDL::updateScreen(int shakeOffset) {
|
|||
r.x = 0;
|
||||
r.y = shakeOffset * _scaleFactor;
|
||||
SDL_RenderGetLogicalSize(_renderer, &r.w, &r.h);
|
||||
SDL_RenderCopy(_renderer, _texture, 0, &r);
|
||||
SDL_RenderCopy(_renderer, _texture, &_texRect, &r);
|
||||
}
|
||||
SDL_RenderPresent(_renderer);
|
||||
_texRect.x = 0;
|
||||
_texRect.y = 0;
|
||||
_texRect.w = _texW;
|
||||
_texRect.h = _texH;
|
||||
}
|
||||
|
||||
void SystemStub_SDL::processEvents() {
|
||||
|
@ -740,8 +756,8 @@ void SystemStub_SDL::processEvent(const SDL_Event &ev, bool &paused) {
|
|||
break;
|
||||
case SDLK_s: {
|
||||
char name[32];
|
||||
snprintf(name, sizeof(name), "screenshot-%03d.tga", _screenshot);
|
||||
saveTGA(name, (const uint8_t *)_screenBuffer, _screenW, _screenH);
|
||||
snprintf(name, sizeof(name), "screenshot-%03d.bmp", _screenshot);
|
||||
saveBMP(name, (const uint8_t *)_screenBuffer, 0, _screenW, _screenH);
|
||||
++_screenshot;
|
||||
debug(DBG_INFO, "Written '%s'", name);
|
||||
}
|
||||
|
@ -771,6 +787,10 @@ void SystemStub_SDL::processEvent(const SDL_Event &ev, bool &paused) {
|
|||
case SDLK_r:
|
||||
_pi.rewind = true;
|
||||
break;
|
||||
case SDLK_g:
|
||||
_pi.dbgMask ^= PlayerInput::DF_AUTOZOOM;
|
||||
debug(DBG_INFO, "Auto zoom %s", (_pi.dbgMask & PlayerInput::DF_AUTOZOOM) ? "enabled" : "disabled");
|
||||
break;
|
||||
case SDLK_KP_PLUS:
|
||||
case SDLK_PAGEUP:
|
||||
_pi.stateSlot = 1;
|
||||
|
@ -785,15 +805,19 @@ void SystemStub_SDL::processEvent(const SDL_Event &ev, bool &paused) {
|
|||
setAsciiChar(_pi, &ev.key.keysym);
|
||||
switch (ev.key.keysym.sym) {
|
||||
case SDLK_LEFT:
|
||||
case SDLK_KP_4:
|
||||
_pi.dirMask &= ~PlayerInput::DIR_LEFT;
|
||||
break;
|
||||
case SDLK_RIGHT:
|
||||
case SDLK_KP_6:
|
||||
_pi.dirMask &= ~PlayerInput::DIR_RIGHT;
|
||||
break;
|
||||
case SDLK_UP:
|
||||
case SDLK_KP_8:
|
||||
_pi.dirMask &= ~PlayerInput::DIR_UP;
|
||||
break;
|
||||
case SDLK_DOWN:
|
||||
case SDLK_KP_2:
|
||||
_pi.dirMask &= ~PlayerInput::DIR_DOWN;
|
||||
break;
|
||||
case SDLK_SPACE:
|
||||
|
@ -829,15 +853,19 @@ void SystemStub_SDL::processEvent(const SDL_Event &ev, bool &paused) {
|
|||
}
|
||||
switch (ev.key.keysym.sym) {
|
||||
case SDLK_LEFT:
|
||||
case SDLK_KP_4:
|
||||
_pi.dirMask |= PlayerInput::DIR_LEFT;
|
||||
break;
|
||||
case SDLK_RIGHT:
|
||||
case SDLK_KP_6:
|
||||
_pi.dirMask |= PlayerInput::DIR_RIGHT;
|
||||
break;
|
||||
case SDLK_UP:
|
||||
case SDLK_KP_8:
|
||||
_pi.dirMask |= PlayerInput::DIR_UP;
|
||||
break;
|
||||
case SDLK_DOWN:
|
||||
case SDLK_KP_2:
|
||||
_pi.dirMask |= PlayerInput::DIR_DOWN;
|
||||
break;
|
||||
case SDLK_BACKSPACE:
|
||||
|
@ -935,6 +963,10 @@ void SystemStub_SDL::prepareGraphics() {
|
|||
_texH *= _scaleFactor;
|
||||
break;
|
||||
}
|
||||
_texRect.x = 0;
|
||||
_texRect.y = 0;
|
||||
_texRect.w = _texW;
|
||||
_texRect.h = _texH;
|
||||
int windowW = _screenW * _scaleFactor;
|
||||
int windowH = _screenH * _scaleFactor;
|
||||
int flags = 0;
|
||||
|
@ -1061,7 +1093,7 @@ void SystemStub_SDL::setScaler(const ScalerParameters *parameters) {
|
|||
}
|
||||
}
|
||||
}
|
||||
_scaleFactor = _scaler ? CLIP(parameters->factor, _scaler->factorMin, _scaler->factorMax) : 1;
|
||||
_scaleFactor = _scaler ? CLIP(parameters->factor, _scaler->factorMin, _scaler->factorMax) : parameters->factor;
|
||||
}
|
||||
|
||||
void SystemStub_SDL::changeScaler(int scalerNum) {
|
||||
|
|
Loading…
Reference in New Issue