Import 0.4.4

This commit is contained in:
Gregory Montoir 2019-10-28 00:00:00 +08:00
parent 514785d0a1
commit 222984d851
25 changed files with 526 additions and 135 deletions

View File

@ -7,7 +7,7 @@ MODPLUG_LIBS := -lmodplug
TREMOR_LIBS := -lvorbisidec -logg TREMOR_LIBS := -lvorbisidec -logg
ZLIB_LIBS := -lz ZLIB_LIBS := -lz
CXXFLAGS += -O2 -Wall -MMD $(SDL_CFLAGS) -DUSE_MODPLUG -DUSE_STATIC_SCALER -DUSE_TREMOR -DUSE_ZLIB CXXFLAGS += -Wall -Wpedantic -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 \ 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 \ menu.cpp mixer.cpp mod_player.cpp ogg_player.cpp piege.cpp resource.cpp resource_aba.cpp \

View File

@ -1,6 +1,6 @@
REminiscence README REminiscence README
Release version: 0.4.3 Release version: 0.4.4
------------------------------------------------------------------------------- -------------------------------------------------------------------------------
@ -46,6 +46,7 @@ These paths can be changed using command line switches :
--widescreen=MODE 16:9 display --widescreen=MODE 16:9 display
--scaler=NAME@X Graphics scaler (default 'scale@3') --scaler=NAME@X Graphics scaler (default 'scale@3')
--language=LANG Language (fr,en,de,sp,it,jp) --language=LANG Language (fr,en,de,sp,it,jp)
--autosave Save game state automatically
The scaler option specifies the algorithm used to smoothen the image in The scaler option specifies the algorithm used to smoothen the image in
addition to a scaling factor. External scalers are also supported, the suffix addition to a scaling factor. External scalers are also supported, the suffix
@ -53,7 +54,7 @@ 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). '--scaler xbrz@2' to use that algorithm with a doubled window size (512x448).
The widescreen option accepts two modes : The widescreen option accepts two modes :
'adjacent' : left and right rooms bitmaps will be drawn (default) 'adjacent' : left and right rooms bitmaps will be drawn
'mirror' : the current room bitmap will be drawn mirrored 'mirror' : the current room bitmap will be drawn mirrored
In-game hotkeys : In-game hotkeys :
@ -83,7 +84,6 @@ Credits:
Delphine Software, obviously, for making another great game. Delphine Software, obviously, for making another great game.
Yaz0r, Pixel and gawd for sharing information they gathered on the game. Yaz0r, Pixel and gawd for sharing information they gathered on the game.
Nicolas Bondoux for sound fixes.
Contact: Contact:

View File

