Import 0.3.1

This commit is contained in:
Gregory Montoir 2016-05-10 00:00:00 +08:00
parent cb9e469636
commit 577fec920a
26 changed files with 775 additions and 288 deletions

View File

@ -1,12 +1,12 @@
SDL_CFLAGS = `sdl-config --cflags`
SDL_LIBS = `sdl-config --libs`
VORBIS_LIBS = -lvorbisidec
MODPLUG_LIBS = -lmodplug
# TREMOR_LIBS = -lvorbisidec -logg
ZLIB_LIBS = -lz
CXX := clang++
CXXFLAGS := -Wall -MMD $(SDL_CFLAGS) -DUSE_ZLIB # -DUSE_MODPLUG
CXXFLAGS := -Wall -MMD $(SDL_CFLAGS) -DUSE_MODPLUG -DUSE_ZLIB # -DUSE_TREMOR
SRCS = collision.cpp cutscene.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 \
@ -16,7 +16,7 @@ SRCS = collision.cpp cutscene.cpp file.cpp fs.cpp game.cpp graphics.cpp main.cpp
OBJS = $(SRCS:.cpp=.o)
DEPS = $(SRCS:.cpp=.d)
LIBS = $(SDL_LIBS) $(VORBIS_LIBS) $(MODPLUG_LIBS) $(ZLIB_LIBS)
LIBS = $(SDL_LIBS) $(MODPLUG_LIBS) $(TREMOR_LIBS) $(ZLIB_LIBS)
rs: $(OBJS)
$(CXX) $(LDFLAGS) -o $@ $(OBJS) $(LIBS)

View File

@ -1,6 +1,6 @@
REminiscence README
Release version: 0.3.0 ($date)
Release version: 0.3.1
-------------------------------------------------------------------------------
@ -15,7 +15,8 @@ game can be found at [1], [2] and [3].
Compiling:
----------
Update the defines in the Makefile if needed. The SDL and zlib libraries are required.
Update the defines in the Makefile if needed. The SDL, zlib and modplug
libraries are required.
Data Files:
@ -64,6 +65,10 @@ directory. These paths can be changed using command line switches :
Usage: rs [OPTIONS]...
--datapath=PATH Path to data files (default 'DATA')
--savepath=PATH Path to save files (default '.')
--levelnum=NUM Level to start from (default '0')
--fullscreen Fullscreen display
--scaler=INDEX Graphics scaler
--language=LANG Language (fr,en,de,sp,it)
In-game hotkeys :

View File

