Import 0.4.4
This commit is contained in:
parent
514785d0a1
commit
222984d851
2
Makefile
2
Makefile
|
@ -7,7 +7,7 @@ MODPLUG_LIBS := -lmodplug
|
|||
TREMOR_LIBS := -lvorbisidec -logg
|
||||
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 \
|
||||
menu.cpp mixer.cpp mod_player.cpp ogg_player.cpp piege.cpp resource.cpp resource_aba.cpp \
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
|
||||
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
|
||||
--scaler=NAME@X Graphics scaler (default 'scale@3')
|
||||
--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
|
||||
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).
|
||||
|
||||
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
|
||||
|
||||
In-game hotkeys :
|
||||
|
@ -83,7 +84,6 @@ Credits:
|
|||
|
||||
Delphine Software, obviously, for making another great game.
|
||||
Yaz0r, Pixel and gawd for sharing information they gathered on the game.
|
||||
Nicolas Bondoux for sound fixes.
|
||||
|
||||
|
||||
Contact:
|
||||
|
|
61
cutscene.cpp
61
cutscene.cpp
|
@ -112,12 +112,12 @@ static bool isNewLineChar(uint8_t chr, Resource *res) {
|
|||
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;
|
||||
uint16_t ret = 0;
|
||||
uint16_t pos = 0;
|
||||
for (; *p != 0xA && *p; ++p) {
|
||||
if (isNewLineChar(*p, _res)) {
|
||||
for (int i = 0; i < len && p[i] != 0xA; ++i) {
|
||||
if (isNewLineChar(p[i], _res)) {
|
||||
*q++ = pos;
|
||||
if (pos > ret) {
|
||||
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) {
|
||||
debug(DBG_CUT, "Cutscene::drawText(x=%d, y=%d, c=%d, justify=%d)", x, y, color, textJustify);
|
||||
int len = 0;
|
||||
if (_res->_type == kResourceTypeMac) {
|
||||
warning("Unhandled Cutscene::drawText"); // TODO
|
||||
return;
|
||||
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;
|
||||
uint16_t last_sep = 0;
|
||||
uint16_t lastSep = 0;
|
||||
if (textJustify != kTextJustifyLeft) {
|
||||
last_sep = findTextSeparators(p);
|
||||
lastSep = findTextSeparators(p, len);
|
||||
if (textJustify != kTextJustifyCenter) {
|
||||
last_sep = (_res->_lang == LANG_JP) ? 20 : 30;
|
||||
lastSep = (_res->_lang == LANG_JP) ? 20 : 30;
|
||||
}
|
||||
}
|
||||
const uint8_t *sep = _textSep;
|
||||
y += 50;
|
||||
x += (_res->_lang == LANG_JP) ? 0 : 8;
|
||||
int16_t yy = y;
|
||||
int16_t xx = x;
|
||||
int16_t yPos = y;
|
||||
int16_t xPos = x;
|
||||
if (textJustify != kTextJustifyLeft) {
|
||||
xx += ((last_sep - *sep++) / 2) * Video::CHAR_W;
|
||||
xPos += ((lastSep - *sep++) / 2) * Video::CHAR_W;
|
||||
}
|
||||
for (; *p != 0xA && *p; ++p) {
|
||||
if (isNewLineChar(*p, _res)) {
|
||||
yy += Video::CHAR_H;
|
||||
xx = x;
|
||||
for (int i = 0; i < len && p[i] != 0xA; ++i) {
|
||||
if (isNewLineChar(p[i], _res)) {
|
||||
yPos += Video::CHAR_H;
|
||||
xPos = x;
|
||||
if (textJustify != kTextJustifyLeft) {
|
||||
xx += ((last_sep - *sep++) / 2) * Video::CHAR_W;
|
||||
xPos += ((lastSep - *sep++) / 2) * Video::CHAR_W;
|
||||
}
|
||||
} else if (*p == 0x20) {
|
||||
xx += Video::CHAR_W;
|
||||
} else if (*p == 0x9) {
|
||||
} else if (p[i] == 0x20) {
|
||||
xPos += Video::CHAR_W;
|
||||
} else if (p[i] == 0x9) {
|
||||
// ignore tab
|
||||
} else {
|
||||
(_vid->*dcf)(page, _vid->_w, xx, yy, fnt, color, *p);
|
||||
xx += Video::CHAR_W;
|
||||
(_vid->*dcf)(page, _vid->_w, xPos, yPos, fnt, color, p[i]);
|
||||
xPos += Video::CHAR_W;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -415,9 +417,12 @@ void Cutscene::op_drawStringAtBottom() {
|
|||
}
|
||||
}
|
||||
|
||||
memset(_pageC + 179 * _vid->_w, 0xC0, 45 * _vid->_w);
|
||||
memset(_page1 + 179 * _vid->_w, 0xC0, 45 * _vid->_w);
|
||||
memset(_page0 + 179 * _vid->_w, 0xC0, 45 * _vid->_w);
|
||||
const int h = 45 * _vid->_layerScale;
|
||||
const int y = Video::GAMESCREEN_H * _vid->_layerScale - h;
|
||||
|
||||
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) {
|
||||
const uint8_t *str = _res->getCineString(strId);
|
||||
if (str) {
|
||||
|
@ -878,7 +883,7 @@ void Cutscene::op_drawStringAtPos() {
|
|||
// 'voyage' - cutscene script redraws the string to refresh the screen
|
||||
if (_id == 0x34 && (strId & 0xFFF) == 0x45) {
|
||||
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);
|
||||
} else {
|
||||
_stub->sleep(15);
|
||||
|
@ -1059,9 +1064,7 @@ void Cutscene::unload() {
|
|||
void Cutscene::prepare() {
|
||||
_page0 = _vid->_frontLayer;
|
||||
_page1 = _vid->_tempLayer;
|
||||
memset(_page1, 0, _vid->_layerSize);
|
||||
_pageC = _vid->_tempLayer2;
|
||||
memset(_pageC, 0, _vid->_layerSize);
|
||||
_stub->_pi.dirMask = 0;
|
||||
_stub->_pi.enter = false;
|
||||
_stub->_pi.space = false;
|
||||
|
@ -1080,6 +1083,10 @@ void Cutscene::prepare() {
|
|||
}
|
||||
|
||||
void Cutscene::playCredits() {
|
||||
if (_res->isMac()) {
|
||||
warning("Cutscene::playCredits() unimplemented");
|
||||
return;
|
||||
}
|
||||
_textCurPtr = _res->isAmiga() ? _creditsDataAmiga : _creditsDataDOS;
|
||||
_textBuf[0] = 0xA;
|
||||
_textCurBuf = _textBuf;
|
||||
|
|
|
@ -113,7 +113,7 @@ struct Cutscene {
|
|||
void updatePalette();
|
||||
void setPalette();
|
||||
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 swapLayers();
|
||||
void drawCreditsText();
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
89
game.cpp
89
game.cpp
|
@ -14,7 +14,7 @@
|
|||
#include "unpack.h"
|
||||
#include "util.h"
|
||||
|
||||
Game::Game(SystemStub *stub, FileSystem *fs, const char *savePath, int level, ResourceType ver, Language lang, 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),
|
||||
_mix(fs, stub), _res(fs, ver, lang), _seq(stub, &_mix), _vid(&_res, stub),
|
||||
_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;
|
||||
_demoBin = -1;
|
||||
_widescreenMode = widescreenMode;
|
||||
_autoSave = autoSave;
|
||||
}
|
||||
|
||||
void Game::run() {
|
||||
|
@ -150,6 +151,7 @@ void Game::run() {
|
|||
resetGameState();
|
||||
_endLoop = false;
|
||||
_frameTimestamp = _stub->getTimeStamp();
|
||||
_saveTimestamp = _frameTimestamp;
|
||||
while (!_stub->_pi.quit && !_endLoop) {
|
||||
mainLoop();
|
||||
if (_demoBin != -1 && _inp_demPos >= _res._demLen) {
|
||||
|
@ -166,8 +168,8 @@ void Game::run() {
|
|||
_stub->_pi.shift = false;
|
||||
// clear widescreen borders
|
||||
if (_stub->hasWidescreen()) {
|
||||
_stub->copyRectLeftBorder(Video::GAMESCREEN_W, Video::GAMESCREEN_H, 0);
|
||||
_stub->copyRectRightBorder(Video::GAMESCREEN_W, Video::GAMESCREEN_H, 0);
|
||||
_stub->copyRectLeftBorder(_vid._w, _vid._h, 0);
|
||||
_stub->copyRectRightBorder(_vid._w, _vid._h, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -271,10 +273,34 @@ void Game::displayTitleScreenMac(int num) {
|
|||
Color c;
|
||||
c.r = c.g = c.b = 0;
|
||||
_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->updateScreen(0);
|
||||
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();
|
||||
if (_stub->_pi.quit) {
|
||||
break;
|
||||
|
@ -324,10 +350,10 @@ void Game::mainLoop() {
|
|||
playCutscene(0x41);
|
||||
_endLoop = true;
|
||||
} else {
|
||||
if (_validSaveState) {
|
||||
if (!loadGameState(0)) {
|
||||
_endLoop = true;
|
||||
}
|
||||
if (_autoSave && loadGameState(kAutoSaveSlot)) {
|
||||
// autosave
|
||||
} else if (_validSaveState && loadGameState(kIngameSaveSlot)) {
|
||||
// ingame save
|
||||
} else {
|
||||
loadLevelData();
|
||||
resetGameState();
|
||||
|
@ -393,6 +419,13 @@ void Game::mainLoop() {
|
|||
}
|
||||
}
|
||||
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() {
|
||||
|
@ -474,7 +507,7 @@ void Game::playCutscene(int id) {
|
|||
_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)
|
||||
Color palette[32];
|
||||
_res.MAC_copyClut16(palette, 0, 0x37);
|
||||
|
@ -533,8 +566,8 @@ void Game::drawCurrentInventoryItem() {
|
|||
|
||||
void Game::showFinalScore() {
|
||||
if (_stub->hasWidescreen()) {
|
||||
_stub->copyRectLeftBorder(Video::GAMESCREEN_W, Video::GAMESCREEN_H, 0);
|
||||
_stub->copyRectRightBorder(Video::GAMESCREEN_W, Video::GAMESCREEN_H, 0);
|
||||
_stub->copyRectLeftBorder(_vid._w, _vid._h, 0);
|
||||
_stub->copyRectRightBorder(_vid._w, _vid._h, 0);
|
||||
}
|
||||
playCutscene(0x49);
|
||||
char buf[50];
|
||||
|
@ -697,8 +730,8 @@ bool Game::handleConfigPanel() {
|
|||
|
||||
bool Game::handleContinueAbort() {
|
||||
if (_stub->hasWidescreen()) {
|
||||
_stub->copyRectLeftBorder(Video::GAMESCREEN_W, Video::GAMESCREEN_H, 0);
|
||||
_stub->copyRectRightBorder(Video::GAMESCREEN_W, Video::GAMESCREEN_H, 0);
|
||||
_stub->copyRectLeftBorder(_vid._w, _vid._h, 0);
|
||||
_stub->copyRectRightBorder(_vid._w, _vid._h, 0);
|
||||
}
|
||||
playCutscene(0x48);
|
||||
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.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) {
|
||||
|
@ -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.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) {
|
||||
|
@ -1617,14 +1650,30 @@ void Game::loadLevelMap() {
|
|||
}
|
||||
}
|
||||
_vid.PC_decodeMap(_currentLevel, _currentRoom);
|
||||
if (_stub->hasWidescreen() && _widescreenMode == kWidescreenMirrorRoom) {
|
||||
_stub->copyRectMirrorBorders(Video::GAMESCREEN_W, Video::GAMESCREEN_H, _vid._backLayer);
|
||||
}
|
||||
break;
|
||||
case kResourceTypeMac:
|
||||
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);
|
||||
break;
|
||||
}
|
||||
if (_stub->hasWidescreen() && _widescreenMode == kWidescreenMirrorRoom) {
|
||||
_stub->copyRectMirrorBorders(_vid._w, _vid._h, _vid._backLayer);
|
||||
}
|
||||
}
|
||||
|
||||
void Game::loadLevelData() {
|
||||
|
@ -1806,7 +1855,7 @@ void Game::drawIcon(uint8_t iconNum, int16_t x, int16_t y, uint8_t colMask) {
|
|||
return;
|
||||
}
|
||||
_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) {
|
||||
|
@ -2006,7 +2055,7 @@ static const uint32_t TAG_FBSV = 0x46425356;
|
|||
|
||||
bool Game::saveGameState(uint8_t slot) {
|
||||
bool success = false;
|
||||
char stateFile[20];
|
||||
char stateFile[32];
|
||||
makeGameStateName(slot, stateFile);
|
||||
File f;
|
||||
if (!f.open(stateFile, "zwb", _savePath)) {
|
||||
|
@ -2033,7 +2082,7 @@ bool Game::saveGameState(uint8_t slot) {
|
|||
|
||||
bool Game::loadGameState(uint8_t slot) {
|
||||
bool success = false;
|
||||
char stateFile[20];
|
||||
char stateFile[32];
|
||||
makeGameStateName(slot, stateFile);
|
||||
File f;
|
||||
if (!f.open(stateFile, "zrb", _savePath)) {
|
||||
|
|
10
game.h
10
game.h
|
@ -25,6 +25,12 @@ struct Game {
|
|||
typedef int (Game::*col_Callback1)(LivePGE *, LivePGE *, 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 {
|
||||
CT_UP_ROOM = 0x00,
|
||||
CT_DOWN_ROOM = 0x40,
|
||||
|
@ -87,8 +93,10 @@ struct Game {
|
|||
bool _endLoop;
|
||||
uint32_t _frameTimestamp;
|
||||
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 displayTitleScreenAmiga();
|
||||
|
|
15
graphics.cpp
15
graphics.cpp
|
@ -211,21 +211,18 @@ void Graphics::fillArea(uint8_t color, bool hasAlpha) {
|
|||
if (x1 >= 0) {
|
||||
if (hasAlpha && color > 0xC7) {
|
||||
do {
|
||||
int16_t x2 = *pts++;
|
||||
if (x2 < _crw && x2 >= x1) {
|
||||
int len = x2 - x1 + 1;
|
||||
for (int i = 0; i < len; ++i) {
|
||||
*(dst + x1 + i) |= color & 8; // XXX 0x88
|
||||
}
|
||||
const int16_t x2 = MIN<int16_t>(_crw - 1, *pts++);
|
||||
for (; x1 <= x2; ++x1) {
|
||||
*(dst + x1) |= color & 8;
|
||||
}
|
||||
dst += _layerPitch;
|
||||
x1 = *pts++;
|
||||
} while (x1 >= 0);
|
||||
} else {
|
||||
do {
|
||||
int16_t x2 = *pts++;
|
||||
if (x2 < _crw && x2 >= x1) {
|
||||
int len = x2 - x1 + 1;
|
||||
const int16_t x2 = MIN<int16_t>(_crw - 1, *pts++);
|
||||
if (x1 <= x2) {
|
||||
const int len = x2 - x1 + 1;
|
||||
memset(dst + x1, color, len);
|
||||
}
|
||||
dst += _layerPitch;
|
||||
|
|
34
intern.h
34
intern.h
|
@ -13,12 +13,6 @@
|
|||
#include <assert.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
|
||||
#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];
|
||||
}
|
||||
|
||||
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) {
|
||||
a += b;
|
||||
if (a < -32768) {
|
||||
|
@ -79,6 +63,24 @@ inline T CLIP(const T& val, const T& a, const T& b) {
|
|||
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 {
|
||||
LANG_FR,
|
||||
LANG_EN,
|
||||
|
|
8
main.cpp
8
main.cpp
|
@ -25,6 +25,7 @@ static const char *USAGE =
|
|||
" --widescreen=MODE 16:9 display\n"
|
||||
" --scaler=NAME@X Graphics scaler (default 'scale@3')\n"
|
||||
" --language=LANG Language (fr,en,de,sp,it,jp)\n"
|
||||
" --autosave Save game state automatically\n"
|
||||
;
|
||||
|
||||
static int detectVersion(FileSystem *fs) {
|
||||
|
@ -215,6 +216,7 @@ int main(int argc, char *argv[]) {
|
|||
const char *savePath = ".";
|
||||
int levelNum = 0;
|
||||
bool fullscreen = false;
|
||||
bool autoSave = false;
|
||||
WidescreenMode widescreen = kWidescreenNone;
|
||||
ScalerParameters scalerParameters = ScalerParameters::defaults();
|
||||
int forcedLanguage = -1;
|
||||
|
@ -234,6 +236,7 @@ int main(int argc, char *argv[]) {
|
|||
{ "scaler", required_argument, 0, 5 },
|
||||
{ "language", required_argument, 0, 6 },
|
||||
{ "widescreen", required_argument, 0, 7 },
|
||||
{ "autosave", no_argument, 0, 8 },
|
||||
{ 0, 0, 0, 0 }
|
||||
};
|
||||
int index;
|
||||
|
@ -281,6 +284,9 @@ int main(int argc, char *argv[]) {
|
|||
case 7:
|
||||
widescreen = parseWidescreen(optarg);
|
||||
break;
|
||||
case 8:
|
||||
autoSave = true;
|
||||
break;
|
||||
default:
|
||||
printf(USAGE, argv[0]);
|
||||
return 0;
|
||||
|
@ -296,7 +302,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);
|
||||
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);
|
||||
g->run();
|
||||
delete g;
|
||||
|
|
19
menu.cpp
19
menu.cpp
|
@ -84,7 +84,7 @@ void Menu::drawString2(const char *str, int16_t y, int16_t x) {
|
|||
}
|
||||
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) {
|
||||
|
@ -201,7 +201,7 @@ bool Menu::handlePasswordScreen() {
|
|||
}
|
||||
_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();
|
||||
_stub->sleep(EVENTS_DELAY);
|
||||
_stub->processEvents();
|
||||
|
@ -254,24 +254,15 @@ bool Menu::handleLevelScreen() {
|
|||
int currentSkill = _skill;
|
||||
int currentLevel = _level;
|
||||
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) {
|
||||
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_14_NORMAL), 23, 14, (currentSkill == 1) ? 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();
|
||||
_stub->sleep(EVENTS_DELAY);
|
||||
|
|
1
menu.h
1
menu.h
|
@ -48,6 +48,7 @@ struct Menu {
|
|||
int opt;
|
||||
};
|
||||
|
||||
static const char *_levelNames[];
|
||||
static const char *_passwordsDOS[];
|
||||
static const char *_passwordsFrAmiga[];
|
||||
static const char *_passwordsEnAmiga[];
|
||||
|
|
10
mixer.cpp
10
mixer.cpp
|
@ -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) {
|
||||
if (_premixHook) {
|
||||
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) {
|
||||
|
|
|
@ -156,8 +156,7 @@ struct ModPlayer_impl {
|
|||
void applyVibrato(int trackNum);
|
||||
void applyPortamento(int trackNum);
|
||||
void handleEffect(int trackNum, bool tick);
|
||||
void mixSamples(int8_t *buf, int len);
|
||||
bool mixS8(int8_t *buf, int len);
|
||||
void mixSamples(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) {
|
||||
Track *tk = &_tracks[i];
|
||||
if (tk->sample != 0 && tk->delayCounter == 0) {
|
||||
int8_t *mixbuf = buf;
|
||||
int16_t *mixbuf = buf;
|
||||
SampleInfo *si = tk->sample;
|
||||
int len = si->len << FRAC_BITS;
|
||||
int loopLen = si->repeatLen << FRAC_BITS;
|
||||
|
@ -614,7 +613,7 @@ void ModPlayer_impl::mixSamples(int8_t *buf, int samplesLen) {
|
|||
}
|
||||
while (count--) {
|
||||
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;
|
||||
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) {
|
||||
const int samplesPerTick = _mixingRate / (50 * _songTempo / 125);
|
||||
while (len != 0) {
|
||||
|
@ -644,16 +644,6 @@ bool ModPlayer_impl::mixS8(int8_t *buf, int len) {
|
|||
}
|
||||
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
|
||||
|
||||
ModPlayer::ModPlayer(Mixer *mixer, FileSystem *fs)
|
||||
|
|
|
@ -325,7 +325,7 @@ int Game::pge_execute(LivePGE *live_pge, InitPGE *init_pge, const Object *obj) {
|
|||
++live_pge->life;
|
||||
}
|
||||
if (obj->flags & 8) {
|
||||
live_pge->life = 0xFFFF;
|
||||
live_pge->life = -1;
|
||||
}
|
||||
|
||||
if (live_pge->flags & 1) {
|
||||
|
@ -1428,7 +1428,7 @@ int Game::pge_op_setCollisionState2(ObjectOpcodeArgs *args) {
|
|||
|
||||
int Game::pge_op_saveState(ObjectOpcodeArgs *args) {
|
||||
_saveStateCompleted = true;
|
||||
_validSaveState = saveGameState(0);
|
||||
_validSaveState = saveGameState(kIngameSaveSlot);
|
||||
return 0xFFFF;
|
||||
}
|
||||
|
||||
|
|
|
@ -1679,6 +1679,10 @@ void Resource::MAC_loadCutsceneText() {
|
|||
_cine_off = 0; // offsets are prepended to _cine_txt
|
||||
}
|
||||
|
||||
void Resource::MAC_loadCreditsText() {
|
||||
_credits = decodeResourceMacData("Credit strings", false);
|
||||
}
|
||||
|
||||
void Resource::MAC_loadSounds() {
|
||||
static const int8_t table[NUM_SFXS] = {
|
||||
0, -1, 1, 2, 3, 4, -1, 5, 6, 7, 8, 9, 10, 11, -1, 12,
|
||||
|
|
|
@ -177,6 +177,7 @@ struct Resource {
|
|||
uint8_t *_perso;
|
||||
uint8_t *_monster;
|
||||
uint8_t *_str;
|
||||
uint8_t *_credits;
|
||||
|
||||
Resource(FileSystem *fs, ResourceType type, Language lang);
|
||||
~Resource();
|
||||
|
@ -335,6 +336,7 @@ struct Resource {
|
|||
void MAC_unloadCutscene();
|
||||
void MAC_loadCutscene(const char *cutscene);
|
||||
void MAC_loadCutsceneText();
|
||||
void MAC_loadCreditsText();
|
||||
void MAC_loadSounds();
|
||||
|
||||
int MAC_getPersoFrame(int anim) const {
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
@ -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) {
|
||||
SampleInfo *si = &_samples[i];
|
||||
if (si->data) {
|
||||
int8_t *mixbuf = buf;
|
||||
int16_t *mixbuf = buf;
|
||||
int len = si->len << FRAC_BITS;
|
||||
int loopLen = si->loopLen << FRAC_BITS;
|
||||
int loopPos = si->loopPos << FRAC_BITS;
|
||||
|
@ -127,7 +127,7 @@ void SfxPlayer::mixSamples(int8_t *buf, int samplesLen) {
|
|||
}
|
||||
while (count--) {
|
||||
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;
|
||||
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) {
|
||||
const int samplesPerTick = _mix->getSampleRate() / 50;
|
||||
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) {
|
||||
int8_t buf[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;
|
||||
return ((SfxPlayer *)param)->mix(samples, len);
|
||||
}
|
||||
|
|
|
@ -81,9 +81,9 @@ struct SfxPlayer {
|
|||
void stop();
|
||||
void playSample(int channel, const uint8_t *sampleData, uint16_t period);
|
||||
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);
|
||||
};
|
||||
|
||||
|
|
|
@ -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
|
||||
};
|
||||
|
||||
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[] = {
|
||||
"JAGUAR", "COMBEL", "ANTIC", "NOLAN", "ARTHUR", "SHIRYU", "RENDER", "BELUGA", // easy
|
||||
"BANTHA", "SHIVA", "KASYYK", "SARLAC", "MAENOC", "SULUST", "NEPTUN", "BELUGA", // normal
|
||||
|
|
19
video.cpp
19
video.cpp
|
@ -49,12 +49,12 @@ Video::~Video() {
|
|||
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);
|
||||
int bx1 = _layerScale * x / SCREENBLOCK_W;
|
||||
int by1 = _layerScale * y / SCREENBLOCK_H;
|
||||
int bx2 = _layerScale * (x + w - 1) / SCREENBLOCK_W;
|
||||
int by2 = _layerScale * (y + h - 1) / SCREENBLOCK_H;
|
||||
int bx1 = scale * x / SCREENBLOCK_W;
|
||||
int by1 = scale * y / SCREENBLOCK_H;
|
||||
int bx2 = scale * (x + w - 1) / SCREENBLOCK_W;
|
||||
int by2 = scale * (y + h - 1) / SCREENBLOCK_H;
|
||||
if (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) {
|
||||
DecodeBuffer buf;
|
||||
memset(&buf, 0, sizeof(buf));
|
||||
buf.ptr = _frontLayer;
|
||||
buf.ptr = dst;
|
||||
buf.w = buf.pitch = _w;
|
||||
buf.h = _h;
|
||||
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);
|
||||
++len;
|
||||
}
|
||||
markBlockAsDirty(x, y, len * CHAR_W, CHAR_H);
|
||||
markBlockAsDirty(x, y, len * CHAR_W, CHAR_H, _layerScale);
|
||||
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) {
|
||||
(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
|
||||
|
@ -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;
|
||||
fixOffsetDecodeBuffer(&buf, dataPtr);
|
||||
_res->MAC_decodeImageData(data, frame, &buf);
|
||||
// divide by screen scale as the dirty blocks range is 256,224
|
||||
markBlockAsDirty(buf.x / _layerScale, buf.y / _layerScale, READ_BE_UINT16(dataPtr) / _layerScale, READ_BE_UINT16(dataPtr + 2) / _layerScale);
|
||||
markBlockAsDirty(buf.x, buf.y, READ_BE_UINT16(dataPtr), READ_BE_UINT16(dataPtr + 2), 1);
|
||||
}
|
||||
}
|
||||
|
|
2
video.h
2
video.h
|
@ -53,7 +53,7 @@ struct Video {
|
|||
Video(Resource *res, SystemStub *stub);
|
||||
~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 fullRefresh();
|
||||
void fadeOut();
|
||||
|
|
Loading…
Reference in New Issue