@ -112,12 +112,12 @@ static bool isNewLineChar(uint8_t chr, Resource *res) {
return chr == nl; return chr == nl;
} }
uint16_t Cutscene::findTextSeparators(const uint8_t *p) { uint16_t Cutscene::findTextSeparators(const uint8_t *p, int len) {
uint8_t *q = _textSep; uint8_t *q = _textSep;
uint16_t ret = 0; uint16_t ret = 0;
uint16_t pos = 0; uint16_t pos = 0;
for (; *p != 0xA && *p; ++p) { for (int i = 0; i < len && p[i] != 0xA; ++i) {
if (isNewLineChar(*p, _res)) { if (isNewLineChar(p[i], _res)) {
*q++ = pos; *q++ = pos;
if (pos > ret) { if (pos > ret) {
ret = pos; ret = pos;
@ -137,41 +137,43 @@ uint16_t Cutscene::findTextSeparators(const uint8_t *p) {
void Cutscene::drawText(int16_t x, int16_t y, const uint8_t *p, uint16_t color, uint8_t *page, int textJustify) { 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); debug(DBG_CUT, "Cutscene::drawText(x=%d, y=%d, c=%d, justify=%d)", x, y, color, textJustify);
int len = 0;
if (_res->_type == kResourceTypeMac) { if (_res->_type == kResourceTypeMac) {
warning("Unhandled Cutscene::drawText"); // TODO len = *p++;
return; } else {
len = strlen((const char *)p);
} }
Video::drawCharFunc dcf = _vid->_drawChar; Video::drawCharFunc dcf = _vid->_drawChar;
const uint8_t *fnt = (_res->_lang == LANG_JP) ? Video::_font8Jp : _res->_fnt; const uint8_t *fnt = (_res->_lang == LANG_JP) ? Video::_font8Jp : _res->_fnt;
uint16_t last_sep = 0; uint16_t lastSep = 0;
if (textJustify != kTextJustifyLeft) { if (textJustify != kTextJustifyLeft) {
last_sep = findTextSeparators(p); lastSep = findTextSeparators(p, len);
if (textJustify != kTextJustifyCenter) { if (textJustify != kTextJustifyCenter) {
last_sep = (_res->_lang == LANG_JP) ? 20 : 30; lastSep = (_res->_lang == LANG_JP) ? 20 : 30;
} }
} }
const uint8_t *sep = _textSep; const uint8_t *sep = _textSep;
y += 50; y += 50;
x += (_res->_lang == LANG_JP) ? 0 : 8; x += (_res->_lang == LANG_JP) ? 0 : 8;
int16_t yy = y; int16_t yPos = y;
int16_t xx = x; int16_t xPos = x;
if (textJustify != kTextJustifyLeft) { if (textJustify != kTextJustifyLeft) {
xx += ((last_sep - *sep++) / 2) * Video::CHAR_W; xPos += ((lastSep - *sep++) / 2) * Video::CHAR_W;
} }
for (; *p != 0xA && *p; ++p) { for (int i = 0; i < len && p[i] != 0xA; ++i) {
if (isNewLineChar(*p, _res)) { if (isNewLineChar(p[i], _res)) {
yy += Video::CHAR_H; yPos += Video::CHAR_H;
xx = x; xPos = x;
if (textJustify != kTextJustifyLeft) { if (textJustify != kTextJustifyLeft) {
xx += ((last_sep - *sep++) / 2) * Video::CHAR_W; xPos += ((lastSep - *sep++) / 2) * Video::CHAR_W;
} }
} else if (*p == 0x20) { } else if (p[i] == 0x20) {
xx += Video::CHAR_W; xPos += Video::CHAR_W;
} else if (*p == 0x9) { } else if (p[i] == 0x9) {
// ignore tab // ignore tab
} else { } else {
(_vid->*dcf)(page, _vid->_w, xx, yy, fnt, color, *p); (_vid->*dcf)(page, _vid->_w, xPos, yPos, fnt, color, p[i]);
xx += Video::CHAR_W; xPos += Video::CHAR_W;
} }
} }
} }
@ -415,9 +417,12 @@ void Cutscene::op_drawStringAtBottom() {
} }
} }
memset(_pageC + 179 * _vid->_w, 0xC0, 45 * _vid->_w); const int h = 45 * _vid->_layerScale;
memset(_page1 + 179 * _vid->_w, 0xC0, 45 * _vid->_w); const int y = Video::GAMESCREEN_H * _vid->_layerScale - h;
memset(_page0 + 179 * _vid->_w, 0xC0, 45 * _vid->_w);
memset(_pageC + y * _vid->_w, 0xC0, h * _vid->_w);
memset(_page1 + y * _vid->_w, 0xC0, h * _vid->_w);
memset(_page0 + y * _vid->_w, 0xC0, h * _vid->_w);
if (strId != 0xFFFF) { if (strId != 0xFFFF) {
const uint8_t *str = _res->getCineString(strId); const uint8_t *str = _res->getCineString(strId);
if (str) { if (str) {
@ -878,7 +883,7 @@ void Cutscene::op_drawStringAtPos() {
// 'voyage' - cutscene script redraws the string to refresh the screen // 'voyage' - cutscene script redraws the string to refresh the screen
if (_id == 0x34 && (strId & 0xFFF) == 0x45) { if (_id == 0x34 && (strId & 0xFFF) == 0x45) {
if ((_cmdPtr - _cmdPtrBak) == 0xA) { if ((_cmdPtr - _cmdPtrBak) == 0xA) {
_stub->copyRect(0, 0, _vid->_w, _vid->_h, _page1, 256); _stub->copyRect(0, 0, _vid->_w, _vid->_h, _page1, _vid->_w);
_stub->updateScreen(0); _stub->updateScreen(0);
} else { } else {
_stub->sleep(15); _stub->sleep(15);
@ -1059,9 +1064,7 @@ void Cutscene::unload() {
void Cutscene::prepare() { void Cutscene::prepare() {
_page0 = _vid->_frontLayer; _page0 = _vid->_frontLayer;
_page1 = _vid->_tempLayer; _page1 = _vid->_tempLayer;
memset(_page1, 0, _vid->_layerSize);
_pageC = _vid->_tempLayer2; _pageC = _vid->_tempLayer2;
memset(_pageC, 0, _vid->_layerSize);
_stub->_pi.dirMask = 0; _stub->_pi.dirMask = 0;
_stub->_pi.enter = false; _stub->_pi.enter = false;
_stub->_pi.space = false; _stub->_pi.space = false;
@ -1080,6 +1083,10 @@ void Cutscene::prepare() {
} }
void Cutscene::playCredits() { void Cutscene::playCredits() {
if (_res->isMac()) {
warning("Cutscene::playCredits() unimplemented");
return;
}
_textCurPtr = _res->isAmiga() ? _creditsDataAmiga : _creditsDataDOS; _textCurPtr = _res->isAmiga() ? _creditsDataAmiga : _creditsDataDOS;
_textBuf[0] = 0xA; _textBuf[0] = 0xA;
_textCurBuf = _textBuf; _textCurBuf = _textBuf;

View File

@ -113,7 +113,7 @@ struct Cutscene {
void updatePalette(); void updatePalette();
void setPalette(); void setPalette();
void setRotationTransform(uint16_t a, uint16_t b, uint16_t c); void setRotationTransform(uint16_t a, uint16_t b, uint16_t c);
uint16_t findTextSeparators(const uint8_t *p); uint16_t findTextSeparators(const uint8_t *p, int len);
void drawText(int16_t x, int16_t y, const uint8_t *p, uint16_t color, uint8_t *page, int textJustify); void drawText(int16_t x, int16_t y, const uint8_t *p, uint16_t color, uint8_t *page, int textJustify);
void swapLayers(); void swapLayers();
void drawCreditsText(); void drawCreditsText();

141
decode_mac.cpp Normal file
View File

@ -0,0 +1,141 @@
#include <assert.h>
#include <stdlib.h>
#include <string.h>
#include "decode_mac.h"
#include "file.h"
uint8_t *decodeLzss(File &f, uint32_t &decodedSize) {
decodedSize = f.readUint32BE();
uint8_t *dst = (uint8_t *)malloc(decodedSize);
uint32_t count = 0;
while (count < decodedSize) {
const int code = f.readByte();
for (int i = 0; i < 8 && count < decodedSize; ++i) {
if ((code & (1 << i)) == 0) {
dst[count++] = f.readByte();
} else {
int offset = f.readUint16BE();
const int len = (offset >> 12) + 3;
offset &= 0xFFF;
for (int j = 0; j < len; ++j) {
dst[count + j] = dst[count - offset - 1 + j];
}
count += len;
}
}
}
assert(count == decodedSize);
return dst;
}
static void setPixel(int x, int y, int w, int h, uint8_t color, DecodeBuffer *buf) {
buf->setPixel(buf, x, y, w, h, color);
}
void decodeC103(const uint8_t *a3, int w, int h, DecodeBuffer *buf) {
uint8_t d0;
int d3 = 0;
int d7 = 1;
int d6 = 0;
int d1 = 0;
static const uint32_t d5 = 0xFFF;
uint8_t a1[0x1000];
for (int y = 0; y < h; ++y) {
for (int x = 0; x < w; ++x) {
assert(d6 >= 0);
if (d6 == 0) {
int carry = d7 & 1;
d7 >>= 1;
if (d7 == 0) {
d7 = *a3++;
const int extended_bit = carry ? 0x80 : 0;
carry = d7 & 1;
d7 = extended_bit | (d7 >> 1);
}
if (!carry) {
d0 = *a3++;
a1[d3] = d0;
++d3;
d3 &= d5;
setPixel(x, y, w, h, d0, buf);
continue;
}
d1 = READ_BE_UINT16(a3); a3 += 2;
d6 = d1;
d1 &= d5;
++d1;
d1 = (d3 - d1) & d5;
d6 >>= 12;
d6 += 3;
}
d0 = a1[d1++];
d1 &= d5;
a1[d3++] = d0;
d3 &= d5;
setPixel(x, y, w, h, d0, buf);
--d6;
}
}
}
void decodeC211(const uint8_t *a3, int w, int h, DecodeBuffer *buf) {
struct {
const uint8_t *ptr;
int repeatCount;
} stack[512];
int y = 0;
int x = 0;
int sp = 0;
while (1) {
uint8_t d0 = *a3++;
if ((d0 & 0x80) != 0) {
++y;
x = 0;
}
int d1 = d0 & 0x1F;
if (d1 == 0) {
d1 = READ_BE_UINT16(a3); a3 += 2;
}
const int carry_set = (d0 & 0x40) != 0;
d0 <<= 2;
if (!carry_set) {
if ((d0 & 0x80) == 0) {
--d1;
if (d1 == 0) {
assert(sp > 0);
--stack[sp - 1].repeatCount;
if (stack[sp - 1].repeatCount >= 0) {
a3 = stack[sp - 1].ptr;
} else {
--sp;
}
} else {
assert(sp < ARRAYSIZE(stack));
stack[sp].ptr = a3;
stack[sp].repeatCount = d1;
++sp;
}
} else {
x += d1;
}
} else {
if ((d0 & 0x80) == 0) {
if (d1 == 1) {
return;
}
const uint8_t color = *a3++;
for (int i = 0; i < d1; ++i) {
setPixel(x++, y, w, h, color, buf);
}
} else {
for (int i = 0; i < d1; ++i) {
setPixel(x++, y, w, h, *a3++, buf);
}
}
}
}
}

23
decode_mac.h Normal file
View File

@ -0,0 +1,23 @@
#ifndef DECODE_MAC_H__
#define DECODE_MAC_H__
#include <stdint.h>
#include "file.h"
uint8_t *decodeLzss(File &f, uint32_t &decodedSize);
struct DecodeBuffer {
uint8_t *ptr;
int w, h, pitch;
int x, y;
bool xflip;
void (*setPixel)(DecodeBuffer *buf, int src_x, int src_y, int src_w, int src_h, uint8_t color);
void *dataPtr;
};
void decodeC103(const uint8_t *a3, int w, int h, DecodeBuffer *buf);
void decodeC211(const uint8_t *a3, int w, int h, DecodeBuffer *buf);
#endif

View File

@ -14,7 +14,7 @@
#include "unpack.h" #include "unpack.h"
#include "util.h" #include "util.h"
Game::Game(SystemStub *stub, FileSystem *fs, const char *savePath, int level, ResourceType ver, Language lang, WidescreenMode widescreenMode) Game::Game(SystemStub *stub, FileSystem *fs, const char *savePath, int level, ResourceType ver, Language lang, WidescreenMode widescreenMode, bool autoSave)
: _cut(&_res, stub, &_vid), _menu(&_res, stub, &_vid), : _cut(&_res, stub, &_vid), _menu(&_res, stub, &_vid),
_mix(fs, stub), _res(fs, ver, lang), _seq(stub, &_mix), _vid(&_res, stub), _mix(fs, stub), _res(fs, ver, lang), _seq(stub, &_mix), _vid(&_res, stub),
_stub(stub), _fs(fs), _savePath(savePath) { _stub(stub), _fs(fs), _savePath(savePath) {
@ -24,6 +24,7 @@ Game::Game(SystemStub *stub, FileSystem *fs, const char *savePath, int level, Re
_currentLevel = _menu._level = level; _currentLevel = _menu._level = level;
_demoBin = -1; _demoBin = -1;
_widescreenMode = widescreenMode; _widescreenMode = widescreenMode;
_autoSave = autoSave;
} }
void Game::run() { void Game::run() {
@ -150,6 +151,7 @@ void Game::run() {
resetGameState(); resetGameState();
_endLoop = false; _endLoop = false;
_frameTimestamp = _stub->getTimeStamp(); _frameTimestamp = _stub->getTimeStamp();
_saveTimestamp = _frameTimestamp;
while (!_stub->_pi.quit && !_endLoop) { while (!_stub->_pi.quit && !_endLoop) {
mainLoop(); mainLoop();
if (_demoBin != -1 && _inp_demPos >= _res._demLen) { if (_demoBin != -1 && _inp_demPos >= _res._demLen) {
@ -166,8 +168,8 @@ void Game::run() {
_stub->_pi.shift = false; _stub->_pi.shift = false;
// clear widescreen borders // clear widescreen borders
if (_stub->hasWidescreen()) { if (_stub->hasWidescreen()) {
_stub->copyRectLeftBorder(Video::GAMESCREEN_W, Video::GAMESCREEN_H, 0); _stub->copyRectLeftBorder(_vid._w, _vid._h, 0);
_stub->copyRectRightBorder(Video::GAMESCREEN_W, Video::GAMESCREEN_H, 0); _stub->copyRectRightBorder(_vid._w, _vid._h, 0);
} }
} }
} }
@ -271,10 +273,34 @@ void Game::displayTitleScreenMac(int num) {
Color c; Color c;
c.r = c.g = c.b = 0; c.r = c.g = c.b = 0;
_stub->setPaletteEntry(0, &c); _stub->setPaletteEntry(0, &c);
} else if (num == Menu::kMacTitleScreen_Flashback) {
_vid.setTextPalette();
_vid._charShadowColor = 0xE0;
} }
_stub->copyRect(0, 0, _vid._w, _vid._h, _vid._frontLayer, _vid._w); _stub->copyRect(0, 0, _vid._w, _vid._h, _vid._frontLayer, _vid._w);
_stub->updateScreen(0); _stub->updateScreen(0);
while (1) { while (1) {
if (num == Menu::kMacTitleScreen_Flashback) {
static const uint8_t selectedColor = 0xE4;
static const uint8_t defaultColor = 0xE8;
for (int i = 0; i < 7; ++i) {
const char *str = Menu::_levelNames[i];
_vid.drawString(str, 24, 24 + i * 16, (_currentLevel == i) ? selectedColor : defaultColor);
}
if (_stub->_pi.dirMask & PlayerInput::DIR_UP) {
_stub->_pi.dirMask &= ~PlayerInput::DIR_UP;
if (_currentLevel > 0) {
--_currentLevel;
}
}
if (_stub->_pi.dirMask & PlayerInput::DIR_DOWN) {
_stub->_pi.dirMask &= ~PlayerInput::DIR_DOWN;
if (_currentLevel < 6) {
++_currentLevel;
}
}
_vid.updateScreen();
}
_stub->processEvents(); _stub->processEvents();
if (_stub->_pi.quit) { if (_stub->_pi.quit) {
break; break;
@ -324,10 +350,10 @@ void Game::mainLoop() {
playCutscene(0x41); playCutscene(0x41);
_endLoop = true; _endLoop = true;
} else { } else {
if (_validSaveState) { if (_autoSave && loadGameState(kAutoSaveSlot)) {
if (!loadGameState(0)) { // autosave
_endLoop = true; } else if (_validSaveState && loadGameState(kIngameSaveSlot)) {
} // ingame save
} else { } else {
loadLevelData(); loadLevelData();
resetGameState(); resetGameState();
@ -393,6 +419,13 @@ void Game::mainLoop() {
} }
} }
inp_handleSpecialKeys(); inp_handleSpecialKeys();
if (_stub->getTimeStamp() - _saveTimestamp >= kAutoSaveIntervalMs) {
// do not save if we just died
if (_pgeLive[0].life > 0) {
saveGameState(kAutoSaveSlot);
_saveTimestamp = _stub->getTimeStamp();
}
}
} }
void Game::updateTiming() { void Game::updateTiming() {
@ -474,7 +507,7 @@ void Game::playCutscene(int id) {
_cut.play(); _cut.play();
} }
} }
if (_res._type == kResourceTypeMac) { if (_res._type == kResourceTypeMac && !(id == 0x48 || id == 0x49)) { // continue or score screens
// restore palette entries modified by the cutscene player (0xC and 0xD) // restore palette entries modified by the cutscene player (0xC and 0xD)
Color palette[32]; Color palette[32];
_res.MAC_copyClut16(palette, 0, 0x37); _res.MAC_copyClut16(palette, 0, 0x37);
@ -533,8 +566,8 @@ void Game::drawCurrentInventoryItem() {
void Game::showFinalScore() { void Game::showFinalScore() {
if (_stub->hasWidescreen()) { if (_stub->hasWidescreen()) {
_stub->copyRectLeftBorder(Video::GAMESCREEN_W, Video::GAMESCREEN_H, 0); _stub->copyRectLeftBorder(_vid._w, _vid._h, 0);
_stub->copyRectRightBorder(Video::GAMESCREEN_W, Video::GAMESCREEN_H, 0); _stub->copyRectRightBorder(_vid._w, _vid._h, 0);
} }
playCutscene(0x49); playCutscene(0x49);
char buf[50]; char buf[50];
@ -697,8 +730,8 @@ bool Game::handleConfigPanel() {
bool Game::handleContinueAbort() { bool Game::handleContinueAbort() {
if (_stub->hasWidescreen()) { if (_stub->hasWidescreen()) {
_stub->copyRectLeftBorder(Video::GAMESCREEN_W, Video::GAMESCREEN_H, 0); _stub->copyRectLeftBorder(_vid._w, _vid._h, 0);
_stub->copyRectRightBorder(Video::GAMESCREEN_W, Video::GAMESCREEN_H, 0); _stub->copyRectRightBorder(_vid._w, _vid._h, 0);
} }
playCutscene(0x48); playCutscene(0x48);
int timeout = 100; int timeout = 100;
@ -1362,7 +1395,7 @@ void Game::drawObjectFrame(const uint8_t *bankDataPtr, const uint8_t *dataPtr, i
_vid.drawSpriteSub4(src, _vid._frontLayer + dst_offset, sprite_w, sprite_clipped_h, sprite_clipped_w, sprite_col_mask); _vid.drawSpriteSub4(src, _vid._frontLayer + dst_offset, sprite_w, sprite_clipped_h, sprite_clipped_w, sprite_col_mask);
} }
} }
_vid.markBlockAsDirty(sprite_x, sprite_y, sprite_clipped_w, sprite_clipped_h); _vid.markBlockAsDirty(sprite_x, sprite_y, sprite_clipped_w, sprite_clipped_h, _vid._layerScale);
} }
void Game::decodeCharacterFrame(const uint8_t *dataPtr, uint8_t *dstPtr) { void Game::decodeCharacterFrame(const uint8_t *dataPtr, uint8_t *dstPtr) {
@ -1499,7 +1532,7 @@ void Game::drawCharacter(const uint8_t *dataPtr, int16_t pos_x, int16_t pos_y, u
_vid.drawSpriteSub4(src, _vid._frontLayer + dst_offset, sprite_w, sprite_clipped_h, sprite_clipped_w, sprite_col_mask); _vid.drawSpriteSub4(src, _vid._frontLayer + dst_offset, sprite_w, sprite_clipped_h, sprite_clipped_w, sprite_col_mask);
} }
} }
_vid.markBlockAsDirty(pos_x, pos_y, sprite_clipped_w, sprite_clipped_h); _vid.markBlockAsDirty(pos_x, pos_y, sprite_clipped_w, sprite_clipped_h, _vid._layerScale);
} }
int Game::loadMonsterSprites(LivePGE *pge) { int Game::loadMonsterSprites(LivePGE *pge) {
@ -1617,14 +1650,30 @@ void Game::loadLevelMap() {
} }
} }
_vid.PC_decodeMap(_currentLevel, _currentRoom); _vid.PC_decodeMap(_currentLevel, _currentRoom);
if (_stub->hasWidescreen() && _widescreenMode == kWidescreenMirrorRoom) {
_stub->copyRectMirrorBorders(Video::GAMESCREEN_W, Video::GAMESCREEN_H, _vid._backLayer);
}
break; break;
case kResourceTypeMac: case kResourceTypeMac:
if (_stub->hasWidescreen() && _widescreenMode == kWidescreenAdjacentRooms) {
const int leftRoom = _res._ctData[CT_LEFT_ROOM + _currentRoom];
if (leftRoom > 0 && hasLevelMap(_currentLevel, leftRoom)) {
_vid.MAC_decodeMap(_currentLevel, leftRoom);
_stub->copyRectLeftBorder(_vid._w, _vid._h, _vid._backLayer);
} else {
_stub->copyRectLeftBorder(_vid._w, _vid._h, 0);
}
const int rightRoom = _res._ctData[CT_RIGHT_ROOM + _currentRoom];
if (rightRoom > 0 && hasLevelMap(_currentLevel, rightRoom)) {
_vid.MAC_decodeMap(_currentLevel, rightRoom);
_stub->copyRectRightBorder(_vid._w, _vid._h, _vid._backLayer);
} else {
_stub->copyRectRightBorder(_vid._w, _vid._h, 0);
}
}
_vid.MAC_decodeMap(_currentLevel, _currentRoom); _vid.MAC_decodeMap(_currentLevel, _currentRoom);
break; break;
} }
if (_stub->hasWidescreen() && _widescreenMode == kWidescreenMirrorRoom) {
_stub->copyRectMirrorBorders(_vid._w, _vid._h, _vid._backLayer);
}
} }
void Game::loadLevelData() { void Game::loadLevelData() {
@ -1806,7 +1855,7 @@ void Game::drawIcon(uint8_t iconNum, int16_t x, int16_t y, uint8_t colMask) {
return; return;
} }
_vid.drawSpriteSub1(buf, _vid._frontLayer + x + y * _vid._w, 16, 16, 16, colMask << 4); _vid.drawSpriteSub1(buf, _vid._frontLayer + x + y * _vid._w, 16, 16, 16, colMask << 4);
_vid.markBlockAsDirty(x, y, 16, 16); _vid.markBlockAsDirty(x, y, 16, 16, _vid._layerScale);
} }
void Game::playSound(uint8_t sfxId, uint8_t softVol) { void Game::playSound(uint8_t sfxId, uint8_t softVol) {
@ -2006,7 +2055,7 @@ static const uint32_t TAG_FBSV = 0x46425356;
bool Game::saveGameState(uint8_t slot) { bool Game::saveGameState(uint8_t slot) {
bool success = false; bool success = false;
char stateFile[20]; char stateFile[32];
makeGameStateName(slot, stateFile); makeGameStateName(slot, stateFile);
File f; File f;
if (!f.open(stateFile, "zwb", _savePath)) { if (!f.open(stateFile, "zwb", _savePath)) {
@ -2033,7 +2082,7 @@ bool Game::saveGameState(uint8_t slot) {
bool Game::loadGameState(uint8_t slot) { bool Game::loadGameState(uint8_t slot) {
bool success = false; bool success = false;
char stateFile[20]; char stateFile[32];
makeGameStateName(slot, stateFile); makeGameStateName(slot, stateFile);
File f; File f;
if (!f.open(stateFile, "zrb", _savePath)) { if (!f.open(stateFile, "zrb", _savePath)) {

10
game.h
View File

@ -25,6 +25,12 @@ struct Game {
typedef int (Game::*col_Callback1)(LivePGE *, LivePGE *, int16_t, int16_t); typedef int (Game::*col_Callback1)(LivePGE *, LivePGE *, int16_t, int16_t);
typedef int (Game::*col_Callback2)(LivePGE *, int16_t, int16_t, int16_t); typedef int (Game::*col_Callback2)(LivePGE *, int16_t, int16_t, int16_t);
enum {
kIngameSaveSlot = 0,
kAutoSaveSlot = 255,
kAutoSaveIntervalMs = 30 * 1000
};
enum { enum {
CT_UP_ROOM = 0x00, CT_UP_ROOM = 0x00,
CT_DOWN_ROOM = 0x40, CT_DOWN_ROOM = 0x40,
@ -87,8 +93,10 @@ struct Game {
bool _endLoop; bool _endLoop;
uint32_t _frameTimestamp; uint32_t _frameTimestamp;
WidescreenMode _widescreenMode; WidescreenMode _widescreenMode;
bool _autoSave;
uint32_t _saveTimestamp;
Game(SystemStub *, FileSystem *, const char *savePath, int level, ResourceType ver, Language lang, WidescreenMode widescreenMode); Game(SystemStub *, FileSystem *, const char *savePath, int level, ResourceType ver, Language lang, WidescreenMode widescreenMode, bool autoSave);
void run(); void run();
void displayTitleScreenAmiga(); void displayTitleScreenAmiga();

View File

@ -211,21 +211,18 @@ void Graphics::fillArea(uint8_t color, bool hasAlpha) {
if (x1 >= 0) { if (x1 >= 0) {
if (hasAlpha && color > 0xC7) { if (hasAlpha && color > 0xC7) {
do { do {
int16_t x2 = *pts++; const int16_t x2 = MIN<int16_t>(_crw - 1, *pts++);
if (x2 < _crw && x2 >= x1) { for (; x1 <= x2; ++x1) {
int len = x2 - x1 + 1; *(dst + x1) |= color & 8;
for (int i = 0; i < len; ++i) {
*(dst + x1 + i) |= color & 8; // XXX 0x88
}
} }
dst += _layerPitch; dst += _layerPitch;
x1 = *pts++; x1 = *pts++;
} while (x1 >= 0); } while (x1 >= 0);
} else { } else {
do { do {
int16_t x2 = *pts++; const int16_t x2 = MIN<int16_t>(_crw - 1, *pts++);
if (x2 < _crw && x2 >= x1) { if (x1 <= x2) {
int len = x2 - x1 + 1; const int len = x2 - x1 + 1;
memset(dst + x1, color, len); memset(dst + x1, color, len);
} }
dst += _layerPitch; dst += _layerPitch;

View File

@ -13,12 +13,6 @@
#include <assert.h> #include <assert.h>
#include <stdint.h> #include <stdint.h>
#undef ABS
#define ABS(x) ((x)<0?-(x):(x))
#undef MAX
#define MAX(x,y) ((x)>(y)?(x):(y))
#undef MIN
#define MIN(x,y) ((x)<(y)?(x):(y))
#undef ARRAYSIZE #undef ARRAYSIZE
#define ARRAYSIZE(a) (int)(sizeof(a)/sizeof(a[0])) #define ARRAYSIZE(a) (int)(sizeof(a)/sizeof(a[0]))
@ -42,16 +36,6 @@ inline uint32_t READ_LE_UINT32(const void *ptr) {
return (b[3] << 24) | (b[2] << 16) | (b[1] << 8) | b[0]; return (b[3] << 24) | (b[2] << 16) | (b[1] << 8) | b[0];
} }
inline int8_t ADDC_S8(int a, int b) {
a += b;
if (a < -128) {
a = -128;
} else if (a > 127) {
a = 127;
}
return a;
}
inline int16_t ADDC_S16(int a, int b) { inline int16_t ADDC_S16(int a, int b) {
a += b; a += b;
if (a < -32768) { if (a < -32768) {
@ -79,6 +63,24 @@ inline T CLIP(const T& val, const T& a, const T& b) {
return val; return val;
} }
#undef MIN
template<typename T>
inline T MIN(T v1, T v2) {
return (v1 < v2) ? v1 : v2;
}
#undef MAX
template<typename T>
inline T MAX(T v1, T v2) {
return (v1 > v2) ? v1 : v2;
}
#undef ABS
template<typename T>
inline T ABS(T t) {
return (t < 0) ? -t : t;
}
enum Language { enum Language {
LANG_FR, LANG_FR,
LANG_EN, LANG_EN,

View File

@ -25,6 +25,7 @@ static const char *USAGE =
" --widescreen=MODE 16:9 display\n" " --widescreen=MODE 16:9 display\n"
" --scaler=NAME@X Graphics scaler (default 'scale@3')\n" " --scaler=NAME@X Graphics scaler (default 'scale@3')\n"
" --language=LANG Language (fr,en,de,sp,it,jp)\n" " --language=LANG Language (fr,en,de,sp,it,jp)\n"
" --autosave Save game state automatically\n"
; ;
static int detectVersion(FileSystem *fs) { static int detectVersion(FileSystem *fs) {
@ -215,6 +216,7 @@ int main(int argc, char *argv[]) {
const char *savePath = "."; const char *savePath = ".";
int levelNum = 0; int levelNum = 0;
bool fullscreen = false; bool fullscreen = false;
bool autoSave = false;
WidescreenMode widescreen = kWidescreenNone; WidescreenMode widescreen = kWidescreenNone;
ScalerParameters scalerParameters = ScalerParameters::defaults(); ScalerParameters scalerParameters = ScalerParameters::defaults();
int forcedLanguage = -1; int forcedLanguage = -1;
@ -234,6 +236,7 @@ int main(int argc, char *argv[]) {
{ "scaler", required_argument, 0, 5 }, { "scaler", required_argument, 0, 5 },
{ "language", required_argument, 0, 6 }, { "language", required_argument, 0, 6 },
{ "widescreen", required_argument, 0, 7 }, { "widescreen", required_argument, 0, 7 },
{ "autosave", no_argument, 0, 8 },
{ 0, 0, 0, 0 } { 0, 0, 0, 0 }
}; };
int index; int index;
@ -281,6 +284,9 @@ int main(int argc, char *argv[]) {
case 7: case 7:
widescreen = parseWidescreen(optarg); widescreen = parseWidescreen(optarg);
break; break;
case 8:
autoSave = true;
break;
default: default:
printf(USAGE, argv[0]); printf(USAGE, argv[0]);
return 0; return 0;
@ -296,7 +302,7 @@ int main(int argc, char *argv[]) {
} }
const Language language = (forcedLanguage == -1) ? detectLanguage(&fs) : (Language)forcedLanguage; const Language language = (forcedLanguage == -1) ? detectLanguage(&fs) : (Language)forcedLanguage;
SystemStub *stub = SystemStub_SDL_create(); SystemStub *stub = SystemStub_SDL_create();
Game *g = new Game(stub, &fs, savePath, levelNum, (ResourceType)version, language, widescreen); Game *g = new Game(stub, &fs, savePath, levelNum, (ResourceType)version, language, widescreen, autoSave);
stub->init(g_caption, g->_vid._w, g->_vid._h, fullscreen, widescreen != kWidescreenNone, &scalerParameters); stub->init(g_caption, g->_vid._w, g->_vid._h, fullscreen, widescreen != kWidescreenNone, &scalerParameters);
g->run(); g->run();
delete g; delete g;

View File

@ -84,7 +84,7 @@ void Menu::drawString2(const char *str, int16_t y, int16_t x) {
} }
break; break;
} }
_vid->markBlockAsDirty(x * w, y * h, len * w, h); _vid->markBlockAsDirty(x * w, y * h, len * w, h, _vid->_layerScale);
} }
void Menu::loadPicture(const char *prefix) { void Menu::loadPicture(const char *prefix) {
@ -201,7 +201,7 @@ bool Menu::handlePasswordScreen() {
} }
_vid->PC_drawChar(0x20, 21, len + 15); _vid->PC_drawChar(0x20, 21, len + 15);
_vid->markBlockAsDirty(15 * Video::CHAR_W, 21 * Video::CHAR_H, (len + 1) * Video::CHAR_W, Video::CHAR_H); _vid->markBlockAsDirty(15 * Video::CHAR_W, 21 * Video::CHAR_H, (len + 1) * Video::CHAR_W, Video::CHAR_H, _vid->_layerScale);
_vid->updateScreen(); _vid->updateScreen();
_stub->sleep(EVENTS_DELAY); _stub->sleep(EVENTS_DELAY);
_stub->processEvents(); _stub->processEvents();
@ -254,24 +254,15 @@ bool Menu::handleLevelScreen() {
int currentSkill = _skill; int currentSkill = _skill;
int currentLevel = _level; int currentLevel = _level;
do { do {
static const char *levelTitles[] = {
"Titan / The Jungle",
"Titan / New Washington",
"Titan / Death Tower Show",
"Earth / Surface",
"Earth / Paradise Club",
"Planet Morphs / Surface",
"Planet Morphs / Inner Core"
};
for (int i = 0; i < 7; ++i) { for (int i = 0; i < 7; ++i) {
drawString(levelTitles[i], 7 + i * 2, 4, (currentLevel == i) ? 2 : 3); drawString(_levelNames[i], 7 + i * 2, 4, (currentLevel == i) ? 2 : 3);
} }
_vid->markBlockAsDirty(4 * Video::CHAR_W, 7 * Video::CHAR_H, 192, 7 * Video::CHAR_H); _vid->markBlockAsDirty(4 * Video::CHAR_W, 7 * Video::CHAR_H, 192, 7 * Video::CHAR_H, _vid->_layerScale);
drawString(_res->getMenuString(LocaleData::LI_13_EASY), 23, 4, (currentSkill == 0) ? 2 : 3); drawString(_res->getMenuString(LocaleData::LI_13_EASY), 23, 4, (currentSkill == 0) ? 2 : 3);
drawString(_res->getMenuString(LocaleData::LI_14_NORMAL), 23, 14, (currentSkill == 1) ? 2 : 3); drawString(_res->getMenuString(LocaleData::LI_14_NORMAL), 23, 14, (currentSkill == 1) ? 2 : 3);
drawString(_res->getMenuString(LocaleData::LI_15_EXPERT), 23, 24, (currentSkill == 2) ? 2 : 3); drawString(_res->getMenuString(LocaleData::LI_15_EXPERT), 23, 24, (currentSkill == 2) ? 2 : 3);
_vid->markBlockAsDirty(4 * Video::CHAR_W, 23 * Video::CHAR_H, 192, Video::CHAR_H); _vid->markBlockAsDirty(4 * Video::CHAR_W, 23 * Video::CHAR_H, 192, Video::CHAR_H, _vid->_layerScale);
_vid->updateScreen(); _vid->updateScreen();
_stub->sleep(EVENTS_DELAY); _stub->sleep(EVENTS_DELAY);

1
menu.h
View File

@ -48,6 +48,7 @@ struct Menu {
int opt; int opt;
}; };
static const char *_levelNames[];
static const char *_passwordsDOS[]; static const char *_passwordsDOS[];
static const char *_passwordsFrAmiga[]; static const char *_passwordsFrAmiga[];
static const char *_passwordsEnAmiga[]; static const char *_passwordsEnAmiga[];

View File

@ -139,6 +139,15 @@ void Mixer::stopMusic() {
} }
} }
static void nr(int16_t *buf, int len) {
static int prev = 0;
for (int i = 0; i < len; ++i) {
const int vnr = buf[i] >> 1;
buf[i] = vnr + prev;
prev = vnr;
}
}
void Mixer::mix(int16_t *out, int len) { void Mixer::mix(int16_t *out, int len) {
if (_premixHook) { if (_premixHook) {
if (!_premixHook(_premixHookData, out, len)) { if (!_premixHook(_premixHookData, out, len)) {
@ -160,6 +169,7 @@ void Mixer::mix(int16_t *out, int len) {
} }
} }
} }
nr(out, len);
} }
void Mixer::mixCallback(void *param, int16_t *buf, int len) { void Mixer::mixCallback(void *param, int16_t *buf, int len) {

View File

@ -156,8 +156,7 @@ struct ModPlayer_impl {
void applyVibrato(int trackNum); void applyVibrato(int trackNum);
void applyPortamento(int trackNum); void applyPortamento(int trackNum);
void handleEffect(int trackNum, bool tick); void handleEffect(int trackNum, bool tick);
void mixSamples(int8_t *buf, int len); void mixSamples(int16_t *buf, int len);
bool mixS8(int8_t *buf, int len);
bool mix(int16_t *buf, int len); bool mix(int16_t *buf, int len);
}; };
@ -584,11 +583,11 @@ void ModPlayer_impl::handleTick() {
} }
} }
void ModPlayer_impl::mixSamples(int8_t *buf, int samplesLen) { void ModPlayer_impl::mixSamples(int16_t *buf, int samplesLen) {
for (int i = 0; i < NUM_TRACKS; ++i) { for (int i = 0; i < NUM_TRACKS; ++i) {
Track *tk = &_tracks[i]; Track *tk = &_tracks[i];
if (tk->sample != 0 && tk->delayCounter == 0) { if (tk->sample != 0 && tk->delayCounter == 0) {
int8_t *mixbuf = buf; int16_t *mixbuf = buf;
SampleInfo *si = tk->sample; SampleInfo *si = tk->sample;
int len = si->len << FRAC_BITS; int len = si->len << FRAC_BITS;
int loopLen = si->repeatLen << FRAC_BITS; int loopLen = si->repeatLen << FRAC_BITS;
@ -614,7 +613,7 @@ void ModPlayer_impl::mixSamples(int8_t *buf, int samplesLen) {
} }
while (count--) { while (count--) {
const int out = si->getPCM(pos >> FRAC_BITS); const int out = si->getPCM(pos >> FRAC_BITS);
*mixbuf = ADDC_S8(*mixbuf, out * tk->volume / 64); *mixbuf = ADDC_S16(*mixbuf, (out * tk->volume / 64) << 8);
++mixbuf; ++mixbuf;
pos += deltaPos; pos += deltaPos;
} }
@ -624,7 +623,8 @@ void ModPlayer_impl::mixSamples(int8_t *buf, int samplesLen) {
} }
} }
bool ModPlayer_impl::mixS8(int8_t *buf, int len) { bool ModPlayer_impl::mix(int16_t *buf, int len) {
memset(buf, 0, sizeof(int16_t) * len);
if (_playing) { if (_playing) {
const int samplesPerTick = _mixingRate / (50 * _songTempo / 125); const int samplesPerTick = _mixingRate / (50 * _songTempo / 125);
while (len != 0) { while (len != 0) {
@ -644,16 +644,6 @@ bool ModPlayer_impl::mixS8(int8_t *buf, int len) {
} }
return _playing; return _playing;
} }
bool ModPlayer_impl::mix(int16_t *samples, int len) {
int8_t buf[len];
memset(buf, 0, sizeof(buf));
const bool ret = mixS8(buf, len);
for (int i = 0; i < len; ++i) {
samples[i] = buf[i] << 8;
}
return ret;
}
#endif #endif
ModPlayer::ModPlayer(Mixer *mixer, FileSystem *fs) ModPlayer::ModPlayer(Mixer *mixer, FileSystem *fs)

View File

@ -325,7 +325,7 @@ int Game::pge_execute(LivePGE *live_pge, InitPGE *init_pge, const Object *obj) {
++live_pge->life; ++live_pge->life;
} }
if (obj->flags & 8) { if (obj->flags & 8) {
live_pge->life = 0xFFFF; live_pge->life = -1;
} }
if (live_pge->flags & 1) { if (live_pge->flags & 1) {
@ -1428,7 +1428,7 @@ int Game::pge_op_setCollisionState2(ObjectOpcodeArgs *args) {
int Game::pge_op_saveState(ObjectOpcodeArgs *args) { int Game::pge_op_saveState(ObjectOpcodeArgs *args) {
_saveStateCompleted = true; _saveStateCompleted = true;
_validSaveState = saveGameState(0); _validSaveState = saveGameState(kIngameSaveSlot);
return 0xFFFF; return 0xFFFF;
} }

View File

@ -1679,6 +1679,10 @@ void Resource::MAC_loadCutsceneText() {
_cine_off = 0; // offsets are prepended to _cine_txt _cine_off = 0; // offsets are prepended to _cine_txt
} }
void Resource::MAC_loadCreditsText() {
_credits = decodeResourceMacData("Credit strings", false);
}
void Resource::MAC_loadSounds() { void Resource::MAC_loadSounds() {
static const int8_t table[NUM_SFXS] = { static const int8_t table[NUM_SFXS] = {
0, -1, 1, 2, 3, 4, -1, 5, 6, 7, 8, 9, 10, 11, -1, 12, 0, -1, 1, 2, 3, 4, -1, 5, 6, 7, 8, 9, 10, 11, -1, 12,

View File

@ -177,6 +177,7 @@ struct Resource {
uint8_t *_perso; uint8_t *_perso;
uint8_t *_monster; uint8_t *_monster;
uint8_t *_str; uint8_t *_str;
uint8_t *_credits;
Resource(FileSystem *fs, ResourceType type, Language lang); Resource(FileSystem *fs, ResourceType type, Language lang);
~Resource(); ~Resource();
@ -335,6 +336,7 @@ struct Resource {
void MAC_unloadCutscene(); void MAC_unloadCutscene();
void MAC_loadCutscene(const char *cutscene); void MAC_loadCutscene(const char *cutscene);
void MAC_loadCutsceneText(); void MAC_loadCutsceneText();
void MAC_loadCreditsText();
void MAC_loadSounds(); void MAC_loadSounds();
int MAC_getPersoFrame(int anim) const { int MAC_getPersoFrame(int anim) const {

103
resource_mac.cpp Normal file
View File

@ -0,0 +1,103 @@
#include <assert.h>
#include <stdlib.h>
#include <string.h>
#include "fs.h"
#include "resource_mac.h"
#include "util.h"
const char *ResourceMac::FILENAME1 = "Flashback.bin";
const char *ResourceMac::FILENAME2 = "Flashback.rsrc";
ResourceMac::ResourceMac(const char *filePath, FileSystem *fs)
: _dataOffset(0), _types(0), _entries(0) {
memset(&_map, 0, sizeof(_map));
_f.open(filePath, "rb", fs);
}
ResourceMac::~ResourceMac() {
if (_entries) {
for (int i = 0; i < _map.typesCount; ++i) {
free(_entries[i]);
}
free(_entries);
}
free(_types);
}
void ResourceMac::load() {
const uint32_t sig = _f.readUint32BE();
if (sig == 0x00051607) { // AppleDouble
debug(DBG_INFO, "Load Macintosh data from AppleDouble");
_f.seek(24);
const int count = _f.readUint16BE();
for (int i = 0; i < count; ++i) {
const int id = _f.readUint32BE();
const int offset = _f.readUint32BE();
const int length = _f.readUint32BE();
if (id == 2) { // resource fork
loadResourceFork(offset, length);
break;
}
}
} else { // MacBinary
debug(DBG_INFO, "Load Macintosh data from MacBinary");
_f.seek(83);
uint32_t dataSize = _f.readUint32BE();
uint32_t resourceOffset = 128 + ((dataSize + 127) & ~127);
loadResourceFork(resourceOffset, dataSize);
}
}
void ResourceMac::loadResourceFork(uint32_t resourceOffset, uint32_t dataSize) {
_f.seek(resourceOffset);
_dataOffset = resourceOffset + _f.readUint32BE();
uint32_t mapOffset = resourceOffset + _f.readUint32BE();
_f.seek(mapOffset + 22);
_f.readUint16BE();
_map.typesOffset = _f.readUint16BE();
_map.namesOffset = _f.readUint16BE();
_map.typesCount = _f.readUint16BE() + 1;
_f.seek(mapOffset + _map.typesOffset + 2);
_types = (ResourceMacType *)calloc(_map.typesCount, sizeof(ResourceMacType));
for (int i = 0; i < _map.typesCount; ++i) {
_f.read(_types[i].id, 4);
_types[i].count = _f.readUint16BE() + 1;
_types[i].startOffset = _f.readUint16BE();
}
_entries = (ResourceMacEntry **)calloc(_map.typesCount, sizeof(ResourceMacEntry *));
for (int i = 0; i < _map.typesCount; ++i) {
_f.seek(mapOffset + _map.typesOffset + _types[i].startOffset);
_entries[i] = (ResourceMacEntry *)calloc(_types[i].count, sizeof(ResourceMacEntry));
for (int j = 0; j < _types[i].count; ++j) {
_entries[i][j].id = _f.readUint16BE();
_entries[i][j].nameOffset = _f.readUint16BE();
_entries[i][j].dataOffset = _f.readUint32BE() & 0x00FFFFFF;
_f.readUint32BE();
}
for (int j = 0; j < _types[i].count; ++j) {
_entries[i][j].name[0] = '\0';
if (_entries[i][j].nameOffset != 0xFFFF) {
_f.seek(mapOffset + _map.namesOffset + _entries[i][j].nameOffset);
const int len = _f.readByte();
assert(len < kResourceMacEntryNameLength - 1);
_f.read(_entries[i][j].name, len);
_entries[i][j].name[len] = '\0';
}
}
}
}
const ResourceMacEntry *ResourceMac::findEntry(const char *name) const {
for (int type = 0; type < _map.typesCount; ++type) {
for (int i = 0; i < _types[type].count; ++i) {
if (strcmp(name, _entries[type][i].name) == 0) {
return &_entries[type][i];
}
}
}
return 0;
}

53
resource_mac.h Normal file
View File

@ -0,0 +1,53 @@
#ifndef RESOURCE_MAC_H__
#define RESOURCE_MAC_H__
#include <stdint.h>
#include "file.h"
struct ResourceMacMap {
uint16_t typesOffset;
uint16_t namesOffset;
uint16_t typesCount;
};
struct ResourceMacType {
unsigned char id[4];
uint16_t count;
uint16_t startOffset;
};
enum {
kResourceMacEntryNameLength = 64
};
struct ResourceMacEntry {
uint16_t id;
uint16_t nameOffset;
uint32_t dataOffset;
char name[kResourceMacEntryNameLength];
};
struct ResourceMac {
static const char *FILENAME1;
static const char *FILENAME2;
File _f;
uint32_t _dataOffset;
ResourceMacMap _map;
ResourceMacType *_types;
ResourceMacEntry **_entries;
ResourceMac(const char *filePath, FileSystem *);
~ResourceMac();
bool isOpen() const { return _entries != 0; }
void load();
void loadResourceFork(uint32_t offset, uint32_t size);
const ResourceMacEntry *findEntry(const char *name) const;
};
#endif

View File

@ -97,11 +97,11 @@ void SfxPlayer::handleTick() {
} }
} }
void SfxPlayer::mixSamples(int8_t *buf, int samplesLen) { void SfxPlayer::mixSamples(int16_t *buf, int samplesLen) {
for (int i = 0; i < NUM_CHANNELS; ++i) { for (int i = 0; i < NUM_CHANNELS; ++i) {
SampleInfo *si = &_samples[i]; SampleInfo *si = &_samples[i];
if (si->data) { if (si->data) {
int8_t *mixbuf = buf; int16_t *mixbuf = buf;
int len = si->len << FRAC_BITS; int len = si->len << FRAC_BITS;
int loopLen = si->loopLen << FRAC_BITS; int loopLen = si->loopLen << FRAC_BITS;
int loopPos = si->loopPos << FRAC_BITS; int loopPos = si->loopPos << FRAC_BITS;
@ -127,7 +127,7 @@ void SfxPlayer::mixSamples(int8_t *buf, int samplesLen) {
} }
while (count--) { while (count--) {
const int out = si->getPCM(pos >> FRAC_BITS); const int out = si->getPCM(pos >> FRAC_BITS);
*mixbuf = ADDC_S8(*mixbuf, out * si->vol / 64); *mixbuf = ADDC_S16(*mixbuf, (out * si->vol / 64) << 8);
++mixbuf; ++mixbuf;
pos += deltaPos; pos += deltaPos;
} }
@ -137,7 +137,8 @@ void SfxPlayer::mixSamples(int8_t *buf, int samplesLen) {
} }
} }
bool SfxPlayer::mix(int8_t *buf, int len) { bool SfxPlayer::mix(int16_t *buf, int len) {
memset(buf, 0, sizeof(int16_t) * len);
if (_playing) { if (_playing) {
const int samplesPerTick = _mix->getSampleRate() / 50; const int samplesPerTick = _mix->getSampleRate() / 50;
while (len != 0) { while (len != 0) {
@ -159,11 +160,5 @@ bool SfxPlayer::mix(int8_t *buf, int len) {
} }
bool SfxPlayer::mixCallback(void *param, int16_t *samples, int len) { bool SfxPlayer::mixCallback(void *param, int16_t *samples, int len) {
int8_t buf[len]; return ((SfxPlayer *)param)->mix(samples, len);
memset(buf, 0, sizeof(buf));
const bool ret = ((SfxPlayer *)param)->mix(buf, len);
for (int i = 0; i < len; ++i) {
samples[i] = buf[i] << 8;
}
return ret;
} }

View File

@ -81,9 +81,9 @@ struct SfxPlayer {
void stop(); void stop();
void playSample(int channel, const uint8_t *sampleData, uint16_t period); void playSample(int channel, const uint8_t *sampleData, uint16_t period);
void handleTick(); void handleTick();
void mixSamples(int8_t *samples, int samplesLen); void mixSamples(int16_t *samples, int samplesLen);
bool mix(int8_t *buf, int len); bool mix(int16_t *buf, int len);
static bool mixCallback(void *param, int16_t *buf, int len); static bool mixCallback(void *param, int16_t *buf, int len);
}; };

View File

@ -3223,6 +3223,16 @@ const uint8_t Game::_protectionPal[] = {
0x08, 0x88, 0x09, 0x99, 0x0A, 0xAA, 0x0B, 0xBB, 0x0C, 0xCC, 0x0D, 0xDD, 0x0E, 0xEE, 0x0F, 0xFF 0x08, 0x88, 0x09, 0x99, 0x0A, 0xAA, 0x0B, 0xBB, 0x0C, 0xCC, 0x0D, 0xDD, 0x0E, 0xEE, 0x0F, 0xFF
}; };
const char *Menu::_levelNames[] {
"Titan / The Jungle",
"Titan / New Washington",
"Titan / Death Tower Show",
"Earth / Surface",
"Earth / Paradise Club",
"Planet Morphs / Surface",
"Planet Morphs / Inner Core"
};
const char *Menu::_passwordsDOS[] = { const char *Menu::_passwordsDOS[] = {
"JAGUAR", "COMBEL", "ANTIC", "NOLAN", "ARTHUR", "SHIRYU", "RENDER", "BELUGA", // easy "JAGUAR", "COMBEL", "ANTIC", "NOLAN", "ARTHUR", "SHIRYU", "RENDER", "BELUGA", // easy
"BANTHA", "SHIVA", "KASYYK", "SARLAC", "MAENOC", "SULUST", "NEPTUN", "BELUGA", // normal "BANTHA", "SHIVA", "KASYYK", "SARLAC", "MAENOC", "SULUST", "NEPTUN", "BELUGA", // normal

View File

@ -49,12 +49,12 @@ Video::~Video() {
free(_screenBlocks); free(_screenBlocks);
} }
void Video::markBlockAsDirty(int16_t x, int16_t y, uint16_t w, uint16_t h) { void Video::markBlockAsDirty(int16_t x, int16_t y, uint16_t w, uint16_t h, int scale) {
debug(DBG_VIDEO, "Video::markBlockAsDirty(%d, %d, %d, %d)", x, y, w, h); debug(DBG_VIDEO, "Video::markBlockAsDirty(%d, %d, %d, %d)", x, y, w, h);
int bx1 = _layerScale * x / SCREENBLOCK_W; int bx1 = scale * x / SCREENBLOCK_W;
int by1 = _layerScale * y / SCREENBLOCK_H; int by1 = scale * y / SCREENBLOCK_H;
int bx2 = _layerScale * (x + w - 1) / SCREENBLOCK_W; int bx2 = scale * (x + w - 1) / SCREENBLOCK_W;
int by2 = _layerScale * (y + h - 1) / SCREENBLOCK_H; int by2 = scale * (y + h - 1) / SCREENBLOCK_H;
if (bx1 < 0) { if (bx1 < 0) {
bx1 = 0; bx1 = 0;
} }
@ -907,7 +907,7 @@ 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) { 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; DecodeBuffer buf;
memset(&buf, 0, sizeof(buf)); memset(&buf, 0, sizeof(buf));
buf.ptr = _frontLayer; buf.ptr = dst;
buf.w = buf.pitch = _w; buf.w = buf.pitch = _w;
buf.h = _h; buf.h = _h;
buf.x = x * _layerScale; buf.x = x * _layerScale;
@ -931,7 +931,7 @@ const char *Video::drawString(const char *str, int16_t x, int16_t y, uint8_t col
(this->*_drawChar)(_frontLayer, _w, x + len * CHAR_W, y, fnt, col, c); (this->*_drawChar)(_frontLayer, _w, x + len * CHAR_W, y, fnt, col, c);
++len; ++len;
} }
markBlockAsDirty(x, y, len * CHAR_W, CHAR_H); markBlockAsDirty(x, y, len * CHAR_W, CHAR_H, _layerScale);
return str - 1; return str - 1;
} }
@ -940,7 +940,7 @@ void Video::drawStringLen(const char *str, int len, int x, int y, uint8_t color)
for (int i = 0; i < len; ++i) { for (int i = 0; i < len; ++i) {
(this->*_drawChar)(_frontLayer, _w, x + i * CHAR_W, y, fnt, color, str[i]); (this->*_drawChar)(_frontLayer, _w, x + i * CHAR_W, y, fnt, color, str[i]);
} }
markBlockAsDirty(x, y, len * CHAR_W, CHAR_H); markBlockAsDirty(x, y, len * CHAR_W, CHAR_H, _layerScale);
} }
Color Video::AMIGA_convertColor(const uint16_t color, bool bgr) { // 4bits to 8bits Color Video::AMIGA_convertColor(const uint16_t color, bool bgr) { // 4bits to 8bits
@ -1052,7 +1052,6 @@ void Video::MAC_drawSprite(int x, int y, const uint8_t *data, int frame, bool xf
buf.setPixel = eraseBackground ? MAC_drawBuffer : MAC_drawBufferMask; buf.setPixel = eraseBackground ? MAC_drawBuffer : MAC_drawBufferMask;
fixOffsetDecodeBuffer(&buf, dataPtr); fixOffsetDecodeBuffer(&buf, dataPtr);
_res->MAC_decodeImageData(data, frame, &buf); _res->MAC_decodeImageData(data, frame, &buf);
// divide by screen scale as the dirty blocks range is 256,224 markBlockAsDirty(buf.x, buf.y, READ_BE_UINT16(dataPtr), READ_BE_UINT16(dataPtr + 2), 1);
markBlockAsDirty(buf.x / _layerScale, buf.y / _layerScale, READ_BE_UINT16(dataPtr) / _layerScale, READ_BE_UINT16(dataPtr + 2) / _layerScale);
} }
} }

View File

@ -53,7 +53,7 @@ struct Video {
Video(Resource *res, SystemStub *stub); Video(Resource *res, SystemStub *stub);
~Video(); ~Video();
void markBlockAsDirty(int16_t x, int16_t y, uint16_t w, uint16_t h); void markBlockAsDirty(int16_t x, int16_t y, uint16_t w, uint16_t h, int scale);
void updateScreen(); void updateScreen();
void fullRefresh(); void fullRefresh();
void fadeOut(); void fadeOut();