@ -18,13 +18,16 @@ Cutscene::Cutscene(Resource *res, SystemStub *stub, Video *vid)
}
void Cutscene::sync() {
// XXX input handling
if (!(_stub->_pi.dbgMask & PlayerInput::DF_FASTMODE)) {
int32_t delay = _stub->getTimeStamp() - _tstamp;
int32_t pause = _frameDelay * TIMER_SLICE - delay;
if (pause > 0) {
_stub->sleep(pause);
}
if (_stub->_pi.quit) {
return;
}
if (_stub->_pi.dbgMask & PlayerInput::DF_FASTMODE) {
return;
}
const int32_t delay = _stub->getTimeStamp() - _tstamp;
const int32_t pause = _frameDelay * TIMER_SLICE - delay;
if (pause > 0) {
_stub->sleep(pause);
}
_tstamp = _stub->getTimeStamp();
}
@ -90,7 +93,7 @@ uint16_t Cutscene::findTextSeparators(const uint8_t *p) {
uint8_t *q = _textSep;
uint16_t ret = 0;
uint16_t pos = 0;
for (; *p != 0xA; ++p) {
for (; *p != 0xA && *p; ++p) {
if (*p == 0x7C) {
*q++ = pos;
if (pos > ret) {
@ -111,6 +114,7 @@ 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, uint8_t n) {
debug(DBG_CUT, "Cutscene::drawText(x=%d, y=%d, c=%d)", x, y, color);
Video::drawCharFunc dcf = _vid->_drawChar;
uint16_t last_sep = 0;
if (n != 0) {
last_sep = findTextSeparators(p);
@ -126,7 +130,7 @@ void Cutscene::drawText(int16_t x, int16_t y, const uint8_t *p, uint16_t color,
if (n != 0) {
xx += ((last_sep - *sep++) & 0xFE) * 4;
}
for (; *p != 0xA; ++p) {
for (; *p != 0xA && *p; ++p) {
if (*p == 0x7C) {
yy += 8;
xx = x;
@ -135,25 +139,11 @@ void Cutscene::drawText(int16_t x, int16_t y, const uint8_t *p, uint16_t color,
}
} else if (*p == 0x20) {
xx += 8;
} else if (*p == 0x9) {
// ignore tab
} else {
uint8_t *dst_char = page + 256 * yy + xx;
const uint8_t *src = _res->_fnt + (*p - 32) * 32;
for (int h = 0; h < 8; ++h) {
for (int w = 0; w < 4; ++w) {
uint8_t c1 = (*src & 0xF0) >> 4;
uint8_t c2 = (*src & 0x0F) >> 0;
++src;
if (c1 != 0) {
*dst_char = (c1 == 0xF) ? color : (0xE0 + c1);
}
++dst_char;
if (c2 != 0) {
*dst_char = (c2 == 0xF) ? color : (0xE0 + c2);
}
++dst_char;
}
dst_char += 256 - 8;
}
uint8_t *dst = page + 256 * yy + xx;
(_vid->*dcf)(dst, 256, _res->_fnt, color, *p);
xx += 8;
}
}
@ -176,11 +166,10 @@ void Cutscene::drawCreditsText() {
return;
}
}
if (_creditsTextCounter <= 0) { // XXX
if (_creditsTextCounter <= 0) {
uint8_t code = *_textCurPtr;
if (code == 0xFF) {
_textBuf[0] = 0xA;
// _cut_status = 0;
} else if (code == 0xFE) {
++_textCurPtr;
code = *_textCurPtr++;
@ -198,10 +187,11 @@ void Cutscene::drawCreditsText() {
}
} else {
*_textCurBuf++ = code;
*_textCurBuf++ = 0xA;
*_textCurBuf = 0xA;
++_textCurPtr;
}
} else {
_creditsTextCounter -= 10; // XXX adjust
_creditsTextCounter -= 10;
}
drawText((_creditsTextPosX - 1) * 8, _creditsTextPosY * 8, _textBuf, 0xEF, _page1, 0);
}
@ -270,7 +260,7 @@ void Cutscene::op_waitForSync() {
_varText = 0xFF;
_frameDelay = 3;
if (_textBuf == _textCurBuf) {
_creditsTextCounter = 20;
_creditsTextCounter = _res->isAmiga() ? 60 : 20;
}
memcpy(_page1, _page0, _vid->_layerSize);
drawCreditsText();
@ -385,6 +375,16 @@ void Cutscene::op_drawStringAtBottom() {
debug(DBG_CUT, "Cutscene::op_drawStringAtBottom()");
uint16_t strId = fetchNextCmdWord();
if (!_creditsSequence) {
// 'espions' - ignore last call, allows caption to be displayed longer on the screen
if (_id == 0x39 && strId == 0xFFFF) {
if ((_res->isDOS() && (_cmdPtr - _cmdPtrBak) == 0x10) || (_res->isAmiga() && (_cmdPtr - _res->_cmd) == 0x9F3)) {
_frameDelay = 100;
setPalette();
return;
}
}
memset(_pageC + 179 * 256, 0xC0, 45 * 256);
memset(_page1 + 179 * 256, 0xC0, 45 * 256);
memset(_page0 + 179 * 256, 0xC0, 45 * 256);
@ -839,7 +839,7 @@ void Cutscene::op_drawStringAtPos() {
uint8_t color = 0xD0 + (strId >> 0xC);
drawText(x, y, str, color, _page1, 2);
}
// workaround for buggy cutscene script
// '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);
@ -964,13 +964,30 @@ void Cutscene::load(uint16_t cutName) {
name = "INTRO";
}
_res->load(name, Resource::OT_CMP);
if (_id == 0x39 && _res->_lang != LANG_FR) {
//
// 'espions' - '... the power which we need' caption is missing in Amiga English.
// fixed in DOS version, opcodes order is wrong
//
// opcode 0 pos 0x323
// opcode 6 pos 0x324
// str 0x3a
//
uint8_t *p = _res->_cmd + 0x322;
if (memcmp(p, "\x00\x18\x00\x3a", 4) == 0) {
p[0] = 0x06 << 2; // op_drawStringAtBottom
p[1] = 0x00;
p[2] = 0x3a;
p[3] = 0x00; // op_markCurPos
}
}
break;
case kResourceTypeDOS:
_res->load(name, Resource::OT_CMD);
_res->load(name, Resource::OT_POL);
_res->load_CINE();
break;
}
_res->load_CINE();
}
void Cutscene::prepare() {
@ -986,12 +1003,11 @@ void Cutscene::prepare() {
_gfx.setClippingRect(8, 50, 240, 128);
}
void Cutscene::startCredits() {
_textCurPtr = _creditsData;
void Cutscene::playCredits() {
_textCurPtr = _res->isAmiga() ? _creditsDataAmiga : _creditsDataDOS;
_textBuf[0] = 0xA;
_textCurBuf = _textBuf;
_creditsSequence = true;
// _cut_status = 1;
_varText = 0;
_textUnk2 = 0;
_creditsTextCounter = 0;
@ -1011,6 +1027,37 @@ void Cutscene::startCredits() {
_creditsSequence = false;
}
void Cutscene::playText(const char *str) {
Color c;
// background
c.r = c.g = c.b = 0;
_stub->setPaletteEntry(0xC0, &c);
// text
c.r = c.g = c.b = 255;
_stub->setPaletteEntry(0xC1, &c);
int lines = 0;
for (int i = 0; str[i]; ++i) {
if (str[i] == '|') {
++lines;
}
}
const int y = (128 - lines * 8) / 2;
memset(_page1, 0xC0, _vid->_layerSize);
drawText(0, y, (const uint8_t *)str, 0xC1, _page1, 1);
_stub->copyRect(0, 0, _vid->_w, _vid->_h, _page1, 256);
_stub->updateScreen(0);
while (!_stub->_pi.quit) {
_stub->processEvents();
if (_stub->_pi.backspace) {
_stub->_pi.backspace = false;
break;
}
_stub->sleep(TIMER_SLICE);
}
}
void Cutscene::play() {
if (_id != 0xFFFF) {
_textCurBuf = NULL;
@ -1044,7 +1091,14 @@ void Cutscene::play() {
}
}
}
if (cutName != 0xFFFF) {
if (g_options.use_text_cutscenes && _res->_lang == LANG_FR) {
for (int i = 0; _frTextsTable[i].str; ++i) {
if (_id == _frTextsTable[i].num) {
playText(_frTextsTable[i].str);
break;
}
}
} else if (cutName != 0xFFFF) {
load(cutName);
mainLoop(cutOff);
}

View File

@ -22,16 +22,23 @@ struct Cutscene {
TIMER_SLICE = 15
};
struct Text {
int num;
const char *str;
};
static const OpcodeStub _opcodeTable[];
static const char *_namesTable[];
static const uint16_t _offsetsTable[];
static const uint8_t _amigaDemoOffsetsTable[];
static const uint16_t _cosTable[];
static const uint16_t _sinTable[];
static const uint8_t _creditsData[];
static const uint8_t _creditsDataDOS[];
static const uint8_t _creditsDataAmiga[];
static const uint16_t _creditsCutSeq[];
static const uint8_t _musicTable[];
static const uint8_t _protectionShapeData[];
static const Text _frTextsTable[];
Graphics _gfx;
Resource *_res;
@ -118,7 +125,8 @@ struct Cutscene {
void mainLoop(uint16_t offset);
void load(uint16_t cutName);
void prepare();
void startCredits();
void playCredits();
void playText(const char *str);
void play();
};

View File

@ -24,9 +24,9 @@ struct File_impl {
virtual uint32_t write(void *ptr, uint32_t len) = 0;
};
struct stdFile : File_impl {
struct StdioFile : File_impl {
FILE *_fp;
stdFile() : _fp(0) {}
StdioFile() : _fp(0) {}
bool open(const char *path, const char *mode) {
_ioErr = false;
_fp = fopen(path, mode);
@ -76,9 +76,9 @@ struct stdFile : File_impl {
};
#ifdef USE_ZLIB
struct zlibFile : File_impl {
struct GzipFile : File_impl {
gzFile _fp;
zlibFile() : _fp(0) {}
GzipFile() : _fp(0) {}
bool open(const char *path, const char *mode) {
_ioErr = false;
_fp = gzopen(path, mode);
@ -147,7 +147,7 @@ bool File::open(const char *filename, const char *mode, FileSystem *fs) {
_impl = 0;
}
assert(mode[0] != 'z');
_impl = new stdFile;
_impl = new StdioFile;
char *path = fs->findPath(filename);
if (path) {
debug(DBG_FILE, "Open file name '%s' mode '%s' path '%s'", filename, mode, path);
@ -166,12 +166,12 @@ bool File::open(const char *filename, const char *mode, const char *directory) {
}
#ifdef USE_ZLIB
if (mode[0] == 'z') {
_impl = new zlibFile;
_impl = new GzipFile;
++mode;
}
#endif
if (!_impl) {
_impl = new stdFile;
_impl = new StdioFile;
}
char path[MAXPATHLEN];
snprintf(path, sizeof(path), "%s/%s", directory, filename);

View File

@ -69,21 +69,26 @@ void Game::run() {
break;
}
if (_res.isAmiga()) {
displayTitleScreenAmiga();
_stub->setScreenSize(Video::GAMESCREEN_W, Video::GAMESCREEN_H);
}
while (!_stub->_pi.quit) {
if (_res.isDOS()) {
switch (_res._type) {
case kResourceTypeDOS:
_mix.playMusic(1);
_menu.handleTitleScreen();
if (_menu._selectedOption == Menu::MENU_OPTION_ITEM_QUIT) {
if (_menu._selectedOption == Menu::MENU_OPTION_ITEM_QUIT || _stub->_pi.quit) {
_stub->_pi.quit = true;
break;
}
_skillLevel = _menu._skill;
_currentLevel = _menu._level;
_mix.stopMusic();
break;
case kResourceTypeAmiga:
displayTitleScreenAmiga();
_stub->setScreenSize(Video::GAMESCREEN_W, Video::GAMESCREEN_H);
break;
}
if (_stub->_pi.quit) {
break;
}
if (_currentLevel == 7) {
_vid.fadeOut();
@ -328,7 +333,7 @@ void Game::playCutscene(int id) {
_cut.play();
}
if (id == 0x3D) {
_cut.startCredits();
_cut.playCredits();
}
_mix.stopMusic();
}

View File

@ -46,6 +46,26 @@ 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) {
a = -32768;
} else if (a > 32767) {
a = 32767;
}
return a;
}
template<typename T>
inline void SWAP(T &a, T &b) {
T tmp = a;
@ -72,6 +92,7 @@ struct Options {
bool enable_password_menu;
bool fade_out_palette;
bool use_tiledata;
bool use_text_cutscenes;
};
struct Color {

View File

@ -19,9 +19,10 @@ static const char *USAGE =
"Usage: %s [OPTIONS]...\n"
" --datapath=PATH Path to data files (default 'DATA')\n"
" --savepath=PATH Path to save files (default '.')\n"
" --levelnum=NUM Start level (default '0')\n"
" --fullscreen Start fullscreen\n"
" --levelnum=NUM Level to start from (default '0')\n"
" --fullscreen Fullscreen display\n"
" --scaler=INDEX Graphics scaler\n"
" --language=LANG Language (fr,en,de,sp,it)\n"
;
static int detectVersion(FileSystem *fs) {
@ -80,6 +81,7 @@ static void initOptions() {
g_options.play_disabled_cutscenes = false;
g_options.enable_password_menu = false;
g_options.fade_out_palette = true;
g_options.use_text_cutscenes = false;
// read configuration file
struct {
const char *name;
@ -90,6 +92,7 @@ static void initOptions() {
{ "enable_password_menu", &g_options.enable_password_menu },
{ "fade_out_palette", &g_options.fade_out_palette },
{ "use_tiledata", &g_options.use_tiledata },
{ "use_text_cutscenes", &g_options.use_text_cutscenes },
{ 0, 0 }
};
static const char *filename = "rs.cfg";
@ -130,6 +133,7 @@ int main(int argc, char *argv[]) {
int levelNum = 0;
int scaler = DEFAULT_SCALER;
bool fullscreen = false;
int forcedLanguage = -1;
if (argc == 2) {
// data path as the only command line argument
struct stat st;
@ -144,6 +148,7 @@ int main(int argc, char *argv[]) {
{ "levelnum", required_argument, 0, 3 },
{ "fullscreen", no_argument, 0, 4 },
{ "scaler", required_argument, 0, 5 },
{ "language", required_argument, 0, 6 },
{ 0, 0, 0, 0 }
};
int index;
@ -170,6 +175,26 @@ int main(int argc, char *argv[]) {
scaler = DEFAULT_SCALER;
}
break;
case 6: {
static const struct {
int lang;
const char *str;
} languages[] = {
{ LANG_FR, "FR" },
{ LANG_EN, "EN" },
{ LANG_DE, "DE" },
{ LANG_SP, "SP" },
{ LANG_IT, "IT" },
{ -1, 0 }
};
for (int i = 0; languages[i].str; ++i) {
if (strcasecmp(languages[i].str, optarg) == 0) {
forcedLanguage = languages[i].lang;
break;
}
}
}
break;
default:
printf(USAGE, argv[0]);
return 0;
@ -183,7 +208,7 @@ int main(int argc, char *argv[]) {
error("Unable to find data files, check that all required files are present");
return -1;
}
Language language = detectLanguage(&fs);
const Language language = (forcedLanguage == -1) ? detectLanguage(&fs) : (Language)forcedLanguage;
SystemStub *stub = SystemStub_SDL_create();
Game *g = new Game(stub, &fs, savePath, levelNum, (ResourceType)version, language);
stub->init(g_caption, Video::GAMESCREEN_W, Video::GAMESCREEN_H, scaler, fullscreen);

View File

@ -101,12 +101,19 @@ void Mixer::playMusic(int num) {
return;
}
}
if (_musicType == MT_OGG && isMusicSfx(num)) { // do not play level action music with background music
return;
}
if (isMusicSfx(num)) { // level action sequence
_sfx.play(num);
_musicType = MT_SFX;
if (_sfx._playing) {
_musicType = MT_SFX;
}
} else { // cutscene
_mod.play(num);
_musicType = MT_MOD;
if (_mod._playing) {
_musicType = MT_MOD;
}
}
}
@ -119,8 +126,7 @@ void Mixer::stopMusic() {
_mod.stop();
break;
case MT_OGG:
_ogg.stopTrack();
_musicTrack = -1;
_ogg.pauseTrack();
break;
case MT_SFX:
_sfx.stop();
@ -133,20 +139,9 @@ void Mixer::stopMusic() {
}
}
static void nr(const int8_t *in, int len, int8_t *out) {
static int prev = 0;
for (int i = 0; i < len; ++i) {
const int vnr = in[i] >> 1;
out[i] = vnr + prev;
prev = vnr;
}
}
void Mixer::mix(int8_t *out, int len) {
int8_t buf[len];
memset(buf, 0, len);
void Mixer::mix(int16_t *out, int len) {
if (_premixHook) {
if (!_premixHook(_premixHookData, buf, len)) {
if (!_premixHook(_premixHookData, out, len)) {
_premixHook = 0;
_premixHookData = 0;
}
@ -160,24 +155,13 @@ void Mixer::mix(int8_t *out, int len) {
break;
}
const int sample = ch->chunk.getPCM(ch->chunkPos >> FRAC_BITS);
addclamp(buf[pos], sample * ch->volume / Mixer::MAX_VOLUME);
out[pos] = ADDC_S16(out[pos], (sample * ch->volume / Mixer::MAX_VOLUME) << 8);
ch->chunkPos += ch->chunkInc;
}
}
}
nr(buf, len, out);
}
void Mixer::addclamp(int8_t& a, int b) {
int add = a + b;
if (add < -128) {
add = -128;
} else if (add > 127) {
add = 127;
}
a = add;
}
void Mixer::mixCallback(void *param, int8_t *buf, int len) {
void Mixer::mixCallback(void *param, int16_t *buf, int len) {
((Mixer *)param)->mix(buf, len);
}

View File

@ -42,7 +42,7 @@ struct FileSystem;
struct SystemStub;
struct Mixer {
typedef bool (*PremixHook)(void *userData, int8_t *buf, int len);
typedef bool (*PremixHook)(void *userData, int16_t *buf, int len);
enum MusicType {
MT_NONE,
@ -79,10 +79,9 @@ struct Mixer {
void stopAll();
void playMusic(int num);
void stopMusic();
void mix(int8_t *buf, int len);
void mix(int16_t *buf, int len);
static void addclamp(int8_t &a, int b);
static void mixCallback(void *param, int8_t *buf, int len);
static void mixCallback(void *param, int16_t *buf, int len);
};
#endif // MIXER_H__

View File

@ -27,7 +27,7 @@ struct ModPlayer_impl {
ModPlug_GetSettings(&_settings);
_settings.mFlags = MODPLUG_ENABLE_OVERSAMPLING | MODPLUG_ENABLE_NOISE_REDUCTION;
_settings.mChannels = 1;
_settings.mBits = 8;
_settings.mBits = 16;
_settings.mFrequency = rate;
_settings.mResamplingMode = MODPLUG_RESAMPLE_FIR;
ModPlug_SetSettings(&_settings);
@ -50,18 +50,14 @@ struct ModPlayer_impl {
}
}
bool mix(int8_t *buf, int len) {
memset(buf, 0, len);
bool mix(int16_t *buf, int len) {
if (_mf) {
const int order = ModPlug_GetCurrentOrder(_mf);
if (order == 3 && _repeatIntro) {
ModPlug_SeekOrder(_mf, 1);
_repeatIntro = false;
}
const int count = ModPlug_Read(_mf, buf, len);
for (int i = 0; i < count; ++i) {
buf[i] ^= 0x80;
}
const int count = ModPlug_Read(_mf, buf, len * sizeof(int16_t));
return count > 0;
}
return false;
@ -152,7 +148,8 @@ struct ModPlayer_impl {
void applyPortamento(int trackNum);
void handleEffect(int trackNum, bool tick);
void mixSamples(int8_t *buf, int len);
bool mix(int8_t *buf, int len);
bool mixS8(int8_t *buf, int len);
bool mix(int16_t *buf, int len);
};
ModPlayer_impl::ModPlayer_impl()
@ -607,7 +604,8 @@ void ModPlayer_impl::mixSamples(int8_t *buf, int samplesLen) {
}
while (count--) {
const int out = si->getPCM(pos >> FRAC_BITS);
Mixer::addclamp(*mixbuf++, out * tk->volume / 64);
*mixbuf = ADDC_S8(*mixbuf, out * tk->volume / 64);
++mixbuf;
pos += deltaPos;
}
}
@ -616,9 +614,8 @@ void ModPlayer_impl::mixSamples(int8_t *buf, int samplesLen) {
}
}
bool ModPlayer_impl::mix(int8_t *buf, int len) {
bool ModPlayer_impl::mixS8(int8_t *buf, int len) {
if (_playing) {
memset(buf, 0, len);
const int samplesPerTick = _mixingRate / (50 * _songTempo / 125);
while (len != 0) {
if (_samplesLeft == 0) {
@ -637,6 +634,16 @@ bool ModPlayer_impl::mix(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)
@ -673,6 +680,6 @@ void ModPlayer::stop() {
}
}
bool ModPlayer::mixCallback(void *param, int8_t *buf, int len) {
bool ModPlayer::mixCallback(void *param, int16_t *buf, int len) {
return ((ModPlayer_impl *)param)->mix(buf, len);
}

View File

@ -31,7 +31,7 @@ struct ModPlayer {
void play(int num);
void stop();
static bool mixCallback(void *param, int8_t *buf, int len);
static bool mixCallback(void *param, int16_t *buf, int len);
};
#endif // MOD_PLAYER_H__

View File

@ -10,6 +10,7 @@
#include "file.h"
#include "mixer.h"
#include "ogg_player.h"
#include "util.h"
#ifdef USE_TREMOR
struct VorbisFile: File {
@ -85,7 +86,7 @@ struct OggDecoder_impl {
_channels = vi->channels;
return true;
}
int read(int8_t *dst, int samples) {
int read(int16_t *dst, int samples) {
int size = samples * _channels * sizeof(int16_t);
if (size > _readBufSize) {
_readBufSize = size;
@ -100,22 +101,26 @@ struct OggDecoder_impl {
const int len = ov_read(&_ovf, (char *)_readBuf, size, 0);
if (len < 0) {
// error in decoder
return 0;
return count;
} else if (len == 0) {
// loop
ov_raw_seek(&_ovf, 0);
continue;
}
assert((len & 1) == 0);
switch (_channels) {
case 2:
assert((len & 1) == 0);
assert((len & 3) == 0);
for (int i = 0; i < len / 2; i += 2) {
Mixer::addclamp(*dst++, ((_readBuf[i] + _readBuf[i + 1]) / 2) >> 8);
const int16_t s16 = (_readBuf[i] + _readBuf[i + 1]) / 2;
*dst = ADDC_S16(*dst, s16);
++dst;
}
break;
case 1:
for (int i = 0; i < len / 2; ++i) {
Mixer::addclamp(*dst++, _readBuf[i] >> 8);
*dst = ADDC_S16(*dst, _readBuf[i]);
++dst;
}
break;
}
@ -188,7 +193,7 @@ void OggPlayer::resumeTrack() {
#endif
}
bool OggPlayer::mix(int8_t *buf, int len) {
bool OggPlayer::mix(int16_t *buf, int len) {
#ifdef USE_TREMOR
if (_impl) {
return _impl->read(buf, len) != 0;
@ -197,7 +202,7 @@ bool OggPlayer::mix(int8_t *buf, int len) {
return false;
}
bool OggPlayer::mixCallback(void *param, int8_t *buf, int len) {
bool OggPlayer::mixCallback(void *param, int16_t *buf, int len) {
return ((OggPlayer *)param)->mix(buf, len);
}

View File

@ -22,8 +22,8 @@ struct OggPlayer {
void pauseTrack();
void resumeTrack();
bool isPlaying() const { return _impl != 0; }
bool mix(int8_t *buf, int len);
static bool mixCallback(void *param, int8_t *buf, int len);
bool mix(int16_t *buf, int len);
static bool mixCallback(void *param, int16_t *buf, int len);
Mixer *_mix;
FileSystem *_fs;

View File

@ -269,28 +269,65 @@ void Resource::load_SPR_OFF(const char *fileName, uint8_t *sprData) {
error("Cannot load '%s'", _entryName);
}
void Resource::load_CINE() {
const char *baseName = 0;
switch (_lang) {
static const char *getCineName(Language lang, ResourceType type) {
switch (lang) {
case LANG_FR:
baseName = "FR_CINE";
break;
case LANG_EN:
baseName = "ENGCINE";
break;
if (type == kResourceTypeAmiga) {
return "FR";
}
return "FR_";
case LANG_DE:
baseName = "GERCINE";
break;
return "GER";
case LANG_SP:
baseName = "SPACINE";
break;
return "SPA";
case LANG_IT:
baseName = "ITACINE";
break;
return "ITA";
case LANG_EN:
default:
return "ENG";
}
}
void Resource::load_CINE() {
const char *prefix = getCineName(_lang, _type);
debug(DBG_RES, "Resource::load_CINE('%s')", prefix);
if (_type == kResourceTypeAmiga) {
if (_isDemo) {
// file not present in demo data files
return;
}
if (_cine_txt == 0) {
snprintf(_entryName, sizeof(_entryName), "%sCINE.TXT", prefix);
File f;
if (f.open(_entryName, "rb", _fs)) {
const int len = f.size();
_cine_txt = (uint8_t *)malloc(len + 1);
if (!_cine_txt) {
error("Unable to allocate cinematics text data");
}
f.read(_cine_txt, len);
if (f.ioErr()) {
error("I/O error when reading '%s'", _entryName);
}
_cine_txt[len] = 0;
uint8_t *p = _cine_txt;
for (int i = 0; i < NUM_CUTSCENE_TEXTS; ++i) {
_cineStrings[i] = p;
uint8_t *sep = (uint8_t *)memchr(p, '\n', &_cine_txt[len] - p);
if (!sep) {
break;
}
p = sep + 1;
}
}
if (!_cine_txt) {
error("Cannot load '%s'", _entryName);
}
}
return;
}
debug(DBG_RES, "Resource::load_CINE('%s')", baseName);
if (_cine_off == 0) {
snprintf(_entryName, sizeof(_entryName), "%s.BIN", baseName);
snprintf(_entryName, sizeof(_entryName), "%sCINE.BIN", prefix);
File f;
if (f.open(_entryName, "rb", _fs)) {
int len = f.size();
@ -310,7 +347,7 @@ void Resource::load_CINE() {
}
}
if (_cine_txt == 0) {
snprintf(_entryName, sizeof(_entryName), "%s.TXT", baseName);
snprintf(_entryName, sizeof(_entryName), "%sCINE.TXT", prefix);
File f;
if (f.open(_entryName, "rb", _fs)) {
int len = f.size();

View File

@ -90,6 +90,7 @@ struct Resource {
enum {
NUM_SFXS = 66,
NUM_BANK_BUFFERS = 50,
NUM_CUTSCENE_TEXTS = 117,
NUM_SPRITES = 1287
};
@ -135,6 +136,7 @@ struct Resource {
uint8_t _numSfx;
uint8_t *_cmd;
uint8_t *_pol;
uint8_t *_cineStrings[NUM_CUTSCENE_TEXTS];
uint8_t *_cine_off;
uint8_t *_cine_txt;
char **_extTextsTable;
@ -209,7 +211,7 @@ struct Resource {
const int offset = READ_BE_UINT16(_cine_off + num * 2);
return _cine_txt + offset;
}
return 0;
return (num >= 0 && num < NUM_CUTSCENE_TEXTS) ? _cineStrings[num] : 0;
}
const char *getMenuString(int num) {
return (num >= 0 && num < LocaleData::LI_NUM) ? _textsTable[num] : "";

3
rs.cfg
View File

@ -12,3 +12,6 @@ fade_out_palette=true
# use .BNQ & .LEV datafiles (tile based rendering) for backgrounds (instead of .MAP)
use_tiledata=false
# display text instead of playing the polygon cutscenes (french only)
use_text_cutscenes=false

View File

@ -102,10 +102,10 @@ void SeqDemuxer::readPalette(uint8_t *dst) {
_f->read(dst, 256 * 3);
}
void SeqDemuxer::readAudioS8(uint8_t *dst) {
void SeqDemuxer::readAudio(int16_t *dst) {
_f->seek(_frameOffset + _audioDataOffset);
for (int i = 0; i < kAudioBufferSize; ++i) {
dst[i] = _f->readUint16BE() >> 8;
dst[i] = _f->readUint16BE();
}
}
@ -246,9 +246,9 @@ void SeqPlayer::play(File *f) {
if (_demux._audioDataSize != 0) {
SoundBufferQueue *sbq = (SoundBufferQueue *)malloc(sizeof(SoundBufferQueue));
if (sbq) {
sbq->data = (uint8_t *)malloc(SeqDemuxer::kAudioBufferSize);
sbq->data = (int16_t *)calloc(SeqDemuxer::kAudioBufferSize, sizeof(int16_t));
if (sbq->data) {
_demux.readAudioS8(sbq->data);
_demux.readAudio(sbq->data);
sbq->size = SeqDemuxer::kAudioBufferSize;
sbq->read = 0;
sbq->next = 0;
@ -332,7 +332,7 @@ void SeqPlayer::play(File *f) {
}
}
bool SeqPlayer::mix(int8_t *buf, int samples) {
bool SeqPlayer::mix(int16_t *buf, int samples) {
if (_soundQueuePreloadSize < kSoundPreloadSize) {
return true;
}
@ -350,7 +350,7 @@ bool SeqPlayer::mix(int8_t *buf, int samples) {
return true;
}
bool SeqPlayer::mixCallback(void *param, int8_t *buf, int len) {
bool SeqPlayer::mixCallback(void *param, int16_t *buf, int len) {
return ((SeqPlayer *)param)->mix(buf, len);
}

View File

@ -28,7 +28,7 @@ struct SeqDemuxer {
void fillBuffer(int num, int offset, int size);
void clearBuffer(int num);
void readPalette(uint8_t *dst);
void readAudioS8(uint8_t *dst);
void readAudio(int16_t *dst);
int _frameOffset;
int _audioDataOffset;
@ -55,7 +55,7 @@ struct SeqPlayer {
static const char *_namesTable[];
struct SoundBufferQueue {
uint8_t *data;
int16_t *data;
int size;
int read;
SoundBufferQueue *next;
@ -66,8 +66,8 @@ struct SeqPlayer {
void setBackBuffer(uint8_t *buf) { _buf = buf; }
void play(File *f);
bool mix(int8_t *buf, int len);
static bool mixCallback(void *param, int8_t *buf, int len);
bool mix(int16_t *buf, int len);
static bool mixCallback(void *param, int16_t *buf, int len);
SystemStub *_stub;
uint8_t *_buf;

View File

@ -127,7 +127,8 @@ void SfxPlayer::mixSamples(int8_t *buf, int samplesLen) {
}
while (count--) {
const int out = si->getPCM(pos >> FRAC_BITS);
Mixer::addclamp(*mixbuf++, out * si->vol / 64);
*mixbuf = ADDC_S8(*mixbuf, out * si->vol / 64);
++mixbuf;
pos += deltaPos;
}
}
@ -138,7 +139,6 @@ void SfxPlayer::mixSamples(int8_t *buf, int samplesLen) {
bool SfxPlayer::mix(int8_t *buf, int len) {
if (_playing) {
memset(buf, 0, len);
const int samplesPerTick = _mix->getSampleRate() / 50;
while (len != 0) {
if (_samplesLeft == 0) {
@ -158,6 +158,12 @@ bool SfxPlayer::mix(int8_t *buf, int len) {
return _playing;
}
bool SfxPlayer::mixCallback(void *param, int8_t *buf, int len) {
return ((SfxPlayer *)param)->mix(buf, 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;
}

View File

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

View File

@ -188,7 +188,7 @@ const uint16_t Cutscene::_sinTable[] = {
0xFFDD, 0xFFE1, 0xFFE6, 0xFFEA, 0xFFEF, 0xFFF3, 0xFFF8, 0xFFFC
};
const uint8_t Cutscene::_creditsData[] = {
const uint8_t Cutscene::_creditsDataDOS[] = {
0xFE, 0x14, 0x00, 0x01, 0x00, 0x04, 0x20, 0x20, 0x54, 0x68, 0x65, 0x20, 0x46, 0x6C, 0x61, 0x73,
0x68, 0x42, 0x61, 0x63, 0x6B, 0x20, 0x54, 0x65, 0x61, 0x6D, 0x20, 0x69, 0x73, 0x2E, 0x2E, 0x2E,
0xFE, 0x32, 0xFE, 0x14, 0x00, 0x01, 0x00, 0x07, 0x00, 0x01, 0x00, 0x05, 0x20, 0x20, 0x49, 0x6E,
@ -356,6 +356,164 @@ const uint8_t Cutscene::_creditsData[] = {
0x20, 0x20, 0x20, 0x20, 0x20, 0x7C, 0xFE, 0x50, 0xFE, 0xFF, 0x00, 0xFF
};
const uint8_t Cutscene::_creditsDataAmiga[] = {
0xFE, 0x14, 0x00, 0x01, 0x00, 0x08, 0x20, 0x20, 0x20, 0x20, 0x20, 0x54, 0x68, 0x65, 0x20, 0x46,
0x6C, 0x61, 0x73, 0x68, 0x42, 0x61, 0x63, 0x6B, 0x20, 0x54, 0x65, 0x61, 0x6D, 0x20, 0x69, 0x73,
0x2E, 0x2E, 0x2E, 0xFE, 0x32, 0x00, 0x01, 0x00, 0x08, 0x20, 0x20, 0x41, 0x6D, 0x69, 0x67, 0x61,
0x20, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6F, 0x6E, 0x3A, 0x7C, 0xFE, 0x14, 0x7C, 0x7C, 0xFE, 0x14,
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x42,
0x65, 0x6E, 0x6F, 0x69, 0x73, 0x74, 0x20, 0x41, 0x72, 0x6F, 0x6E, 0x20, 0x20, 0x20, 0x20, 0x7C,
0xFE, 0x14, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
0x20, 0x54, 0x68, 0x69, 0x65, 0x72, 0x72, 0x79, 0x20, 0x4C, 0x65, 0x76, 0x61, 0x73, 0x74, 0x72,
0x65, 0x20, 0x7C, 0xFE, 0x14, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
0x20, 0x20, 0x20, 0x20, 0x54, 0x68, 0x69, 0x65, 0x72, 0x72, 0x79, 0x20, 0x50, 0x65, 0x72, 0x72,
0x65, 0x61, 0x75, 0x20, 0x20, 0x7C, 0xFE, 0x14, 0xFE, 0x14, 0x00, 0x01, 0x00, 0x07, 0x00, 0x01,
0x00, 0x09, 0x20, 0x20, 0x50, 0x72, 0x6F, 0x67, 0x72, 0x61, 0x6D, 0x6D, 0x65, 0x72, 0x73, 0x3A,
0x7C, 0x7C, 0xFE, 0x14, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
0x20, 0x20, 0x20, 0x20, 0x42, 0x65, 0x6E, 0x6F, 0x69, 0x73, 0x74, 0x20, 0x41, 0x72, 0x6F, 0x6E,
0x20, 0x20, 0x20, 0x20, 0x7C, 0xFE, 0x14, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x50, 0x68, 0x69, 0x6C, 0x69, 0x70, 0x70, 0x65, 0x20, 0x43,
0x68, 0x61, 0x73, 0x74, 0x65, 0x6C, 0x20, 0x7C, 0xFE, 0x14, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x50, 0x61, 0x75,
0x6C, 0x20, 0x43, 0x75, 0x69, 0x73, 0x73, 0x65, 0x74, 0x20, 0x7C, 0xFE, 0x14, 0x20, 0x20, 0x20,
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x46, 0x72, 0x65, 0x64,
0x65, 0x72, 0x69, 0x63, 0x20, 0x53, 0x61, 0x76, 0x6F, 0x69, 0x72, 0x20, 0x20, 0x7C, 0xFE, 0x14,
0xFE, 0x14, 0x00, 0x01, 0x00, 0x07, 0x20, 0x20, 0x47, 0x72, 0x61, 0x70, 0x68, 0x69, 0x63, 0x20,
0x41, 0x72, 0x74, 0x69, 0x73, 0x74, 0x73, 0x3A, 0x7C, 0x7C, 0xFE, 0x14, 0x20, 0x20, 0x20, 0x20,
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x50, 0x61, 0x74, 0x72, 0x69,
0x63, 0x6B, 0x20, 0x44, 0x61, 0x68, 0x65, 0x72, 0x20, 0x20, 0x20, 0x20, 0x7C, 0xFE, 0x14, 0x20,
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x54, 0x68,
0x69, 0x65, 0x72, 0x72, 0x79, 0x20, 0x4C, 0x65, 0x76, 0x61, 0x73, 0x74, 0x72, 0x65, 0x20, 0x7C,
0xFE, 0x14, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
0x20, 0x20, 0x20, 0x44, 0x65, 0x6E, 0x69, 0x73, 0x20, 0x4D, 0x65, 0x72, 0x63, 0x69, 0x65, 0x72,
0x20, 0x20, 0x7C, 0xFE, 0x14, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
0x20, 0x20, 0x20, 0x20, 0x54, 0x68, 0x69, 0x65, 0x72, 0x72, 0x79, 0x20, 0x50, 0x65, 0x72, 0x72,
0x65, 0x61, 0x75, 0x20, 0x20, 0x7C, 0xFE, 0x14, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
0x20, 0x20, 0x20, 0x20, 0x20, 0x43, 0x68, 0x72, 0x69, 0x73, 0x74, 0x69, 0x61, 0x6E, 0x20, 0x52,
0x6F, 0x62, 0x65, 0x72, 0x74, 0x20, 0x20, 0x20, 0x7C, 0xFE, 0x14, 0x20, 0x20, 0x20, 0x20, 0x20,
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x46, 0x61, 0x62, 0x72, 0x69, 0x63,
0x65, 0x20, 0x56, 0x69, 0x73, 0x73, 0x65, 0x72, 0x6F, 0x74, 0x20, 0x7C, 0xFE, 0x14, 0xFE, 0x14,
0x00, 0x01, 0x00, 0x07, 0x20, 0x20, 0x48, 0x61, 0x72, 0x64, 0x77, 0x61, 0x72, 0x65, 0x20, 0x45,
0x6E, 0x67, 0x69, 0x6E, 0x65, 0x65, 0x72, 0x3A, 0x7C, 0x7C, 0xFE, 0x14, 0x20, 0x20, 0x20, 0x20,
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x54, 0x68, 0x69, 0x65, 0x72, 0x72,
0x79, 0x20, 0x47, 0x61, 0x65, 0x72, 0x74, 0x68, 0x6E, 0x65, 0x72, 0x20, 0x7C, 0xFE, 0x14, 0x00,
0x01, 0x00, 0x07, 0x20, 0x20, 0x53, 0x74, 0x6F, 0x72, 0x79, 0x3A, 0x7C, 0x7C, 0xFE, 0x14, 0x20,
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
0x20, 0x50, 0x61, 0x75, 0x6C, 0x20, 0x43, 0x75, 0x69, 0x73, 0x73, 0x65, 0x74, 0x20, 0x20, 0x7C,
0xFE, 0x14, 0x00, 0x01, 0x00, 0x08, 0x20, 0x20, 0x4C, 0x65, 0x76, 0x65, 0x6C, 0x20, 0x44, 0x65,
0x73, 0x69, 0x67, 0x6E, 0x3A, 0x7C, 0x7C, 0xFE, 0x14, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x50, 0x61, 0x75, 0x6C, 0x20,
0x43, 0x75, 0x69, 0x73, 0x73, 0x65, 0x74, 0x20, 0x20, 0x7C, 0xFE, 0x14, 0x20, 0x20, 0x20, 0x20,
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x50, 0x61, 0x74, 0x72, 0x69,
0x63, 0x6B, 0x20, 0x44, 0x61, 0x68, 0x65, 0x72, 0x20, 0x20, 0x20, 0x20, 0x7C, 0xFE, 0x14, 0x20,
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
0x44, 0x65, 0x6E, 0x69, 0x73, 0x20, 0x4D, 0x65, 0x72, 0x63, 0x69, 0x65, 0x72, 0x20, 0x20, 0x7C,
0xFE, 0x14, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
0x46, 0x72, 0x65, 0x64, 0x65, 0x72, 0x69, 0x63, 0x20, 0x53, 0x61, 0x76, 0x6F, 0x69, 0x72, 0x20,
0x20, 0x20, 0x7C, 0xFE, 0x14, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
0x20, 0x20, 0x20, 0x20, 0x46, 0x61, 0x62, 0x72, 0x69, 0x63, 0x65, 0x20, 0x56, 0x69, 0x73, 0x73,
0x65, 0x72, 0x6F, 0x74, 0x20, 0x7C, 0xFE, 0x14, 0xFE, 0x14, 0x00, 0x01, 0x00, 0x08, 0x20, 0x20,
0x4D, 0x75, 0x73, 0x69, 0x63, 0x3A, 0x7C, 0x7C, 0xFE, 0x14, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x4A, 0x65, 0x61, 0x6E,
0x20, 0x42, 0x61, 0x75, 0x64, 0x6C, 0x6F, 0x74, 0x20, 0x20, 0x7C, 0xFE, 0x14, 0x20, 0x20, 0x20,
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x52, 0x61, 0x70, 0x68,
0x61, 0x65, 0x6C, 0x20, 0x47, 0x65, 0x73, 0x71, 0x75, 0x61, 0x20, 0x20, 0x20, 0x7C, 0xFE, 0x14,
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x46,
0x61, 0x62, 0x72, 0x69, 0x63, 0x65, 0x20, 0x56, 0x69, 0x73, 0x73, 0x65, 0x72, 0x6F, 0x74, 0x20,
0x7C, 0xFE, 0x14, 0xFE, 0x14, 0x00, 0x01, 0x00, 0x08, 0x20, 0x20, 0x53, 0x6F, 0x75, 0x6E, 0x64,
0x20, 0x66, 0x78, 0x3A, 0x7C, 0x7C, 0xFE, 0x14, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x42, 0x65, 0x6E, 0x6F, 0x69, 0x73, 0x74, 0x20, 0x41,
0x72, 0x6F, 0x6E, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7C, 0xFE, 0x14, 0x20, 0x20, 0x20, 0x20, 0x20,
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x50, 0x68, 0x69, 0x6C, 0x69, 0x70, 0x70,
0x65, 0x20, 0x43, 0x68, 0x61, 0x73, 0x74, 0x65, 0x6C, 0x20, 0x20, 0x7C, 0xFE, 0x14, 0x20, 0x20,
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
0x50, 0x61, 0x75, 0x6C, 0x20, 0x43, 0x75, 0x69, 0x73, 0x73, 0x65, 0x74, 0x20, 0x20, 0x7C, 0xFE,
0x14, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
0x46, 0x61, 0x62, 0x72, 0x69, 0x63, 0x65, 0x20, 0x56, 0x69, 0x73, 0x73, 0x65, 0x72, 0x6F, 0x74,
0x20, 0x7C, 0xFE, 0x14, 0xFE, 0x14, 0x00, 0x01, 0x00, 0x06, 0x20, 0x20, 0x41, 0x63, 0x74, 0x6F,
0x72, 0x73, 0x3A, 0x7C, 0x7C, 0xFE, 0x14, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x42, 0x65, 0x6E, 0x6F, 0x69, 0x73, 0x74, 0x20, 0x41, 0x72,
0x6F, 0x6E, 0x20, 0x20, 0x20, 0x20, 0x7C, 0xFE, 0x14, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x50, 0x61, 0x74, 0x72, 0x69, 0x63, 0x6B, 0x20,
0x44, 0x61, 0x68, 0x65, 0x72, 0x20, 0x20, 0x20, 0x20, 0x7C, 0xFE, 0x14, 0x20, 0x20, 0x20, 0x20,
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x54, 0x68, 0x69, 0x65, 0x72,
0x72, 0x79, 0x20, 0x4C, 0x65, 0x76, 0x61, 0x73, 0x74, 0x72, 0x65, 0x20, 0x7C, 0xFE, 0x14, 0x20,
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
0x44, 0x65, 0x6E, 0x69, 0x73, 0x20, 0x4D, 0x65, 0x72, 0x63, 0x69, 0x65, 0x72, 0x20, 0x20, 0x7C,
0xFE, 0x14, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
0x20, 0x54, 0x68, 0x69, 0x65, 0x72, 0x72, 0x79, 0x20, 0x50, 0x65, 0x72, 0x72, 0x65, 0x61, 0x75,
0x20, 0x20, 0x7C, 0xFE, 0x14, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
0x20, 0x20, 0x43, 0x68, 0x72, 0x69, 0x73, 0x74, 0x69, 0x61, 0x6E, 0x20, 0x52, 0x6F, 0x62, 0x65,
0x72, 0x74, 0x20, 0x20, 0x20, 0x7C, 0xFE, 0x14, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x46, 0x61, 0x62, 0x72, 0x69, 0x63, 0x65, 0x20, 0x56,
0x69, 0x73, 0x73, 0x65, 0x72, 0x6F, 0x74, 0x20, 0x7C, 0xFE, 0x14, 0xFE, 0x14, 0x00, 0x01, 0x00,
0x0A, 0x20, 0x20, 0x56, 0x69, 0x64, 0x65, 0x6F, 0x20, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x6F,
0x72, 0x73, 0x3A, 0x7C, 0x7C, 0xFE, 0x14, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
0x20, 0x20, 0x20, 0x09, 0x20, 0x20, 0x20, 0x50, 0x61, 0x74, 0x72, 0x69, 0x63, 0x6B, 0x20, 0x44,
0x61, 0x68, 0x65, 0x72, 0x20, 0x20, 0x20, 0x20, 0x7C, 0xFE, 0x14, 0x20, 0x20, 0x20, 0x20, 0x20,
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x54, 0x68, 0x69, 0x65, 0x72, 0x72,
0x79, 0x20, 0x50, 0x65, 0x72, 0x72, 0x65, 0x61, 0x75, 0x20, 0x20, 0x7C, 0xFE, 0x14, 0x20, 0x20,
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x46, 0x61, 0x62,
0x72, 0x69, 0x63, 0x65, 0x20, 0x56, 0x69, 0x73, 0x73, 0x65, 0x72, 0x6F, 0x74, 0x20, 0x7C, 0xFE,
0x14, 0xFE, 0x14, 0x00, 0x01, 0x00, 0x0A, 0x20, 0x20, 0x56, 0x69, 0x64, 0x65, 0x6F, 0x20, 0x43,
0x6F, 0x2D, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x6F, 0x72, 0x73, 0x3A, 0x7C, 0x7C, 0xFE, 0x14,
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x54,
0x68, 0x69, 0x65, 0x72, 0x72, 0x79, 0x20, 0x4C, 0x65, 0x76, 0x61, 0x73, 0x74, 0x72, 0x65, 0x20,
0x7C, 0xFE, 0x14, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
0x20, 0x20, 0x20, 0x20, 0x44, 0x65, 0x6E, 0x69, 0x73, 0x20, 0x4D, 0x65, 0x72, 0x63, 0x69, 0x65,
0x72, 0x20, 0x20, 0x7C, 0xFE, 0x14, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
0x20, 0x20, 0x20, 0x43, 0x68, 0x72, 0x69, 0x73, 0x74, 0x69, 0x61, 0x6E, 0x20, 0x52, 0x6F, 0x62,
0x65, 0x72, 0x74, 0x20, 0x20, 0x20, 0x7C, 0xFE, 0x14, 0xFE, 0x14, 0x00, 0x01, 0x00, 0x0A, 0x20,
0x20, 0x56, 0x69, 0x64, 0x65, 0x6F, 0x20, 0x73, 0x66, 0x78, 0x3A, 0x7C, 0x7C, 0xFE, 0x14, 0x20,
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
0x20, 0x50, 0x61, 0x75, 0x6C, 0x20, 0x43, 0x75, 0x69, 0x73, 0x73, 0x65, 0x74, 0x20, 0x20, 0x7C,
0xFE, 0x14, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
0x20, 0x54, 0x68, 0x69, 0x65, 0x72, 0x72, 0x79, 0x20, 0x50, 0x65, 0x72, 0x72, 0x65, 0x61, 0x75,
0x20, 0x20, 0x7C, 0xFE, 0x14, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
0x20, 0x20, 0x20, 0x20, 0x46, 0x61, 0x62, 0x72, 0x69, 0x63, 0x65, 0x20, 0x56, 0x69, 0x73, 0x73,
0x65, 0x72, 0x6F, 0x74, 0x20, 0x7C, 0xFE, 0x14, 0xFE, 0x14, 0x00, 0x01, 0x00, 0x07, 0x20, 0x20,
0x54, 0x65, 0x73, 0x74, 0x65, 0x72, 0x73, 0x3A, 0x7C, 0x7C, 0xFE, 0x14, 0x20, 0x20, 0x20, 0x20,
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x50, 0x68, 0x69, 0x6C,
0x20, 0x42, 0x72, 0x61, 0x64, 0x6C, 0x65, 0x79, 0x20, 0x20, 0x20, 0x20, 0x7C, 0xFE, 0x14, 0x20,
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x50, 0x61, 0x74, 0x72, 0x69,
0x63, 0x69, 0x61, 0x20, 0x43, 0x75, 0x69, 0x73, 0x73, 0x65, 0x74, 0x20, 0x20, 0x20, 0x20, 0x7C,
0xFE, 0x14, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
0x20, 0x53, 0x69, 0x6D, 0x6F, 0x6E, 0x20, 0x48, 0x61, 0x64, 0x6C, 0x69, 0x6E, 0x67, 0x74, 0x6F,
0x6E, 0x20, 0x7C, 0xFE, 0x14, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
0x20, 0x20, 0x20, 0x44, 0x61, 0x6E, 0x69, 0x65, 0x6C, 0x20, 0x4C, 0x6C, 0x65, 0x77, 0x65, 0x6C,
0x6C, 0x79, 0x6E, 0x20, 0x20, 0x7C, 0xFE, 0x14, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
0x20, 0x4A, 0x65, 0x61, 0x6E, 0x2D, 0x50, 0x69, 0x65, 0x72, 0x72, 0x65, 0x20, 0x4C, 0x75, 0x63,
0x6B, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7C, 0xFE, 0x14, 0x20, 0x20, 0x20, 0x20, 0x20,
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x4D, 0x61, 0x72, 0x74, 0x69, 0x6E, 0x20,
0x53, 0x6D, 0x69, 0x74, 0x68, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7C, 0xFE, 0x14, 0xFE, 0x14,
0x00, 0x01, 0x00, 0x08, 0x20, 0x20, 0x4D, 0x61, 0x6E, 0x79, 0x20, 0x74, 0x68, 0x61, 0x6E, 0x6B,
0x73, 0x20, 0x74, 0x6F, 0x2E, 0x2E, 0x2E, 0x7C, 0x7C, 0xFE, 0x14, 0x20, 0x20, 0x20, 0x20, 0x20,
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x4C, 0x6F, 0x72, 0x69, 0x20, 0x43,
0x68, 0x72, 0x69, 0x73, 0x74, 0x65, 0x6E, 0x73, 0x65, 0x6E, 0x20, 0x7C, 0xFE, 0x14, 0x20, 0x20,
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x50, 0x61, 0x74, 0x72, 0x69, 0x63, 0x69,
0x61, 0x20, 0x43, 0x75, 0x69, 0x73, 0x73, 0x65, 0x74, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7C, 0xFE,
0x14, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x41, 0x6E, 0x6E, 0x65, 0x2D, 0x4D,
0x61, 0x72, 0x69, 0x65, 0x20, 0x4A, 0x6F, 0x61, 0x73, 0x73, 0x69, 0x6D, 0x20, 0x20, 0x20, 0x20,
0x20, 0x7C, 0xFE, 0x14, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x4A, 0x65, 0x61, 0x6E,
0x2D, 0x50, 0x69, 0x65, 0x72, 0x72, 0x65, 0x20, 0x4C, 0x75, 0x63, 0x6B, 0x20, 0x20, 0x20, 0x20,
0x20, 0x20, 0x20, 0x20, 0x7C, 0xFE, 0x14, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x4D, 0x61, 0x72, 0x63, 0x20, 0x4D, 0x69, 0x6E, 0x69, 0x65,
0x72, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7C, 0xFE, 0x14, 0xFE, 0x14, 0x00, 0x01, 0x00, 0x05,
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x44, 0x69, 0x72, 0x65, 0x63,
0x74, 0x65, 0x64, 0x20, 0x62, 0x79, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
0x7C, 0x7C, 0xFE, 0x14, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x50, 0x61,
0x75, 0x6C, 0x20, 0x43, 0x75, 0x69, 0x73, 0x73, 0x65, 0x74, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
0x20, 0x20, 0x20, 0x20, 0x7C, 0xFE, 0x14, 0xFE, 0x14, 0x00, 0x01, 0x00, 0x05, 0x20, 0x20, 0x20,
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x50, 0x72, 0x6F, 0x64, 0x75, 0x63, 0x65, 0x64, 0x20,
0x62, 0x79, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7C, 0x7C, 0xFE,
0x14, 0x20, 0x20, 0x20, 0x44, 0x65, 0x6C, 0x70, 0x68, 0x69, 0x6E, 0x65, 0x20, 0x53, 0x6F, 0x66,
0x74, 0x77, 0x61, 0x72, 0x65, 0x20, 0x2F, 0x20, 0x55, 0x53, 0x20, 0x47, 0x4F, 0x4C, 0x44, 0x20,
0x20, 0x7C, 0xFE, 0x14, 0x20, 0x7C, 0x20, 0x7C, 0x20, 0x7C, 0x20, 0x7C, 0x20, 0x7C, 0x20, 0x7C,
0x20, 0x7C, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x28, 0x63, 0x29, 0x4D,
0x43, 0x4D, 0x58, 0x43, 0x49, 0x49, 0x49, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
0x20, 0x20, 0x7C, 0xFE, 0x14, 0xFE, 0x28, 0x00, 0xFF
};
const uint16_t Cutscene::_creditsCutSeq[] = {
0x00, 0x05, 0x2F, 0x32, 0x36, 0x3E, 0x30, 0x39, 0x3F, 0x14, 0x34, 0xFFFF
};
@ -649,6 +807,29 @@ const uint8_t Cutscene::_protectionShapeData[] = {
0x03, 0xFD, 0x06, 0xFC, 0x00, 0x19
};
const Cutscene::Text Cutscene::_frTextsTable[] = {
{ 1, "VOUS RAMASSEZ L'HOLOCUBE" },
{ 2, "VOUS RAMASSEZ LA CLE" },
{ 4, "VOUS RAMASSEZ LE PISTOLET" },
{ 5, "VOTRE BOUCLIER EST RECHARGE" },
{ 10, "VOUS RAMASSEZ||LA CARTE DE CREDITS" },
{ 14, "LA PILE EST RECHARGEE" },
{ 15, "VOUS RAMASSEZ LA PILE" },
{ 16, "VOUS RAMASSEZ LE TELEPORTEUR" },
{ 18, "VOUS RAMASSEZ LA CARTE ID" },
{ 21, "VOUS LUI DONNEZ LE TELEPORTEUR" },
{ 32, "LA RECEPTIONNISTE VOUS DONNE||UN PAQUET" },
{ 33, "VOUS DONNEZ LE COLIS" },
{ 34, "LE GOUVERNEUR VOUS DONNE||LA CARTE DE TRAVAIL" },
{ 35, "LE FAUSSAIRE VOUS DONNE||LA FAUSSE CARTE ID" },
{ 36, "VOUS RAMASSEZ LE FUSIBLE" },
{ 43, "VOUS LUI TENDEZ||VOS PAPIERS" },
{ 44, "VOUS LUI DONNEZ L'ARGENT" },
{ 49, "IL VOUS DONNE||UNE CEINTURE ANTI-G" },
{ 60, "VOUS RAMASSEZ LE TELE RECEPTEUR" },
{ -1, 0 }
};
const Level Game::_gameLevels[] = {
{ "level1", "level1", "level1", 0x00, 1, 3 },
{ "level2", "level2", "level2", 0x2F, 1, 4 },

View File

@ -45,7 +45,7 @@ struct PlayerInput {
};
struct SystemStub {
typedef void (*AudioCallback)(void *param, int8_t *stream, int len);
typedef void (*AudioCallback)(void *param, int16_t *stream, int len);
PlayerInput _pi;

View File

@ -9,26 +9,31 @@
#include "systemstub.h"
#include "util.h"
struct SystemStub_SDL : SystemStub {
enum {
MAX_BLIT_RECTS = 200,
SOUND_SAMPLE_RATE = 22050,
JOYSTICK_COMMIT_VALUE = 3200
};
static const int kAudioHz = 22050;
static const int kJoystickCommitValue = 3200;
struct SystemStub_SDL : SystemStub {
#if SDL_VERSION_ATLEAST(2, 0, 0)
SDL_Window *_window;
SDL_Renderer *_renderer;
SDL_Texture *_texture;
#else
SDL_Surface *_surface;
#endif
SDL_PixelFormat *_fmt;
const char *_caption;
uint16_t *_screenBuffer;
uint16_t *_fadeScreenBuffer;
SDL_Surface *_screenSurface;
bool _fullscreen;
int _currentScaler;
int _scaler;
uint8_t _overscanColor;
uint16_t _pal[256];
int _screenW, _screenH;
SDL_Joystick *_joystick;
SDL_Rect _blitRects[MAX_BLIT_RECTS];
SDL_Rect _blitRects[200];
int _numBlitRects;
bool _fadeOnUpdateScreen;
void (*_audioCbProc)(void *, int8_t *, int);
void (*_audioCbProc)(void *, int16_t *, int);
void *_audioCbData;
int _screenshot;
@ -53,11 +58,11 @@ struct SystemStub_SDL : SystemStub {
virtual void unlockAudio();
void processEvent(const SDL_Event &ev, bool &paused);
void prepareGfxMode();
void cleanupGfxMode();
void switchGfxMode(bool fullscreen, uint8_t scaler);
void flipGfx();
void forceGfxRedraw();
void prepareGraphics();
void cleanupGraphics();
void changeGraphics(bool fullscreen, uint8_t scaler);
void flipGraphics();
void forceGraphicsRedraw();
void drawRect(SDL_Rect *rect, uint8_t color, uint16_t *dst, uint16_t dstPitch);
};
@ -68,14 +73,15 @@ SystemStub *SystemStub_SDL_create() {
void SystemStub_SDL::init(const char *title, int w, int h, int scaler, bool fullscreen) {
SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_JOYSTICK);
SDL_ShowCursor(SDL_DISABLE);
SDL_WM_SetCaption(title, NULL);
_caption = title;
memset(&_pi, 0, sizeof(_pi));
_screenBuffer = 0;
_fadeScreenBuffer = 0;
_fadeOnUpdateScreen = false;
_fullscreen = fullscreen;
_currentScaler = scaler;
_scaler = scaler;
memset(_pal, 0, sizeof(_pal));
_screenW = _screenH = 0;
setScreenSize(w, h);
_joystick = NULL;
if (SDL_NumJoysticks() > 0) {
@ -85,9 +91,10 @@ void SystemStub_SDL::init(const char *title, int w, int h, int scaler, bool full
}
void SystemStub_SDL::destroy() {
cleanupGfxMode();
if (SDL_JoystickOpened(0)) {
cleanupGraphics();
if (_joystick) {
SDL_JoystickClose(_joystick);
_joystick = 0;
}
SDL_Quit();
}
@ -108,7 +115,7 @@ void SystemStub_SDL::setScreenSize(int w, int h) {
}
_screenW = w;
_screenH = h;
prepareGfxMode();
prepareGraphics();
}
void SystemStub_SDL::setPalette(const uint8_t *pal, int n) {
@ -117,16 +124,16 @@ void SystemStub_SDL::setPalette(const uint8_t *pal, int n) {
uint8_t r = pal[i * 3 + 0];
uint8_t g = pal[i * 3 + 1];
uint8_t b = pal[i * 3 + 2];
_pal[i] = SDL_MapRGB(_screenSurface->format, r, g, b);
_pal[i] = SDL_MapRGB(_fmt, r, g, b);
}
}
void SystemStub_SDL::setPaletteEntry(int i, const Color *c) {
_pal[i] = SDL_MapRGB(_screenSurface->format, c->r, c->g, c->b);
_pal[i] = SDL_MapRGB(_fmt, c->r, c->g, c->b);
}
void SystemStub_SDL::getPaletteEntry(int i, Color *c) {
SDL_GetRGB(_pal[i], _screenSurface->format, &c->r, &c->g, &c->b);
SDL_GetRGB(_pal[i], _fmt, &c->r, &c->g, &c->b);
}
void SystemStub_SDL::setOverscanColor(int i) {
@ -134,7 +141,7 @@ void SystemStub_SDL::setOverscanColor(int i) {
}
void SystemStub_SDL::copyRect(int x, int y, int w, int h, const uint8_t *buf, int pitch) {
if (_numBlitRects >= MAX_BLIT_RECTS) {
if (_numBlitRects >= ARRAYSIZE(_blitRects)) {
warning("SystemStub_SDL::copyRect() Too many blit rects, you may experience graphical glitches");
} else {
// extend the dirty region by 1 pixel for scalers accessing 'outer' pixels
@ -211,24 +218,39 @@ static uint16_t blendPixel16(uint16_t colorSrc, uint16_t colorDst, uint32_t mask
}
void SystemStub_SDL::updateScreen(int shakeOffset) {
const int mul = _scalers[_currentScaler].factor;
#if SDL_VERSION_ATLEAST(2, 0, 0)
// _fadeOnUpdateScreen
SDL_UpdateTexture(_texture, 0, _screenBuffer + _screenW + 1, _screenW * sizeof(uint16_t));
SDL_RenderClear(_renderer);
if (shakeOffset != 0) {
SDL_Rect r;
r.x = 0;
r.y = shakeOffset * _scalers[_scaler].factor;
SDL_GetRendererOutputSize(_renderer, &r.w, &r.h);
r.h -= r.y;
SDL_RenderCopy(_renderer, _texture, 0, &r);
} else {
SDL_RenderCopy(_renderer, _texture, 0, 0);
}
SDL_RenderPresent(_renderer);
#else
const int mul = _scalers[_scaler].factor;
if (_fadeOnUpdateScreen) {
const int tempScreenBufferSize = (_screenH + 2) * (_screenW + 2) * sizeof(uint16_t);
uint16_t *tempScreenBuffer = (uint16_t *)calloc(tempScreenBufferSize, 1);
assert(tempScreenBuffer);
const SDL_PixelFormat *pf = _screenSurface->format;
const uint32_t colorMask = (pf->Gmask << 16) | (pf->Rmask | pf->Bmask);
const uint32_t colorMask = (_fmt->Gmask << 16) | (_fmt->Rmask | _fmt->Bmask);
const uint16_t *screenBuffer = _screenBuffer + _screenW + 1;
for (int i = 1; i <= 16; ++i) {
for (int x = 0; x < _screenH * _screenW; ++x) {
tempScreenBuffer[_screenW + 1 + x] = blendPixel16(_fadeScreenBuffer[x], screenBuffer[x], colorMask, i);
}
SDL_LockSurface(_screenSurface);
uint16_t *dst = (uint16_t *)_screenSurface->pixels;
SDL_LockSurface(_surface);
uint16_t *dst = (uint16_t *)_surface->pixels;
const uint16_t *src = tempScreenBuffer + _screenW + 1;
(*_scalers[_currentScaler].proc)(dst, _screenSurface->pitch, src, _screenW, _screenW, _screenH);
SDL_UnlockSurface(_screenSurface);
SDL_UpdateRect(_screenSurface, 0, 0, _screenW * mul, _screenH * mul);
(*_scalers[_scaler].proc)(dst, _surface->pitch, src, _screenW, _screenW, _screenH);
SDL_UnlockSurface(_surface);
SDL_UpdateRect(_surface, 0, 0, _screenW * mul, _screenH * mul);
SDL_Delay(30);
}
free(tempScreenBuffer);
@ -240,35 +262,36 @@ void SystemStub_SDL::updateScreen(int shakeOffset) {
SDL_Rect *br = &_blitRects[i];
int dx = br->x * mul;
int dy = br->y * mul;
SDL_LockSurface(_screenSurface);
uint16_t *dst = (uint16_t *)_screenSurface->pixels + dy * _screenSurface->pitch / 2 + dx;
SDL_LockSurface(_surface);
uint16_t *dst = (uint16_t *)_surface->pixels + dy * _surface->pitch / 2 + dx;
const uint16_t *src = _screenBuffer + (br->y + 1) * _screenW + (br->x + 1);
(*_scalers[_currentScaler].proc)(dst, _screenSurface->pitch, src, _screenW, br->w, br->h);
SDL_UnlockSurface(_screenSurface);
(*_scalers[_scaler].proc)(dst, _surface->pitch, src, _screenW, br->w, br->h);
SDL_UnlockSurface(_surface);
br->x *= mul;
br->y *= mul;
br->w *= mul;
br->h *= mul;
}
SDL_UpdateRects(_screenSurface, _numBlitRects, _blitRects);
SDL_UpdateRects(_surface, _numBlitRects, _blitRects);
} else {
SDL_LockSurface(_screenSurface);
SDL_LockSurface(_surface);
int w = _screenW;
int h = _screenH - shakeOffset;
uint16_t *dst = (uint16_t *)_screenSurface->pixels + shakeOffset * mul * _screenSurface->pitch / 2;
uint16_t *dst = (uint16_t *)_surface->pixels + shakeOffset * mul * _surface->pitch / 2;
const uint16_t *src = _screenBuffer + _screenW + 1;
(*_scalers[_currentScaler].proc)(dst, _screenSurface->pitch, src, _screenW, w, h);
SDL_UnlockSurface(_screenSurface);
(*_scalers[_scaler].proc)(dst, _surface->pitch, src, _screenW, w, h);
SDL_UnlockSurface(_surface);
SDL_Rect r;
r.x = 0;
r.y = 0;
r.w = _screenW * mul;
r.h = shakeOffset * mul;
SDL_FillRect(_screenSurface, &r, _pal[_overscanColor]);
SDL_FillRect(_surface, &r, _pal[_overscanColor]);
SDL_UpdateRect(_screenSurface, 0, 0, _screenW * mul, _screenH * mul);
SDL_UpdateRect(_surface, 0, 0, _screenW * mul, _screenH * mul);
}
#endif
_numBlitRects = 0;
}
@ -294,13 +317,26 @@ void SystemStub_SDL::processEvent(const SDL_Event &ev, bool &paused) {
case SDL_QUIT:
_pi.quit = true;
break;
#if SDL_VERSION_ATLEAST(2, 0, 0)
case SDL_WINDOWEVENT:
switch (ev.window.event) {
case SDL_WINDOWEVENT_FOCUS_GAINED:
case SDL_WINDOWEVENT_FOCUS_LOST:
paused = (ev.window.event == SDL_WINDOWEVENT_FOCUS_LOST);
SDL_PauseAudio(paused);
break;
}
break;
#else
case SDL_ACTIVEEVENT:
if (ev.active.state & SDL_APPINPUTFOCUS) {
paused = ev.active.gain == 0;
SDL_PauseAudio(paused ? 1 : 0);
}
break;
case SDL_JOYHATMOTION:
#endif
case SDL_JOYHATMOTION:
if (_joystick) {
_pi.dirMask = 0;
if (ev.jhat.value & SDL_HAT_UP) {
_pi.dirMask |= PlayerInput::DIR_UP;
@ -314,42 +350,32 @@ void SystemStub_SDL::processEvent(const SDL_Event &ev, bool &paused) {
if (ev.jhat.value & SDL_HAT_RIGHT) {
_pi.dirMask |= PlayerInput::DIR_RIGHT;
}
break;
case SDL_JOYAXISMOTION:
}
break;
case SDL_JOYAXISMOTION:
if (_joystick) {
switch (ev.jaxis.axis) {
case 0:
if (ev.jaxis.value > JOYSTICK_COMMIT_VALUE) {
_pi.dirMask &= ~(PlayerInput::DIR_RIGHT | PlayerInput::DIR_LEFT);
if (ev.jaxis.value > kJoystickCommitValue) {
_pi.dirMask |= PlayerInput::DIR_RIGHT;
if (_pi.dirMask & PlayerInput::DIR_LEFT) {
_pi.dirMask &= ~PlayerInput::DIR_LEFT;
}
} else if (ev.jaxis.value < -JOYSTICK_COMMIT_VALUE) {
} else if (ev.jaxis.value < -kJoystickCommitValue) {
_pi.dirMask |= PlayerInput::DIR_LEFT;
if (_pi.dirMask & PlayerInput::DIR_RIGHT) {
_pi.dirMask &= ~PlayerInput::DIR_RIGHT;
}
} else {
_pi.dirMask &= ~(PlayerInput::DIR_RIGHT | PlayerInput::DIR_LEFT);
}
break;
case 1:
if (ev.jaxis.value > JOYSTICK_COMMIT_VALUE) {
_pi.dirMask &= ~(PlayerInput::DIR_UP | PlayerInput::DIR_DOWN);
if (ev.jaxis.value > kJoystickCommitValue) {
_pi.dirMask |= PlayerInput::DIR_DOWN;
if (_pi.dirMask & PlayerInput::DIR_UP) {
_pi.dirMask &= ~PlayerInput::DIR_UP;
}
} else if (ev.jaxis.value < -JOYSTICK_COMMIT_VALUE) {
} else if (ev.jaxis.value < -kJoystickCommitValue) {
_pi.dirMask |= PlayerInput::DIR_UP;
if (_pi.dirMask & PlayerInput::DIR_DOWN) {
_pi.dirMask &= ~PlayerInput::DIR_DOWN;
}
} else {
_pi.dirMask &= ~(PlayerInput::DIR_UP | PlayerInput::DIR_DOWN);
}
break;
}
break;
case SDL_JOYBUTTONDOWN:
}
break;
case SDL_JOYBUTTONDOWN:
if (_joystick) {
switch (ev.jbutton.button) {
case 0:
_pi.space = true;
@ -364,8 +390,10 @@ void SystemStub_SDL::processEvent(const SDL_Event &ev, bool &paused) {
_pi.backspace = true;
break;
}
break;
case SDL_JOYBUTTONUP:
}
break;
case SDL_JOYBUTTONUP:
if (_joystick) {
switch (ev.jbutton.button) {
case 0:
_pi.space = false;
@ -380,7 +408,8 @@ void SystemStub_SDL::processEvent(const SDL_Event &ev, bool &paused) {
_pi.backspace = false;
break;
}
break;
}
break;
case SDL_KEYUP:
switch (ev.key.keysym.sym) {
case SDLK_LEFT:
@ -415,23 +444,26 @@ void SystemStub_SDL::processEvent(const SDL_Event &ev, bool &paused) {
case SDL_KEYDOWN:
if (ev.key.keysym.mod & KMOD_ALT) {
if (ev.key.keysym.sym == SDLK_RETURN) {
switchGfxMode(!_fullscreen, _currentScaler);
changeGraphics(!_fullscreen, _scaler);
} else if (ev.key.keysym.sym == SDLK_KP_PLUS || ev.key.keysym.sym == SDLK_PAGEUP) {
uint8_t s = _currentScaler + 1;
uint8_t s = _scaler + 1;
if (s < NUM_SCALERS) {
switchGfxMode(_fullscreen, s);
changeGraphics(_fullscreen, s);
}
} else if (ev.key.keysym.sym == SDLK_KP_MINUS || ev.key.keysym.sym == SDLK_PAGEDOWN) {
int8_t s = _currentScaler - 1;
if (_currentScaler > 0) {
switchGfxMode(_fullscreen, s);
int8_t s = _scaler - 1;
if (_scaler > 0) {
changeGraphics(_fullscreen, s);
}
} else if (ev.key.keysym.sym == SDLK_s) {
#if SDL_VERSION_ATLEAST(2, 0, 0)
#else
char name[32];
snprintf(name, sizeof(name), "screenshot-%03d.bmp", _screenshot);
SDL_SaveBMP(_screenSurface, name);
SDL_SaveBMP(_surface, name);
++_screenshot;
debug(DBG_INFO, "Written '%s'", name);
#endif
}
break;
} else if (ev.key.keysym.mod & KMOD_CTRL) {
@ -443,7 +475,7 @@ void SystemStub_SDL::processEvent(const SDL_Event &ev, bool &paused) {
_pi.dbgMask ^= PlayerInput::DF_SETLIFE;
} else if (ev.key.keysym.sym == SDLK_m) {
_pi.mirrorMode = !_pi.mirrorMode;
flipGfx();
flipGraphics();
} else if (ev.key.keysym.sym == SDLK_s) {
_pi.save = true;
} else if (ev.key.keysym.sym == SDLK_l) {
@ -506,22 +538,20 @@ uint32_t SystemStub_SDL::getTimeStamp() {
return SDL_GetTicks();
}
static void mixAudioS8ToU8(void *param, uint8_t *buf, int len) {
static void mixAudioS16(void *param, uint8_t *buf, int len) {
SystemStub_SDL *stub = (SystemStub_SDL *)param;
stub->_audioCbProc(stub->_audioCbData, (int8_t *)buf, len);
for (int i = 0; i < len; ++i) {
buf[i] ^= 0x80;
}
memset(buf, 0, len);
stub->_audioCbProc(stub->_audioCbData, (int16_t *)buf, len / 2);
}
void SystemStub_SDL::startAudio(AudioCallback callback, void *param) {
SDL_AudioSpec desired, obtained;
memset(&desired, 0, sizeof(desired));
desired.freq = SOUND_SAMPLE_RATE;
desired.format = AUDIO_U8;
desired.freq = kAudioHz;
desired.format = AUDIO_S16SYS;
desired.channels = 1;
desired.samples = 2048;
desired.callback = mixAudioS8ToU8;
desired.callback = mixAudioS16;
desired.userdata = this;
if (SDL_OpenAudio(&desired, &obtained) == 0) {
_audioCbProc = callback;
@ -537,7 +567,7 @@ void SystemStub_SDL::stopAudio() {
}
uint32_t SystemStub_SDL::getOutputSampleRate() {
return SOUND_SAMPLE_RATE;
return kAudioHz;
}
void SystemStub_SDL::lockAudio() {
@ -548,17 +578,49 @@ void SystemStub_SDL::unlockAudio() {
SDL_UnlockAudio();
}
void SystemStub_SDL::prepareGfxMode() {
int w = _screenW * _scalers[_currentScaler].factor;
int h = _screenH * _scalers[_currentScaler].factor;
_screenSurface = SDL_SetVideoMode(w, h, 16, _fullscreen ? (SDL_FULLSCREEN | SDL_HWSURFACE) : SDL_HWSURFACE);
if (!_screenSurface) {
error("SystemStub_SDL::prepareGfxMode() Unable to allocate _screen buffer");
void SystemStub_SDL::prepareGraphics() {
#if SDL_VERSION_ATLEAST(2, 0, 0)
switch (_scaler) {
case SCALER_SCALE_2X:
case SCALER_SCALE_3X:
case SCALER_SCALE_4X:
SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "1");
break;
default:
SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "0"); // nearest pixel sampling
break;
}
forceGfxRedraw();
const int windowW = _screenW * _scalers[_scaler].factor;
const int windowH = _screenH * _scalers[_scaler].factor;
int flags = 0;
if (_fullscreen) {
flags |= SDL_WINDOW_FULLSCREEN_DESKTOP;
}
_window = SDL_CreateWindow(_caption, SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, windowW, windowH, flags);
_renderer = SDL_CreateRenderer(_window, -1, SDL_RENDERER_ACCELERATED);
SDL_RenderSetLogicalSize(_renderer, windowW, windowH);
static const uint32_t kPixelFormat = SDL_PIXELFORMAT_RGB565;
_texture = SDL_CreateTexture(_renderer, kPixelFormat, SDL_TEXTUREACCESS_STREAMING, _screenW, _screenH);
_fmt = SDL_AllocFormat(kPixelFormat);
#else
SDL_WM_SetCaption(_caption, NULL);
const int w = _screenW * _scalers[_scaler].factor;
const int h = _screenH * _scalers[_scaler].factor;
int flags = SDL_HWSURFACE;
if (_fullscreen) {
flags |= SDL_FULLSCREEN;
}
static const int kBitDepth = 16;
_surface = SDL_SetVideoMode(w, h, kBitDepth, flags);
if (!_surface) {
error("SystemStub_SDL::prepareGraphics() Unable to allocate _screen buffer");
}
_fmt = _surface->format;
#endif
forceGraphicsRedraw();
}
void SystemStub_SDL::cleanupGfxMode() {
void SystemStub_SDL::cleanupGraphics() {
if (_screenBuffer) {
free(_screenBuffer);
_screenBuffer = 0;
@ -567,21 +629,56 @@ void SystemStub_SDL::cleanupGfxMode() {
free(_fadeScreenBuffer);
_fadeScreenBuffer = 0;
}
if (_screenSurface) {
// freed by SDL_Quit()
_screenSurface = 0;
#if SDL_VERSION_ATLEAST(2, 0, 0)
if (_window) {
SDL_DestroyWindow(_window);
_window = 0;
}
if (_renderer) {
SDL_DestroyRenderer(_renderer);
_renderer = 0;
}
if (_fmt) {
SDL_FreeFormat(_fmt);
_fmt = 0;
}
#else
if (_surface) {
// freed by SDL_Quit()
_surface = 0;
}
#endif
}
void SystemStub_SDL::switchGfxMode(bool fullscreen, uint8_t scaler) {
SDL_FreeSurface(_screenSurface);
void SystemStub_SDL::changeGraphics(bool fullscreen, uint8_t scaler) {
if (_fadeScreenBuffer) {
free(_fadeScreenBuffer);
_fadeScreenBuffer = 0;
}
#if SDL_VERSION_ATLEAST(2, 0, 0)
if (_window) {
SDL_DestroyWindow(_window);
_window = 0;
}
if (_renderer) {
SDL_DestroyRenderer(_renderer);
_renderer = 0;
}
if (_fmt) {
SDL_FreeFormat(_fmt);
_fmt = 0;
}
#else
SDL_FreeSurface(_surface);
_surface = 0;
#endif
_fullscreen = fullscreen;
_currentScaler = scaler;
prepareGfxMode();
forceGfxRedraw();
_scaler = scaler;
prepareGraphics();
forceGraphicsRedraw();
}
void SystemStub_SDL::flipGfx() {
void SystemStub_SDL::flipGraphics() {
uint16_t scanline[256];
assert(_screenW <= 256);
uint16_t *p = _screenBuffer + _screenW + 1;
@ -593,10 +690,10 @@ void SystemStub_SDL::flipGfx() {
memcpy(p, scanline, _screenW * sizeof(uint16_t));
p += _screenW;
}
forceGfxRedraw();
forceGraphicsRedraw();
}
void SystemStub_SDL::forceGfxRedraw() {
void SystemStub_SDL::forceGraphicsRedraw() {
_numBlitRects = 1;
_blitRects[0].x = 0;
_blitRects[0].y = 0;

View File

@ -25,6 +25,15 @@ Video::Video(Resource *res, SystemStub *stub)
_charFrontColor = 0;
_charTransparentColor = 0;
_charShadowColor = 0;
_drawChar = 0;
switch (_res->_type) {
case kResourceTypeAmiga:
_drawChar = &Video::AMIGA_drawStringChar;
break;
case kResourceTypeDOS:
_drawChar = &Video::PC_drawStringChar;
break;
}
}
Video::~Video() {
@ -150,6 +159,12 @@ void Video::setPaletteSlotLE(int palSlot, const uint8_t *palData) {
void Video::setTextPalette() {
debug(DBG_VIDEO, "Video::setTextPalette()");
setPaletteSlotLE(0xE, _textPal);
if (_res->isAmiga()) {
Color c;
c.r = c.g = 0xEE;
c.b = 0;
_stub->setPaletteEntry(0xE7, &c);
}
}
void Video::setPalette0xF() {
@ -313,6 +328,35 @@ static void AMIGA_planar8(uint8_t *dst, int w, int h, const uint8_t *src) {
}
}
static void AMIGA_planar24(uint8_t *dst, int w, int h, const uint8_t *src) {
assert(w == 24);
for (int y = 0; y < h; ++y) {
for (int i = 0; i < 16; ++i) {
int color = 0;
const int mask = 1 << (15 - i);
for (int bit = 0; bit < 4; ++bit) {
if (READ_BE_UINT16(src + bit * 2) & mask) {
color |= 1 << bit;
}
}
dst[i] = color;
}
src += 8;
for (int i = 0; i < 8; ++i) {
int color = 0;
const int mask = 1 << (7 - i);
for (int bit = 0; bit < 4; ++bit) {
if (src[bit] & mask) {
color |= 1 << bit;
}
}
dst[16 + i] = color;
}
src += 4;
dst += w;
}
}
static void AMIGA_planar_mask(uint8_t *dst, int x0, int y0, int w, int h, uint8_t *src, uint8_t *mask, int size) {
dst += y0 * 256 + x0;
for (int y = 0; y < h; ++y) {
@ -341,22 +385,23 @@ static void AMIGA_planar_mask(uint8_t *dst, int x0, int y0, int w, int h, uint8_
}
static void AMIGA_decodeRle(uint8_t *dst, const uint8_t *src) {
int code = READ_BE_UINT16(src) & 0x7FFF; src += 2;
const uint8_t *end = src + code;
do {
code = *src++;
const int size = READ_BE_UINT16(src) & 0x7FFF; src += 2;
for (int i = 0; i < size; ) {
int code = src[i++];
if ((code & 0x80) == 0) {
++code;
memcpy(dst, src, code);
src += code;
if (i + code > size) {
code = size - i;
}
memcpy(dst, &src[i], code);
i += code;
} else {
code = 1 - ((int8_t)code);
memset(dst, *src, code);
++src;
memset(dst, src[i], code);
++i;
}
dst += code;
} while (src < end);
assert(src == end);
}
}
static void PC_drawTileMask(uint8_t *dst, int x0, int y0, int w, int h, uint8_t *m, uint8_t *p, int size) {
@ -615,12 +660,17 @@ void Video::AMIGA_decodeLev(int level, int room) {
// done in ::PC_setLevelPalettes
return;
}
// background
setPaletteSlotBE(0x0, _mapPalSlot1);
for (int i = 1; i < 5; ++i) {
setPaletteSlotBE(i, _mapPalSlot3);
}
setPaletteSlotBE(0x6, _mapPalSlot3);
// objects
setPaletteSlotBE(0x1, (level == 0 || level == 1) ? _mapPalSlot3 : _mapPalSlot2);
setPaletteSlotBE(0x2, _mapPalSlot3);
setPaletteSlotBE(0x3, _mapPalSlot3);
// conrad
setPaletteSlotBE(0x4, _mapPalSlot3);
// foreground
setPaletteSlotBE(0x8, _mapPalSlot1);
// inventory
setPaletteSlotBE(0xA, _mapPalSlot3);
}
@ -655,6 +705,9 @@ void Video::AMIGA_decodeSpc(const uint8_t *src, int w, int h, uint8_t *dst) {
case 32:
AMIGA_planar16(dst, w / 16, h, 4, src);
break;
case 24:
AMIGA_planar24(dst, w, h, src);
break;
default:
warning("AMIGA_decodeSpc w=%d unimplemented", w);
break;
@ -788,7 +841,7 @@ void Video::AMIGA_drawStringChar(uint8_t *dst, int pitch, const uint8_t *src, ui
for (int y = 0; y < 8; ++y) {
for (int x = 0; x < 8; ++x) {
if (src[x] != 0) {
dst[x] = 0x1D;
dst[x] = color;
}
}
src += 16;
@ -819,15 +872,7 @@ void Video::PC_drawStringChar(uint8_t *dst, int pitch, const uint8_t *src, uint8
const char *Video::drawString(const char *str, int16_t x, int16_t y, uint8_t col) {
debug(DBG_VIDEO, "Video::drawString('%s', %d, %d, 0x%X)", str, x, y, col);
void (Video::*drawCharFunc)(uint8_t *, int, const uint8_t *, uint8_t, uint8_t) = 0;
switch (_res->_type) {
case kResourceTypeAmiga:
drawCharFunc = &Video::AMIGA_drawStringChar;
break;
case kResourceTypeDOS:
drawCharFunc = &Video::PC_drawStringChar;
break;
}
drawCharFunc dcf = _drawChar;
int len = 0;
uint8_t *dst = _frontLayer + y * 256 + x;
while (1) {
@ -835,7 +880,7 @@ const char *Video::drawString(const char *str, int16_t x, int16_t y, uint8_t col
if (c == 0 || c == 0xB || c == 0xA) {
break;
}
(this->*drawCharFunc)(dst, 256, _res->_fnt, col, c);
(this->*dcf)(dst, 256, _res->_fnt, col, c);
dst += CHAR_W;
++len;
}

View File

@ -13,6 +13,8 @@ struct Resource;
struct SystemStub;
struct Video {
typedef void (Video::*drawCharFunc)(uint8_t *, int, const uint8_t *, uint8_t, uint8_t);
enum {
GAMESCREEN_W = 256,
GAMESCREEN_H = 224,
@ -44,6 +46,7 @@ struct Video {
uint8_t *_screenBlocks;
bool _fullRefresh;
uint8_t _shakeOffset;
drawCharFunc _drawChar;
Video(Resource *res, SystemStub *stub);
~Video();