From 5aa50ec889109864cbdd13298b4026c8ed78f484 Mon Sep 17 00:00:00 2001 From: Eugene Sandulenko Date: Sat, 28 Feb 2009 10:46:33 +0000 Subject: Patch #2638336: Broken Sword PSX Support svn-id: r38956 --- engines/sword1/control.cpp | 127 +++++++++- engines/sword1/detection.cpp | 25 +- engines/sword1/logic.cpp | 4 + engines/sword1/module.mk | 3 +- engines/sword1/mouse.cpp | 61 +++-- engines/sword1/music.cpp | 43 +++- engines/sword1/music.h | 1 + engines/sword1/screen.cpp | 539 ++++++++++++++++++++++++++++++++++++++----- engines/sword1/screen.h | 9 + engines/sword1/sound.cpp | 144 ++++++++++-- engines/sword1/sound.h | 3 +- engines/sword1/sword1.cpp | 77 ++++++- engines/sword1/sword1.h | 6 +- engines/sword1/text.cpp | 38 ++- engines/sword1/text.h | 1 + 15 files changed, 947 insertions(+), 134 deletions(-) (limited to 'engines/sword1') diff --git a/engines/sword1/control.cpp b/engines/sword1/control.cpp index 248f593aab..ce237fe9f9 100644 --- a/engines/sword1/control.cpp +++ b/engines/sword1/control.cpp @@ -45,6 +45,7 @@ #include "sword1/sword1.h" #include "sword1/sworddefs.h" #include "sword1/swordres.h" +#include "sword1/screen.h" namespace Sword1 { @@ -120,10 +121,11 @@ ControlButton::ControlButton(uint16 x, uint16 y, uint32 resId, uint8 id, uint8 f _resMan->resOpen(_resId); FrameHeader *tmp = _resMan->fetchFrame(_resMan->fetchRes(_resId), 0); _width = _resMan->getUint16(tmp->width); + _width = (_width > SCREEN_WIDTH) ? SCREEN_WIDTH : _width; _height = _resMan->getUint16(tmp->height); if ((x == 0) && (y == 0)) { // center the frame (used for panels); - _x = (640 - _width) / 2; - _y = (480 - _height) / 2; + _x = (((640 - _width) / 2) < 0)? 0 : ((640 - _width) / 2) ; + _y = (((480 - _height) / 2) < 0)? 0 : ((480 - _height) / 2); } _dstBuf = screenBuf + _y * SCREEN_WIDTH + _x; _system = system; @@ -141,13 +143,78 @@ void ControlButton::draw(void) { FrameHeader *fHead = _resMan->fetchFrame(_resMan->fetchRes(_resId), _frameIdx); uint8 *src = (uint8*)fHead + sizeof(FrameHeader); uint8 *dst = _dstBuf; - for (uint16 cnt = 0; cnt < _resMan->readUint16(&fHead->height); cnt++) { - for (uint16 cntx = 0; cntx < _resMan->readUint16(&fHead->width); cntx++) - if (src[cntx]) - dst[cntx] = src[cntx]; - dst += SCREEN_WIDTH; - src += _resMan->readUint16(&fHead->width); - } + + if (SwordEngine::isPsx() && _resId) { + uint8 *HIFbuf = (uint8*)malloc(_resMan->readUint16(&fHead->height) * _resMan->readUint16(&fHead->width)); + memset(HIFbuf, 0, _resMan->readUint16(&fHead->height) * _resMan->readUint16(&fHead->width)); + Screen::decompressHIF(src, HIFbuf); + src = HIFbuf; + + if (_resMan->readUint16(&fHead->width) < 300) + for (uint16 cnt = 0; cnt < _resMan->readUint16(&fHead->height); cnt++) { + for (uint16 cntx = 0; cntx < _resMan->readUint16(&fHead->width); cntx++) + if (src[cntx]) + dst[cntx] = src[cntx]; + + dst += SCREEN_WIDTH; + for (uint16 cntx = 0; cntx < _resMan->readUint16(&fHead->width); cntx++) + if (src[cntx]) + dst[cntx] = src[cntx]; + + dst += SCREEN_WIDTH; + src += _resMan->readUint16(&fHead->width); + } + else if (_resId == SR_DEATHPANEL) { //Hardcoded goodness for death panel psx version + for (uint16 cnt = 0; cnt < _resMan->readUint16(&fHead->height)/2; cnt++) { + //Stretched panel is bigger than 640px, check we don't draw outside screen + for (uint16 cntx = 0; (cntx < (_resMan->readUint16(&fHead->width))/3) && (cntx < (SCREEN_WIDTH-3) ); cntx++) + if (src[cntx]) { + dst[cntx * 3] = src[cntx]; + dst[cntx * 3 + 1] = src[cntx]; + dst[cntx * 3 + 2] = src[cntx]; + } + dst+= SCREEN_WIDTH; + + for (uint16 cntx = 0; cntx < (_resMan->readUint16(&fHead->width))/3; cntx++) + if (src[cntx]) { + dst[cntx * 3] = src[cntx]; + dst[cntx * 3 + 1] = src[cntx]; + dst[cntx * 3 + 2] = src[cntx]; + } + dst += SCREEN_WIDTH; + src += _resMan->readUint16(&fHead->width)/3; + } + } else { //NASTY HACK, save slots needs to be multiplied my 4 in height... need a better way to identify these images + for (uint16 cnt = 0; cnt < _resMan->readUint16(&fHead->height); cnt++) { + for (uint16 cntx = 0; cntx < _resMan->readUint16(&fHead->width) / 2; cntx++) + if (src[cntx]) { + dst[cntx * 2] = src[cntx]; + dst[cntx * 2 + 1] = src[cntx]; + } + + dst += SCREEN_WIDTH; + for (uint16 cntx = 0; cntx < _resMan->readUint16(&fHead->width) / 2; cntx++) + if (src[cntx]) { + dst[cntx * 2] = src[cntx]; + dst[cntx * 2 + 1] = src[cntx]; + } + + dst += SCREEN_WIDTH; + src += _resMan->readUint16(&fHead->width)/2; + } + } + + free(HIFbuf); + } else + for (uint16 cnt = 0; cnt < _resMan->readUint16(&fHead->height); cnt++) { + for (uint16 cntx = 0; cntx < _resMan->readUint16(&fHead->width); cntx++) + if (src[cntx]) + dst[cntx] = src[cntx]; + + dst += SCREEN_WIDTH; + src += _resMan->readUint16(&fHead->width); + } + _system->copyRectToScreen(_dstBuf, SCREEN_WIDTH, _x, _y, _width, _height); } @@ -940,17 +1007,37 @@ void Control::renderText(const uint8 *str, uint16 x, uint16 y, uint8 mode) { FrameHeader *chSpr = _resMan->fetchFrame(font, *str - 32); uint8 *sprData = (uint8*)chSpr + sizeof(FrameHeader); + uint8 *HIFbuf = NULL; + + if (SwordEngine::isPsx()) { //Text fonts are compressed in psx version + HIFbuf = (uint8 *)malloc(_resMan->getUint16(chSpr->height) * _resMan->getUint16(chSpr->width)); + memset(HIFbuf, 0, _resMan->getUint16(chSpr->height) * _resMan->getUint16(chSpr->width)); + Screen::decompressHIF(sprData, HIFbuf); + sprData = HIFbuf; + } + for (uint16 cnty = 0; cnty < _resMan->getUint16(chSpr->height); cnty++) { for (uint16 cntx = 0; cntx < _resMan->getUint16(chSpr->width); cntx++) { if (sprData[cntx]) dst[cntx] = sprData[cntx]; } + + if(SwordEngine::isPsx()) { //On PSX version we need to double horizontal lines + dst += SCREEN_WIDTH; + for (uint16 cntx = 0; cntx < _resMan->getUint16(chSpr->width); cntx++) + if (sprData[cntx]) + dst[cntx] = sprData[cntx]; + } + sprData += _resMan->getUint16(chSpr->width); dst += SCREEN_WIDTH; } destX += _resMan->getUint16(chSpr->width) - 3; str++; + + free(HIFbuf); } + _system->copyRectToScreen(_screenBuf + y * SCREEN_WIDTH + x, SCREEN_WIDTH, x, y, (destX - x) + 3, 28); } @@ -963,14 +1050,34 @@ void Control::renderVolumeBar(uint8 id, uint8 volL, uint8 volR) { FrameHeader *frHead = _resMan->fetchFrame(_resMan->openFetchRes(SR_VLIGHT), (vol + 15) >> 4); uint8 *destMem = _screenBuf + destY * SCREEN_WIDTH + destX; uint8 *srcMem = (uint8*)frHead + sizeof(FrameHeader); - for (uint16 cnty = 0; cnty < _resMan->getUint16(frHead->height); cnty++) { + uint16 barHeight = _resMan->getUint16(frHead->height); + uint8 *psxVolBuf = NULL; + + if (SwordEngine::isPsx()) { + psxVolBuf = (uint8 *)malloc(_resMan->getUint16(frHead->height) / 2 * _resMan->getUint16(frHead->width)); + memset(psxVolBuf, 0, _resMan->getUint16(frHead->height) / 2 * _resMan->getUint16(frHead->width)); + Screen::decompressHIF(srcMem, psxVolBuf); + srcMem = psxVolBuf; + barHeight /= 2; + } + + for (uint16 cnty = 0; cnty < barHeight; cnty++) { memcpy(destMem, srcMem, _resMan->getUint16(frHead->width)); + + if(SwordEngine::isPsx()) { //linedoubling + destMem += SCREEN_WIDTH; + memcpy(destMem, srcMem, _resMan->getUint16(frHead->width)); + } + srcMem += _resMan->getUint16(frHead->width); destMem += SCREEN_WIDTH; } + _system->copyRectToScreen(_screenBuf + destY * SCREEN_WIDTH + destX, SCREEN_WIDTH, destX, destY, _resMan->getUint16(frHead->width), _resMan->getUint16(frHead->height)); _resMan->resClose(SR_VLIGHT); destX += 32; + + free(psxVolBuf); } } diff --git a/engines/sword1/detection.cpp b/engines/sword1/detection.cpp index b478199f86..17ae871099 100644 --- a/engines/sword1/detection.cpp +++ b/engines/sword1/detection.cpp @@ -45,6 +45,8 @@ static const PlainGameDescriptor sword1MacFullSettings = {"sword1mac", "Broken Sword 1: The Shadow of the Templars (Mac)"}; static const PlainGameDescriptor sword1MacDemoSettings = {"sword1macdemo", "Broken Sword 1: The Shadow of the Templars (Mac demo)"}; +static const PlainGameDescriptor sword1PSXSettings = + {"sword1psx", "Broken Sword 1: The Shadow of the Templars (PlayStation)"}; // check these subdirectories (if present) static const char *g_dirNames[] = { "clusters", "speech" }; @@ -52,20 +54,23 @@ static const char *g_dirNames[] = { "clusters", "speech" }; #define NUM_COMMON_FILES_TO_CHECK 1 #define NUM_PC_FILES_TO_CHECK 3 #define NUM_MAC_FILES_TO_CHECK 4 +#define NUM_PSX_FILES_TO_CHECK 2 #define NUM_DEMO_FILES_TO_CHECK 1 #define NUM_MAC_DEMO_FILES_TO_CHECK 1 -#define NUM_FILES_TO_CHECK NUM_COMMON_FILES_TO_CHECK + NUM_PC_FILES_TO_CHECK + NUM_MAC_FILES_TO_CHECK + NUM_DEMO_FILES_TO_CHECK + NUM_MAC_DEMO_FILES_TO_CHECK +#define NUM_FILES_TO_CHECK NUM_COMMON_FILES_TO_CHECK + NUM_PC_FILES_TO_CHECK + NUM_MAC_FILES_TO_CHECK + NUM_PSX_FILES_TO_CHECK + NUM_DEMO_FILES_TO_CHECK + NUM_MAC_DEMO_FILES_TO_CHECK static const char *g_filesToCheck[NUM_FILES_TO_CHECK] = { // these files have to be found - "swordres.rif", // Mac and PC version - "general.clu", // PC version only - "compacts.clu", // PC version only - "scripts.clu", // PC version only + "swordres.rif", // Mac, PC and PSX version + "general.clu", // PC and PSX version + "compacts.clu", // PC and PSX version + "scripts.clu", // PC and PSX version "general.clm", // Mac version only "compacts.clm", // Mac version only "scripts.clm", // Mac version only "paris2.clm", // Mac version (full game only) "cows.mad", // this one should only exist in the demo version "scripts.clm", // Mac version both demo and full game + "speech.dat", // PSX version only + "tunes.dat", // PSX version only // the engine needs several more files to work, but checking these should be sufficient }; @@ -123,6 +128,8 @@ GameDescriptor SwordMetaEngine::findGame(const char *gameid) const { return sword1MacFullSettings; if (0 == scumm_stricmp(gameid, sword1MacDemoSettings.gameid)) return sword1MacDemoSettings; + if (0 == scumm_stricmp(gameid, sword1PSXSettings.gameid)) + return sword1PSXSettings; return GameDescriptor(); } @@ -157,6 +164,7 @@ GameList SwordMetaEngine::detectGames(const Common::FSList &fslist) const { bool macFilesFound = true; bool demoFilesFound = true; bool macDemoFilesFound = true; + bool psxFilesFound = true; for (i = 0; i < NUM_COMMON_FILES_TO_CHECK; i++) if (!filesFound[i]) mainFilesFound = false; @@ -172,10 +180,15 @@ GameList SwordMetaEngine::detectGames(const Common::FSList &fslist) const { for (j = 0; j < NUM_DEMO_FILES_TO_CHECK; i++, j++) if (!filesFound[i]) macDemoFilesFound = false; + for (j = 0; j < NUM_PSX_FILES_TO_CHECK; i++, j++) + if (!filesFound[i]) + psxFilesFound = false; if (mainFilesFound && pcFilesFound && demoFilesFound) detectedGames.push_back(sword1DemoSettings); - else if (mainFilesFound && pcFilesFound) + else if (mainFilesFound && pcFilesFound && psxFilesFound) + detectedGames.push_back(sword1PSXSettings); + else if (mainFilesFound && pcFilesFound && !psxFilesFound) detectedGames.push_back(sword1FullSettings); else if (mainFilesFound && macFilesFound) detectedGames.push_back(sword1MacFullSettings); diff --git a/engines/sword1/logic.cpp b/engines/sword1/logic.cpp index bd35e774a0..211fe51179 100644 --- a/engines/sword1/logic.cpp +++ b/engines/sword1/logic.cpp @@ -112,6 +112,10 @@ void Logic::newScreen(uint32 screen) { fnFullSetFrame(cpt, SAND_25, IMPPLSCDT, IMPPLS, 0, 0, 0, 0); // impression filled with plaster } + // work around, at screen 69 in psx version TOP menu gets stuck at disabled, fix it at next screen (71) + if( (screen == 71) && (SwordEngine::isPsx())) + _scriptVars[TOP_MENU_DISABLED] = 0; + if (SwordEngine::_systemVars.justRestoredGame) { // if we've just restored a game - we want George to be exactly as saved fnAddHuman(NULL, 0, 0, 0, 0, 0, 0, 0); if (_scriptVars[GEORGE_WALKING]) { // except that if George was walking when we saveed the game diff --git a/engines/sword1/module.mk b/engines/sword1/module.mk index 6e48069dd0..e7297bfecd 100644 --- a/engines/sword1/module.mk +++ b/engines/sword1/module.mk @@ -18,7 +18,8 @@ MODULE_OBJS := \ sound.o \ staticres.o \ sword1.o \ - text.o + text.o \ + vag.o # This module can be built as a plugin ifeq ($(ENABLE_SWORD1), DYNAMIC_PLUGIN) diff --git a/engines/sword1/mouse.cpp b/engines/sword1/mouse.cpp index fa26e49e81..870a5a4594 100644 --- a/engines/sword1/mouse.cpp +++ b/engines/sword1/mouse.cpp @@ -37,6 +37,7 @@ #include "sword1/sworddefs.h" #include "sword1/swordres.h" #include "sword1/menu.h" +#include "sword1/sword1.h" namespace Sword1 { @@ -201,13 +202,30 @@ void Mouse::createPointer(uint32 ptrId, uint32 luggageId) { if (ptrId) { MousePtr *lugg = NULL; MousePtr *ptr = (MousePtr*)_resMan->openFetchRes(ptrId); - uint16 resSizeX = _resMan->getLEUint16(ptr->sizeX); - uint16 resSizeY = _resMan->getLEUint16(ptr->sizeY); uint16 noFrames = _resMan->getLEUint16(ptr->numFrames); + uint16 ptrSizeX = _resMan->getLEUint16(ptr->sizeX); + uint16 ptrSizeY = _resMan->getLEUint16(ptr->sizeY); + uint16 luggSizeX; + uint16 luggSizeY; + uint16 resSizeX; + uint16 resSizeY; + + if (SwordEngine::isPsx()) //PSX pointers are half height + ptrSizeY *= 2; + if (luggageId) { lugg = (MousePtr*)_resMan->openFetchRes(luggageId); - resSizeX = MAX(resSizeX, (uint16)((resSizeX / 2) + _resMan->getLEUint16(lugg->sizeX))); - resSizeY = MAX(resSizeY, (uint16)((resSizeY / 2) + _resMan->getLEUint16(lugg->sizeY))); + luggSizeX = _resMan->getLEUint16(lugg->sizeX); + luggSizeY = _resMan->getLEUint16(lugg->sizeY); + + if (SwordEngine::isPsx()) + luggSizeY *= 2; + + resSizeX = MAX(ptrSizeX, (uint16)((ptrSizeX / 2) + luggSizeX)); + resSizeY = MAX(ptrSizeY, (uint16)((ptrSizeY / 2) + luggSizeY)); + } else { + resSizeX = ptrSizeX; + resSizeY = ptrSizeY; } _currentPtr = (MousePtr*)malloc(sizeof(MousePtr) + resSizeX * resSizeY * noFrames); _currentPtr->hotSpotX = _resMan->getLEUint16(ptr->hotSpotX); @@ -218,31 +236,48 @@ void Mouse::createPointer(uint32 ptrId, uint32 luggageId) { uint8 *ptrData = (uint8*)_currentPtr + sizeof(MousePtr); memset(ptrData, 255, resSizeX * resSizeY * noFrames); if (luggageId) { - uint8 *dstData = ptrData + resSizeX - _resMan->getLEUint16(lugg->sizeX); + uint8 *dstData = ptrData + resSizeX - luggSizeX; for (uint32 frameCnt = 0; frameCnt < noFrames; frameCnt++) { uint8 *luggSrc = (uint8*)lugg + sizeof(MousePtr); - dstData += (resSizeY - _resMan->getLEUint16(lugg->sizeY)) * resSizeX; - for (uint32 cnty = 0; cnty < _resMan->getLEUint16(lugg->sizeY); cnty++) { - for (uint32 cntx = 0; cntx < _resMan->getLEUint16(lugg->sizeX); cntx++) + dstData += (resSizeY - luggSizeY) * resSizeX; + for (uint32 cnty = 0; cnty < (SwordEngine::isPsx() ? luggSizeY / 2 : luggSizeY); cnty++) { + for (uint32 cntx = 0; cntx < luggSizeX; cntx++) if (luggSrc[cntx]) dstData[cntx] = luggSrc[cntx]; + + if(SwordEngine::isPsx()) { + dstData += resSizeX; + for (uint32 cntx = 0; cntx < luggSizeX; cntx++) + if (luggSrc[cntx]) + dstData[cntx] = luggSrc[cntx]; + } + dstData += resSizeX; - luggSrc += _resMan->getLEUint16(lugg->sizeX); + luggSrc += luggSizeX; } } _resMan->resClose(luggageId); } + uint8 *dstData = ptrData; uint8 *srcData = (uint8*)ptr + sizeof(MousePtr); for (uint32 frameCnt = 0; frameCnt < noFrames; frameCnt++) { - for (uint32 cnty = 0; cnty < _resMan->getLEUint16(ptr->sizeY); cnty++) { - for (uint32 cntx = 0; cntx < _resMan->getLEUint16(ptr->sizeX); cntx++) + for (uint32 cnty = 0; cnty < (SwordEngine::isPsx() ? ptrSizeY / 2 : ptrSizeY); cnty++) { + for (uint32 cntx = 0; cntx < ptrSizeX; cntx++) if (srcData[cntx]) dstData[cntx] = srcData[cntx]; - srcData += _resMan->getLEUint16(ptr->sizeX); + + if(SwordEngine::isPsx()) { + dstData +=resSizeX; + for (uint32 cntx = 0; cntx < ptrSizeX; cntx++) + if (srcData[cntx]) + dstData[cntx] = srcData[cntx]; + } + + srcData += ptrSizeX; dstData += resSizeX; } - dstData += (resSizeY - _resMan->getLEUint16(ptr->sizeY)) * resSizeX; + dstData += (resSizeY - ptrSizeY) * resSizeX; } _resMan->resClose(ptrId); } diff --git a/engines/sword1/music.cpp b/engines/sword1/music.cpp index 27e7568fcc..8553b6a286 100644 --- a/engines/sword1/music.cpp +++ b/engines/sword1/music.cpp @@ -29,7 +29,10 @@ #include "common/util.h" #include "common/system.h" +#include "sword1/sword1.h" #include "sword1/music.h" +#include "sword1/vag.h" + #include "sound/aiff.h" #include "sound/flac.h" #include "sound/mixer.h" @@ -252,6 +255,35 @@ bool MusicHandle::play(const char *fileBase, bool loop) { return true; } +bool MusicHandle::playPSX(uint16 id, bool loop) { + stop(); + + if (!_file.isOpen()) + if (!_file.open("tunes.dat")) + return false; + + Common::File tableFile; + if (!tableFile.open("tunes.tab")) + return false; + + tableFile.seek((id - 1) * 8, SEEK_SET); + uint32 offset = tableFile.readUint32LE() * 0x800; + uint32 size = tableFile.readUint32LE(); + + tableFile.close(); + + if (size != 0xffffffff) { + _file.seek(offset, SEEK_SET); + _audioSource = new VagStream(_file.readStream(size), loop); + fadeUp(); + } else { + _audioSource = NULL; + return false; + } + + return true; +} + void MusicHandle::fadeDown() { if (streaming()) { if (_fading < 0) @@ -276,8 +308,7 @@ bool MusicHandle::endOfData() const { return !streaming(); } -// is we don't have an audiosource, return some dummy values. -// shouldn't happen anyways. +// if we don't have an audiosource, return some dummy values. bool MusicHandle::streaming(void) const { return (_audioSource) ? (!_audioSource->endOfStream()) : false; } @@ -411,7 +442,13 @@ void Music::startMusic(int32 tuneId, int32 loopFlag) { /* The handle will load the music file now. It can take a while, so unlock the mutex before, to have the soundthread playing normally. As the corresponding _converter is NULL, the handle will be ignored by the playing thread */ - if (_handles[newStream].play(_tuneList[tuneId], loopFlag != 0)) { + if (SwordEngine::isPsx()) { ; + if (_handles[newStream].playPSX(tuneId, loopFlag != 0)) { + _mutex.lock(); + _converter[newStream] = Audio::makeRateConverter(_handles[newStream].getRate(), _mixer->getOutputRate(), _handles[newStream].isStereo(), false); + _mutex.unlock(); + } + } else if (_handles[newStream].play(_tuneList[tuneId], loopFlag != 0)) { _mutex.lock(); _converter[newStream] = Audio::makeRateConverter(_handles[newStream].getRate(), _mixer->getOutputRate(), _handles[newStream].isStereo(), false); _mutex.unlock(); diff --git a/engines/sword1/music.h b/engines/sword1/music.h index 11cd3730ec..6487a1c3e5 100644 --- a/engines/sword1/music.h +++ b/engines/sword1/music.h @@ -47,6 +47,7 @@ public: MusicHandle() : _fading(0), _audioSource(NULL) {} virtual int readBuffer(int16 *buffer, const int numSamples); bool play(const char *filename, bool loop); + bool playPSX(uint16 id, bool loop); void stop(); void fadeUp(); void fadeDown(); diff --git a/engines/sword1/screen.cpp b/engines/sword1/screen.cpp index e5b1fcd5e5..3d184e15e5 100644 --- a/engines/sword1/screen.cpp +++ b/engines/sword1/screen.cpp @@ -34,6 +34,7 @@ #include "sword1/resman.h" #include "sword1/objectman.h" #include "sword1/menu.h" +#include "sword1/swordres.h" #include "sword1/sword1.h" #ifdef BACKEND_8BIT @@ -137,7 +138,7 @@ void Screen::fnSetPalette(uint8 start, uint16 length, uint32 id, bool fadeUp) { if (start == 0) // force color 0 to black palData[0] = palData[1] = palData[2] = 0; - if (SwordEngine::_systemVars.isMac) { // see bug #1701058 + if (SwordEngine::isMac()) { // see bug #1701058 if (start != 0 && start + length == 256) // and force color 255 to black as well palData[(length-1)*3+0] = palData[(length-1)*3+1] = palData[(length-1)*3+2] = 0; } @@ -358,24 +359,45 @@ void Screen::quitScreen(void) { void Screen::draw(void) { uint8 cnt; + + debug(8, "Screen::draw() -> _currentScreen %u", _currentScreen); + if (_currentScreen == 54) { // rm54 has a BACKGROUND parallax layer in parallax[0] - if (_parallax[0]) + if (_parallax[0] && !SwordEngine::isPsx() ) //Avoid drawing this parallax on PSX edition, it gets occluded by background renderParallax(_parallax[0]); uint8 *src = _layerBlocks[0]; uint8 *dest = _screenBuf; + uint8 *indxScreen = NULL; + + if(SwordEngine::isPsx()) { + indxScreen = psxShrinkedBackgroundToIndexed(_layerBlocks[0], _scrnSizeX, _scrnSizeY); + src = indxScreen; + } for (uint16 cnty = 0; cnty < _scrnSizeY; cnty++) for (uint16 cntx = 0; cntx < _scrnSizeX; cntx++) { if (*src) - if (!SwordEngine::_systemVars.isMac || *src != 255) // see bug #1701058 + if (!(SwordEngine::isMac()) || *src != 255) // see bug #1701058 *dest = *src; dest++; src++; } - } else + free(indxScreen); + + } else if (!(SwordEngine::isPsx())) { memcpy(_screenBuf, _layerBlocks[0], _scrnSizeX * _scrnSizeY); + } else { //We are using PSX version + uint8 *indxScreen; + if(_currentScreen == 45 || _currentScreen == 55 || + _currentScreen == 57 || _currentScreen == 63 || _currentScreen == 71) // Width shrinked backgrounds + indxScreen = psxShrinkedBackgroundToIndexed(_layerBlocks[0], _scrnSizeX, _scrnSizeY); + else + indxScreen = psxBackgroundToIndexed(_layerBlocks[0], _scrnSizeX, _scrnSizeY); + memcpy(_screenBuf, indxScreen, _scrnSizeX * _scrnSizeY); + free(indxScreen); + } for (cnt = 0; cnt < _backLength; cnt++) processImage(_backList[cnt]); @@ -393,6 +415,18 @@ void Screen::draw(void) { if (_parallax[1]) renderParallax(_parallax[1]); + // PSX version has parallax layer for this room in an external file (TRAIN.PLX) + if(SwordEngine::isPsx() && _currentScreen == 63) { + Common::File parallax; + uint8 *trainPLX = NULL; + parallax.open("TRAIN.PLX"); + trainPLX = (uint8*) malloc(parallax.size()); + parallax.read(trainPLX, parallax.size()); + parallax.close(); + renderParallax(trainPLX); + free(trainPLX); + } + for (cnt = 0; cnt < _foreLength; cnt++) processImage(_foreList[cnt]); @@ -403,8 +437,9 @@ void Screen::processImage(uint32 id) { Object *compact; FrameHeader *frameHead; int scale; - + compact = _objMan->fetchObject(id); + if (compact->o_type == TYPE_TEXT) frameHead = _textMan->giveSpriteData((uint8)compact->o_target); else @@ -414,6 +449,7 @@ void Screen::processImage(uint32 id) { uint16 spriteX = compact->o_anim_x; uint16 spriteY = compact->o_anim_y; + if (compact->o_status & STAT_SHRINK) { scale = (compact->o_scale_a * compact->o_ycoord + compact->o_scale_b) / 256; spriteX += ((int16)_resMan->readUint16(&frameHead->offsetX) * scale) / 256; @@ -425,7 +461,13 @@ void Screen::processImage(uint32 id) { } uint8 *tonyBuf = NULL; - if (frameHead->runTimeComp[3] == '7') { // RLE7 encoded? + uint8 *hifBuf = NULL; + if (SwordEngine::isPsx() && compact->o_type != TYPE_TEXT) { // PSX sprites are compressed with HIF + hifBuf = (uint8*)malloc(_resMan->readUint16(&frameHead->width) * _resMan->readUint16(&frameHead->height)/2); + memset(hifBuf, 0x00, (_resMan->readUint16(&frameHead->width) * _resMan->readUint16(&frameHead->height)/2)); + decompressHIF(sprData, hifBuf); + sprData = hifBuf; + } else if (frameHead->runTimeComp[3] == '7') { // RLE7 encoded? decompressRLE7(sprData, _resMan->readUint32(&frameHead->compSize), _rleBuffer); sprData = _rleBuffer; } else if (frameHead->runTimeComp[3] == '0') { // RLE0 encoded? @@ -439,14 +481,29 @@ void Screen::processImage(uint32 id) { uint16 sprSizeX, sprSizeY; if (compact->o_status & STAT_SHRINK) { - sprSizeX = (scale * _resMan->readUint16(&frameHead->width)) / 256; - sprSizeY = (scale * _resMan->readUint16(&frameHead->height)) / 256; - fastShrink(sprData, _resMan->readUint16(&frameHead->width), _resMan->readUint16(&frameHead->height), scale, _shrinkBuffer); + memset(_shrinkBuffer, 0, SHRINK_BUFFER_SIZE); //Clean shrink buffer to avoid corruption + if( SwordEngine::isPsx() && (compact->o_resource != GEORGE_MEGA)) { //PSX Height shrinked sprites + sprSizeX = (scale * _resMan->readUint16(&frameHead->width)) / 256; + sprSizeY = (scale * (_resMan->readUint16(&frameHead->height))) / 256 / 2; + fastShrink(sprData, _resMan->readUint16(&frameHead->width), (_resMan->readUint16(&frameHead->height)) / 2, scale, _shrinkBuffer); + } else if (SwordEngine::isPsx()) { //PSX width/height shrinked sprites + sprSizeX = (scale * _resMan->readUint16(&frameHead->width)) / 256 / 2; + sprSizeY = (scale * _resMan->readUint16(&frameHead->height)) / 256 / 2; + fastShrink(sprData, _resMan->readUint16(&frameHead->width) / 2, _resMan->readUint16(&frameHead->height) / 2, scale, _shrinkBuffer); + } else { + sprSizeX = (scale * _resMan->readUint16(&frameHead->width)) / 256; + sprSizeY = (scale * _resMan->readUint16(&frameHead->height)) / 256; + fastShrink(sprData, _resMan->readUint16(&frameHead->width), _resMan->readUint16(&frameHead->height), scale, _shrinkBuffer); + } sprData = _shrinkBuffer; } else { sprSizeX = _resMan->readUint16(&frameHead->width); - sprSizeY = _resMan->readUint16(&frameHead->height); + if(SwordEngine::isPsx()) { //PSX sprites are half height + sprSizeY = _resMan->readUint16(&frameHead->height) / 2; + } else + sprSizeY = (_resMan->readUint16(&frameHead->height)); } + if (!(compact->o_status & STAT_OVERRIDE)) { //mouse size linked to exact size & coordinates of sprite box - shrink friendly if (_resMan->readUint16(&frameHead->offsetX) || _resMan->readUint16(&frameHead->offsetY)) { @@ -463,24 +520,45 @@ void Screen::processImage(uint32 id) { compact->o_mouse_y2 = spriteY + sprSizeY; } } + uint16 sprPitch = sprSizeX; uint16 incr; spriteClipAndSet(&spriteX, &spriteY, &sprSizeX, &sprSizeY, &incr); + if ((sprSizeX > 0) && (sprSizeY > 0)) { - drawSprite(sprData + incr, spriteX, spriteY, sprSizeX, sprSizeY, sprPitch); - if (!(compact->o_status&STAT_FORE)) + if( (!(SwordEngine::isPsx()) || (compact->o_type == TYPE_TEXT) + || (compact->o_resource == LVSFLY) || !(compact->o_resource == GEORGE_MEGA) && (sprSizeX < 260))) + drawSprite(sprData + incr, spriteX, spriteY, sprSizeX, sprSizeY, sprPitch); + else if (((sprSizeX >= 260) && (sprSizeX < 450)) || ((compact->o_resource == GMWRITH) && (sprSizeX < 515)) // a psx shrinked sprite (1/2 width) + || ((compact->o_resource == GMPOWER) && (sprSizeX < 515)) ) // some needs to be hardcoded, headers don't give useful infos + drawPsxHalfShrinkedSprite(sprData + incr, spriteX, spriteY, sprSizeX / 2, sprSizeY, sprPitch / 2); + else if (sprSizeX >= 450) // A PSX double shrinked sprite (1/3 width) + drawPsxFullShrinkedSprite(sprData + incr, spriteX, spriteY, sprSizeX / 3, sprSizeY, sprPitch / 3); + else // This is for psx half shrinked, walking george and remaining sprites + drawPsxHalfShrinkedSprite(sprData + incr, spriteX, spriteY, sprSizeX, sprSizeY, sprPitch); + if (!(compact->o_status&STAT_FORE) && !(SwordEngine::isPsx() && (compact->o_resource == MOUBUSY))) // Check fixes moue sprite being masked by layer, happens only on psx verticalMask(spriteX, spriteY, sprSizeX, sprSizeY); } + if (compact->o_type != TYPE_TEXT) _resMan->resClose(compact->o_resource); + if (tonyBuf) free(tonyBuf); + + if (hifBuf) + free(hifBuf); } void Screen::verticalMask(uint16 x, uint16 y, uint16 bWidth, uint16 bHeight) { if (_roomDefTable[_currentScreen].totalLayers <= 1) return; + if (SwordEngine::isPsx()) { // PSX sprites are vertical shrinked, and some width shrinked + bHeight *= 2; + bWidth *= 2; + } + bWidth = (bWidth + (x & (SCRNGRID_X - 1)) + (SCRNGRID_X - 1)) / SCRNGRID_X; bHeight = (bHeight + (y & (SCRNGRID_Y - 1)) + (SCRNGRID_Y - 1)) / SCRNGRID_Y; @@ -504,7 +582,11 @@ void Screen::verticalMask(uint16 x, uint16 y, uint16 bWidth, uint16 bHeight) { uint16 *grid = _layerGrid[level] + gridX + blkx + gridY * lGridSizeX; for (int16 blky = bHeight - 1; blky >= 0; blky--) { if (*grid) { - uint8 *blkData = _layerBlocks[level + 1] + (_resMan->readUint16(grid) - 1) * 128; + uint8 *blkData; + if (SwordEngine::isPsx()) + blkData = _layerBlocks[level + 1] + (_resMan->readUint16(grid) - 1) * 64; //PSX layers are half height too... + else + blkData = _layerBlocks[level + 1] + (_resMan->readUint16(grid) - 1) * 128; blitBlockClear(x + blkx, y + blky, blkData); } else break; @@ -517,23 +599,45 @@ void Screen::verticalMask(uint16 x, uint16 y, uint16 bWidth, uint16 bHeight) { void Screen::blitBlockClear(uint16 x, uint16 y, uint8 *data) { uint8 *dest = _screenBuf + (y * SCRNGRID_Y) * _scrnSizeX + (x * SCRNGRID_X); - for (uint8 cnty = 0; cnty < SCRNGRID_Y; cnty++) { + + for (uint8 cnty = 0; cnty < (SwordEngine::isPsx() ? SCRNGRID_Y / 2 : SCRNGRID_Y); cnty++) { for (uint8 cntx = 0; cntx < SCRNGRID_X; cntx++) if (data[cntx]) dest[cntx] = data[cntx]; + + if (SwordEngine::isPsx()) { + dest += _scrnSizeX; + for (uint8 cntx = 0; cntx < SCRNGRID_X; cntx++) + if (data[cntx]) + dest[cntx] = data[cntx]; + } + data += SCRNGRID_X; dest += _scrnSizeX; } } void Screen::renderParallax(uint8 *data) { - ParallaxHeader *header = (ParallaxHeader*)data; - uint32 *lineIndexes = (uint32*)(data + sizeof(ParallaxHeader)); - assert((_resMan->getUint16(header->sizeX) >= SCREEN_WIDTH) && (_resMan->getUint16(header->sizeY) >= SCREEN_DEPTH)); - uint16 paraScrlX, paraScrlY; uint16 scrnScrlX, scrnScrlY; uint16 scrnWidth, scrnHeight; + uint16 paraSizeX, paraSizeY; + uint8 *psxPlx = NULL; + ParallaxHeader *header = NULL; + uint32 *lineIndexes = NULL; + + if (SwordEngine::isPsx()) { //Parallax headers are different in PSX version + psxPlx = psxParallaxToIndexed(data); + paraSizeX = READ_LE_UINT16(psxPlx); + paraSizeY = READ_LE_UINT16(psxPlx+2); + } else { + header = (ParallaxHeader*)data; + lineIndexes = (uint32*)(data + sizeof(ParallaxHeader)); + paraSizeX = _resMan->getUint16(header->sizeX); + paraSizeY = _resMan->getUint16(header->sizeY); + } + + assert((paraSizeX >= SCREEN_WIDTH) && (paraSizeY >= SCREEN_DEPTH)); // we have to render more than the visible screen part for displaying scroll frames scrnScrlX = MIN((uint32)_oldScrollX, Logic::_scriptVars[SCROLL_OFFSET_X]); @@ -541,71 +645,141 @@ void Screen::renderParallax(uint8 *data) { scrnScrlY = MIN((uint32)_oldScrollY, Logic::_scriptVars[SCROLL_OFFSET_Y]); scrnHeight = SCREEN_DEPTH + ABS((int32)_oldScrollY - (int32)Logic::_scriptVars[SCROLL_OFFSET_Y]); + if (_scrnSizeX != SCREEN_WIDTH) { - double scrlfx = (_resMan->getUint16(header->sizeX) - SCREEN_WIDTH) / ((double)(_scrnSizeX - SCREEN_WIDTH)); + double scrlfx = (paraSizeX - SCREEN_WIDTH) / ((double)(_scrnSizeX - SCREEN_WIDTH)); paraScrlX = (uint16)(scrnScrlX * scrlfx); } else paraScrlX = 0; if (_scrnSizeY != SCREEN_DEPTH) { - double scrlfy = (_resMan->getUint16(header->sizeY) - SCREEN_DEPTH) / ((double)(_scrnSizeY - SCREEN_DEPTH)); + double scrlfy = (paraSizeY - SCREEN_DEPTH) / ((double)(_scrnSizeY - SCREEN_DEPTH)); paraScrlY = (uint16)(scrnScrlY * scrlfy); } else paraScrlY = 0; - for (uint16 cnty = 0; cnty < scrnHeight; cnty++) { - uint8 *src = data + _resMan->readUint32(lineIndexes + cnty + paraScrlY); - uint8 *dest = _screenBuf + scrnScrlX + (cnty + scrnScrlY) * _scrnSizeX; - uint16 remain = paraScrlX; - uint16 xPos = 0; - while (remain) { // skip past the first part of the parallax to get to the right scrolling position - uint8 doSkip = *src++; - if (doSkip <= remain) - remain -= doSkip; - else { - xPos = doSkip - remain; - dest += xPos; - remain = 0; - } - uint8 doCopy = *src++; - if (doCopy <= remain) { - remain -= doCopy; - src += doCopy; - } else { - uint16 remCopy = doCopy - remain; - memcpy(dest, src + remain, remCopy); - dest += remCopy; - src += doCopy; - xPos = remCopy; - remain = 0; - } + if(SwordEngine::isPsx()) + for (uint16 cnty = 0; (cnty < SCREEN_DEPTH) && (cnty < paraSizeY); cnty++) { + uint8 *src = psxPlx + 4 + paraScrlY * paraSizeX + cnty * paraSizeX + paraScrlX; + uint8 *dest = _screenBuf + scrnScrlX + (cnty + scrnScrlY) * _scrnSizeX/* * 2*/; + uint8 pix; + for (uint16 idx = 0; (idx < SCREEN_WIDTH) && (idx < paraSizeX); idx++) // make sure we don't write outside screen + if (pix = *(src + idx)) //If data is 0x00, don't write (transparency) + *(dest + idx) = pix; } - while (xPos < scrnWidth) { - if (uint8 skip = *src++) { - dest += skip; - xPos += skip; - } - if (xPos < scrnWidth) { - if (uint8 doCopy = *src++) { - if (xPos + doCopy > scrnWidth) - doCopy = scrnWidth - xPos; - memcpy(dest, src, doCopy); - dest += doCopy; - xPos += doCopy; + else + for (uint16 cnty = 0; cnty < scrnHeight; cnty++) { + uint8 *src = data + _resMan->readUint32(lineIndexes + cnty + paraScrlY); + uint8 *dest = _screenBuf + scrnScrlX + (cnty + scrnScrlY) * _scrnSizeX; + uint16 remain = paraScrlX; + uint16 xPos = 0; + while (remain) { // skip past the first part of the parallax to get to the right scrolling position + uint8 doSkip = *src++; + if (doSkip <= remain) + remain -= doSkip; + else { + xPos = doSkip - remain; + dest += xPos; + remain = 0; + } + uint8 doCopy = *src++; + if (doCopy <= remain) { + remain -= doCopy; + src += doCopy; + } else { + uint16 remCopy = doCopy - remain; + memcpy(dest, src + remain, remCopy); + dest += remCopy; src += doCopy; + xPos = remCopy; + remain = 0; + } + } + while (xPos < scrnWidth) { + if (uint8 skip = *src++) { + dest += skip; + xPos += skip; + } + if (xPos < scrnWidth) { + if (uint8 doCopy = *src++) { + if (xPos + doCopy > scrnWidth) + doCopy = scrnWidth - xPos; + memcpy(dest, src, doCopy); + dest += doCopy; + xPos += doCopy; + src += doCopy; + } } } } - } + + if (psxPlx) + free(psxPlx); } void Screen::drawSprite(uint8 *sprData, uint16 sprX, uint16 sprY, uint16 sprWidth, uint16 sprHeight, uint16 sprPitch) { uint8 *dest = _screenBuf + (sprY * _scrnSizeX) + sprX; - + for (uint16 cnty = 0; cnty < sprHeight; cnty++) { for (uint16 cntx = 0; cntx < sprWidth; cntx++) if (sprData[cntx]) dest[cntx] = sprData[cntx]; + + if (SwordEngine::isPsx()) { //On PSX version we need to double horizontal lines + dest += _scrnSizeX; + for (uint16 cntx = 0; cntx < sprWidth; cntx++) + if (sprData[cntx]) + dest[cntx] = sprData[cntx]; + } + + sprData += sprPitch; + dest += _scrnSizeX; + } +} + +// Used to draw psx sprites which are 1/2 of original width +void Screen::drawPsxHalfShrinkedSprite(uint8 *sprData, uint16 sprX, uint16 sprY, uint16 sprWidth, uint16 sprHeight, uint16 sprPitch) { + uint8 *dest = _screenBuf + (sprY * _scrnSizeX) + sprX; + + for (uint16 cnty = 0; cnty < sprHeight; cnty++) { + for (uint16 cntx = 0; cntx < sprWidth; cntx++) + if (sprData[cntx]) { + dest[cntx * 2] = sprData[cntx]; //In these sprites we need to double vetical lines too... + dest[cntx * 2 + 1] = sprData[cntx]; + } + + dest += _scrnSizeX; + for (uint16 cntx = 0; cntx < sprWidth; cntx++) + if (sprData[cntx]) { + dest[cntx * 2] = sprData[cntx]; + dest[cntx * 2 + 1] = sprData[cntx]; + } + + sprData += sprPitch; + dest += _scrnSizeX; + } +} + +// Used to draw psx sprites which are 1/3 of original width +void Screen::drawPsxFullShrinkedSprite(uint8 *sprData, uint16 sprX, uint16 sprY, uint16 sprWidth, uint16 sprHeight, uint16 sprPitch) { + uint8 *dest = _screenBuf + (sprY * _scrnSizeX) + sprX; + + for (uint16 cnty = 0; cnty < sprHeight; cnty++) { + for (uint16 cntx = 0; cntx < sprWidth ; cntx++) + if (sprData[cntx]) { + dest[cntx * 3] = sprData[cntx]; //In these sprites we need to double vertical lines too... + dest[cntx * 3 + 1] = sprData[cntx]; + dest[cntx * 3 + 2] = sprData[cntx]; + } + + dest += _scrnSizeX; + for (uint16 cntx = 0; cntx < sprWidth; cntx++) + if (sprData[cntx]) { + dest[cntx * 3] = sprData[cntx]; + dest[cntx * 3 + 1] = sprData[cntx]; + dest[cntx * 3 + 2] = sprData[cntx]; + } + sprData += sprPitch; dest += _scrnSizeX; } @@ -618,6 +792,7 @@ void Screen::fastShrink(uint8 *src, uint32 width, uint32 height, uint32 scale, u uint32 step = 0x10000 / scale; uint8 columnTab[160]; uint32 res = step >> 1; + for (uint16 cnt = 0; cnt < resWidth; cnt++) { columnTab[cnt] = (uint8)(res >> 8); res += step; @@ -675,6 +850,188 @@ void Screen::addToGraphicList(uint8 listId, uint32 objId) { } } +uint8* Screen::psxBackgroundToIndexed(uint8* psxBackground, uint32 bakXres, uint32 bakYres) { + uint32 xresInTiles = bakXres / 16; + uint32 yresInTiles = ((bakYres / 2) % 16) ? (bakYres / 32) + 1 : (bakYres / 32); + uint32 totTiles = xresInTiles * yresInTiles; + uint32 tileYpos = 0; //tile position in a virtual xresInTiles * yresInTiles grid + uint32 tileXpos = 0; + uint32 tag = READ_LE_UINT32(psxBackground); + + uint8 *decomp_tile = (uint8 *)malloc(16 * 16); //Tiles are always 16 * 16 + uint8 *halfres_buffer = (uint8 *)malloc(totTiles * 16 * 16); //This buffer will contain the half vertical res image + + bool isCompressed = (tag == 0x434F4D50); + + psxBackground += 4; //We skip the id tag + + for (uint32 currentTile = 0; currentTile < totTiles; currentTile++) { + uint32 tileOffset = READ_LE_UINT32(psxBackground + 4 * currentTile); + + if(isCompressed) + decompressHIF(psxBackground + tileOffset - 4, decomp_tile); //Decompress the tile into decomp_tile + else + memcpy(decomp_tile, psxBackground + tileOffset - 4, 16*16); + + if (currentTile > 0 && !(currentTile % xresInTiles)) { //Finished a line of tiles, going down + tileYpos++; + tileXpos = 0; + } + + for (byte tileLine=0; tileLine<16; tileLine++) + memcpy(halfres_buffer + tileLine * bakXres + tileXpos * 16 + tileYpos * bakXres * 16, decomp_tile + tileLine * 16, 16); //Copy data to destination buffer + + tileXpos++; + } + + free(decomp_tile); + + uint8 *fullres_buffer = (uint8 *)malloc(bakXres * yresInTiles * 32); + memset(fullres_buffer, 0x00, bakXres * yresInTiles * 32); + + //Let's linedouble the image (to keep correct aspect ratio) + for (uint32 currentLine = 0; currentLine < (bakYres/2); currentLine++) { + memcpy(fullres_buffer + currentLine * bakXres * 2, halfres_buffer + currentLine * bakXres, bakXres); // destination_line is 2*original_line + memcpy(fullres_buffer + currentLine * bakXres * 2 + bakXres, halfres_buffer + currentLine * bakXres, bakXres); // destination_line+1 + } + + free(halfres_buffer); + + return fullres_buffer; +} + +// needed because some psx backgrounds are half width and half height +uint8* Screen::psxShrinkedBackgroundToIndexed(uint8* psxBackground, uint32 bakXres, uint32 bakYres) { + uint32 xresInTiles = (bakXres / 2) % 16 ? (bakXres / 32) + 1 : (bakXres / 32); + uint32 yresInTiles = (bakYres / 2) % 16 ? (bakYres / 32) + 1 : (bakYres / 32); + uint32 totTiles = xresInTiles * yresInTiles; + uint32 tileYpos = 0; //tile position in a virtual xresInTiles * yresInTiles grid + uint32 tileXpos = 0; + uint32 dataBegin = READ_LE_UINT32(psxBackground + 4); + uint32 realWidth = xresInTiles * 16; + + uint8 *decomp_tile = (uint8 *)malloc(16 * 16); //Tiles are always 16 * 16 + uint8 *halfres_buffer = (uint8*) malloc(totTiles * 16 * 16); //This buffer will contain the half vertical res image + memset(halfres_buffer, 0, totTiles * 16 * 16); + + bool isCompressed = (READ_LE_UINT32(psxBackground) == MKID_BE('COMP')); + + totTiles -= xresInTiles; + psxBackground += 4; //We skip the id tag + + uint32 currentTile; + for (currentTile = 0; currentTile < totTiles; currentTile++) { + uint32 tileOffset = READ_LE_UINT32(psxBackground + 4 * currentTile); + + if(isCompressed) + decompressHIF(psxBackground + tileOffset - 4, decomp_tile); //Decompress the tile into decomp_tile + else + memcpy(decomp_tile, psxBackground + tileOffset - 4, 16 * 16); + + if (currentTile > 0 && !(currentTile % xresInTiles)) { //Finished a line of tiles, going down + tileYpos++; + tileXpos = 0; + } + + for (byte tileLine = 0; tileLine < 16; tileLine++) + memcpy(halfres_buffer + (tileLine * realWidth) + (tileXpos * 16) + (tileYpos * realWidth * 16), decomp_tile + (tileLine * 16), 16); //Copy data to destination buffer + + tileXpos++; + } + + uint8 *fullres_buffer = (uint8 *)malloc(bakXres * (yresInTiles + 1) * 32); + memset(fullres_buffer, 0x00, bakXres * (yresInTiles + 1) * 32); + + for (uint32 currentLine = 0; currentLine < ((yresInTiles - 1) * 16); currentLine++) { + for (uint32 cntx = 0; cntx < realWidth; cntx++) { + fullres_buffer[currentLine * 2 * bakXres + cntx * 2] = halfres_buffer[currentLine * realWidth + cntx]; + fullres_buffer[currentLine * 2 * bakXres + cntx * 2 + 1] = halfres_buffer[currentLine * realWidth + cntx]; + } + for (uint32 cntx = 0; cntx < realWidth; cntx++) { + fullres_buffer[(currentLine * 2 + 1) * bakXres + cntx * 2] = halfres_buffer[currentLine * realWidth + cntx]; + fullres_buffer[(currentLine * 2 + 1) * bakXres + cntx * 2 + 1] = halfres_buffer[currentLine * realWidth + cntx]; + } + } + free(halfres_buffer); + + //Calculate number of remaining tiles + uint32 remainingTiles = (dataBegin - (currentTile * 4 + 4)) / 4; + + // Last line of tiles is FULL WIDTH! + uint32 tileHeight = (remainingTiles == xresInTiles * 2) ? 16 : 8; + + halfres_buffer = (uint8*) malloc(bakXres * 16 * 2); + memset(halfres_buffer, 0, bakXres * 16 * 2); + + tileXpos = 0; + for (; currentTile < totTiles + remainingTiles; currentTile++) { + uint32 tileOffset = READ_LE_UINT32(psxBackground + 4 * currentTile); + + if(isCompressed) + decompressHIF(psxBackground + tileOffset - 4, decomp_tile); //Decompress the tile into decomp_tile + else + memcpy(decomp_tile, psxBackground + tileOffset - 4, 256); + + for (byte tileLine = 0; tileLine < tileHeight; tileLine++) + memcpy(halfres_buffer + tileLine * bakXres * 2 + tileXpos * 16, decomp_tile + tileLine * 16, 16); + + tileXpos++; + } + + free(decomp_tile); + + for (uint32 currentLine = 0; currentLine < tileHeight; currentLine++) { + memcpy(fullres_buffer + (currentLine + (yresInTiles - 1) * 16) * bakXres * 2, halfres_buffer + currentLine * bakXres * 2, bakXres * 2); + memcpy(fullres_buffer + (currentLine + (yresInTiles - 1) * 16) * bakXres * 2 + bakXres, halfres_buffer + currentLine * bakXres * 2, bakXres * 2); + } + + free(halfres_buffer); + + return fullres_buffer; +} + +uint8* Screen::psxParallaxToIndexed(uint8* psxParallax) { + uint16 xresInTiles = READ_LE_UINT16(psxParallax + 10); + uint16 yresInTiles = READ_LE_UINT16(psxParallax + 12); + uint16 totTiles = READ_LE_UINT16(psxParallax + 14); + uint32 plxXres = xresInTiles * 16; + uint32 plxYres = yresInTiles * 16; + + uint8 *plxPos = psxParallax + 16; + uint8 *plxOff = psxParallax + 16 + totTiles * 2; + uint8 *plxData = psxParallax + 16 + totTiles * 2 + totTiles * 4; + + uint8 *decomp_tile = (uint8 *)malloc(16 * 16); // Tiles are always 16 * 16 + uint8 *halfres_buffer = (uint8 *)malloc(4 + yresInTiles * xresInTiles * 16 * 16); //This buffer will contain the half vertical res image + memset(halfres_buffer, 0, 4 + yresInTiles * xresInTiles * 16 * 16); //Clean the buffer + + for (uint16 currentTile = 0; currentTile < totTiles - 1; currentTile++) { + uint32 tileOffset = READ_LE_UINT32(plxOff + 4 * currentTile); + uint8 tileXpos = *(plxPos + 2 * currentTile); //Fetch tile position in grid + uint8 tileYpos = *(plxPos + 2 * currentTile + 1); + decompressHIF(plxData + tileOffset, decomp_tile); //Decompress the tile into decomp_tile + + for (byte tileLine = 0; tileLine < 16; tileLine++) + memcpy(halfres_buffer + tileLine * plxXres + tileXpos * 16 + tileYpos * plxXres * 16, decomp_tile + tileLine * 16, 16); //Copy data to destination buffer + } + + free(decomp_tile); + + uint8 *dest_buffer = (uint8*) malloc (plxXres * plxYres * 2 + 4); + WRITE_LE_UINT16(dest_buffer, plxXres); //Insert resolution information + WRITE_LE_UINT16(dest_buffer + 2, plxYres*2); + + //Let's linedouble the image (to keep correct aspect ratio) + for (uint32 currentLine = 0; currentLine < plxYres; currentLine++) { + memcpy(dest_buffer + 4 + currentLine * plxXres * 2, halfres_buffer + currentLine * plxXres, plxXres); // destination_line is 2*original_line + memcpy(dest_buffer + 4 + currentLine * plxXres * 2 + plxXres, halfres_buffer + currentLine * plxXres, plxXres); // destination_line+1 + } + + free(halfres_buffer); + + return dest_buffer; +} + void Screen::decompressTony(uint8 *src, uint32 compSize, uint8 *dest) { uint8 *endOfData = src + compSize; while (src < endOfData) { @@ -721,6 +1078,30 @@ void Screen::decompressRLE0(uint8 *src, uint32 compSize, uint8 *dest) { } } +void Screen::decompressHIF(uint8 *src, uint8 *dest) { + for (;;) { //Main loop + byte control_byte = *src++; + uint32 byte_count = 0; + while (byte_count < 8) { + if (control_byte & 0x80) { + uint16 info_word = READ_BE_UINT16(src); //Read the info word + src += 2; + if (info_word == 0xFFFF) return; //Got 0xFFFF code, finished. + + int32 repeat_count = (info_word >> 12) + 2; //How many time data needs to be refetched + while(repeat_count >= 0) { + uint8 *old_data_src = dest - ((info_word & 0xFFF) + 1); + *dest++ = *old_data_src; + repeat_count--; + } + } else + *dest++ = *src++; + byte_count++; + control_byte <<= 1; //Shifting left the control code one bit + } + } +} + void Screen::fadePalette(void) { if (_fadingStep == 16) memcpy(_currentPalette, _targetPalette, 256 * 4); @@ -773,6 +1154,7 @@ void Screen::spriteClipAndSet(uint16 *pSprX, uint16 *pSprY, uint16 *pSprWidth, u *pSprWidth = 0; else *pSprWidth = (uint16)sprW; + *pSprX = (uint16)sprX; *pSprY = (uint16)sprY; @@ -780,6 +1162,19 @@ void Screen::spriteClipAndSet(uint16 *pSprX, uint16 *pSprY, uint16 *pSprWidth, u // sprite will be drawn, so mark it in the grid buffer uint16 gridH = (*pSprHeight + (sprY & (SCRNGRID_Y - 1)) + (SCRNGRID_Y - 1)) / SCRNGRID_Y; uint16 gridW = (*pSprWidth + (sprX & (SCRNGRID_X - 1)) + (SCRNGRID_X - 1)) / SCRNGRID_X; + + if(SwordEngine::isPsx()) { + gridH *= 2; // This will correct the PSX sprite being cut at half height + gridW *= 2; // and masking problems when sprites are stretched in width + + uint16 bottomSprPos = (*pSprY + (*pSprHeight) * 2); //Position of bottom line of sprite + if ( bottomSprPos > _scrnSizeY ) { //Check that resized psx sprite isn't drawn outside of screen boundaries + uint16 outScreen = bottomSprPos - _scrnSizeY; + *pSprHeight -= (outScreen % 2) ? (outScreen + 1) / 2 : outScreen / 2; + } + + } + uint16 gridX = sprX / SCRNGRID_X; uint16 gridY = sprY / SCRNGRID_Y; uint8 *gridBuf = _screenGrid + gridX + gridY * _gridSizeX; @@ -806,16 +1201,32 @@ void Screen::showFrame(uint16 x, uint16 y, uint32 resId, uint32 frameNo, const b uint8 frame[40 * 40]; int i, j; - memset(frame, 199, sizeof(frame)); // Dark gray background + if(SwordEngine::isPsx()) + memset(frame, 0, sizeof(frame)); // PSX top menu is black + else + memset(frame, 199, sizeof(frame)); // Dark gray background if (resId != 0xffffffff) { FrameHeader *frameHead = _resMan->fetchFrame(_resMan->openFetchRes(resId), frameNo); uint8 *frameData = ((uint8*)frameHead) + sizeof(FrameHeader); - for (i = 0; i < _resMan->getUint16(frameHead->height); i++) { - for (j = 0; j < _resMan->getUint16(frameHead->height); j++) { - frame[(i + 4) * 40 + j + 2] = frameData[i * _resMan->getUint16(frameHead->width) + j]; + if (SwordEngine::isPsx()) { //We need to decompress PSX frames + uint8 *frameBufferPSX = (uint8 *)malloc(_resMan->getUint16(frameHead->width) * _resMan->getUint16(frameHead->height)/2); + decompressHIF(frameData, frameBufferPSX); + + for (i = 0; i < _resMan->getUint16(frameHead->height) / 2; i++) { + for (j = 0; j < _resMan->getUint16(frameHead->width); j++) { + uint8 data = frameBufferPSX[i * _resMan->getUint16(frameHead->width) + j]; + frame[(i * 2 + 4) * 40 + j + 2] = data; + frame[(i * 2 + 1 + 4) * 40 + j + 2] = data; //Linedoubling the sprite + } } + + free(frameBufferPSX); + } else { + for (i = 0; i < _resMan->getUint16(frameHead->height); i++) + for (j = 0; j < _resMan->getUint16(frameHead->height); j++) + frame[(i + 4) * 40 + j + 2] = frameData[i * _resMan->getUint16(frameHead->width) + j]; } _resMan->resClose(resId); diff --git a/engines/sword1/screen.h b/engines/sword1/screen.h index b115408120..7ecc96fefc 100644 --- a/engines/sword1/screen.h +++ b/engines/sword1/screen.h @@ -96,6 +96,8 @@ public: void fnFlash(uint8 color); void fnBorder(uint8 color); + static void decompressHIF(uint8 *src, uint8 *dest); + #ifdef BACKEND_8BIT void plotYUV(byte *lut, int width, int height, byte *const *dat); #endif @@ -116,6 +118,11 @@ private: void processImage(uint32 id); void spriteClipAndSet(uint16 *pSprX, uint16 *pSprY, uint16 *sprWidth, uint16 *sprHeight, uint16 *incr); void drawSprite(uint8 *sprData, uint16 sprX, uint16 sprY, uint16 sprWidth, uint16 sprHeight, uint16 sprPitch); + void drawPsxHalfShrinkedSprite(uint8 *sprData, uint16 sprX, uint16 sprY, uint16 sprWidth, uint16 sprHeight, uint16 sprPitch); + void drawPsxFullShrinkedSprite(uint8 *sprData, uint16 sprX, uint16 sprY, uint16 sprWidth, uint16 sprHeight, uint16 sprPitch); + uint8* psxBackgroundToIndexed(uint8* psxBackground, uint32 bakXres, uint32 bakYres); + uint8* psxShrinkedBackgroundToIndexed(uint8* psxBackground, uint32 bakXres, uint32 bakYres); + uint8* psxParallaxToIndexed(uint8* psxParallax); void decompressRLE7(uint8 *src, uint32 compSize, uint8 *dest); void decompressRLE0(uint8 *src, uint32 compSize, uint8 *dest); void decompressTony(uint8 *src, uint32 compSize, uint8 *dest); @@ -160,3 +167,5 @@ private: #endif //BSSCREEN_H + + diff --git a/engines/sword1/sound.cpp b/engines/sword1/sound.cpp index caf6d48613..cab3c2d2a4 100644 --- a/engines/sword1/sound.cpp +++ b/engines/sword1/sound.cpp @@ -34,6 +34,7 @@ #include "sword1/resman.h" #include "sword1/logic.h" #include "sword1/sword1.h" +#include "sword1/vag.h" #include "sound/flac.h" #include "sound/mp3.h" @@ -160,22 +161,29 @@ void Sound::playSample(QueueElement *elem) { if (_fxList[elem->id].roomVolList[cnt].roomNo) { if ((_fxList[elem->id].roomVolList[cnt].roomNo == (int)Logic::_scriptVars[SCREEN]) || (_fxList[elem->id].roomVolList[cnt].roomNo == -1)) { - + uint8 volL = (_fxList[elem->id].roomVolList[cnt].leftVol * 10 * _sfxVolL) / 255; uint8 volR = (_fxList[elem->id].roomVolList[cnt].rightVol * 10 * _sfxVolR) / 255; int8 pan = (volR - volL) / 2; uint8 volume = (volR + volL) / 2; - uint32 size = READ_LE_UINT32(sampleData + 0x28); - uint8 flags; - if (READ_LE_UINT16(sampleData + 0x22) == 16) - flags = Audio::Mixer::FLAG_16BITS | Audio::Mixer::FLAG_LITTLE_ENDIAN; - else - flags = Audio::Mixer::FLAG_UNSIGNED; - if (READ_LE_UINT16(sampleData + 0x16) == 2) - flags |= Audio::Mixer::FLAG_STEREO; - if (_fxList[elem->id].type == FX_LOOP) - flags |= Audio::Mixer::FLAG_LOOP; - _mixer->playRaw(Audio::Mixer::kSFXSoundType, &elem->handle, sampleData + 0x2C, size, 11025, flags, elem->id, volume, pan); + + if (SwordEngine::isPsx()) { ; + uint32 size = READ_LE_UINT32(sampleData); + Audio::AudioStream *audStream = new VagStream(new Common::MemoryReadStream(sampleData + 4, size-4), _fxList[elem->id].type == FX_LOOP); + _mixer->playInputStream(Audio::Mixer::kSFXSoundType, &elem->handle, audStream, elem->id, volume, pan, false, false, false); + } else { + uint32 size = READ_LE_UINT32(sampleData + 0x28); + uint8 flags; + if (READ_LE_UINT16(sampleData + 0x22) == 16) + flags = Audio::Mixer::FLAG_16BITS | Audio::Mixer::FLAG_LITTLE_ENDIAN; + else + flags = Audio::Mixer::FLAG_UNSIGNED; + if (READ_LE_UINT16(sampleData + 0x16) == 2) + flags |= Audio::Mixer::FLAG_STEREO; + if (_fxList[elem->id].type == FX_LOOP) + flags |= Audio::Mixer::FLAG_LOOP; + _mixer->playRaw(Audio::Mixer::kSFXSoundType, &elem->handle, sampleData + 0x2C, size, 11025, flags, elem->id, volume, pan); + } } } else break; @@ -187,11 +195,69 @@ bool Sound::startSpeech(uint16 roomNo, uint16 localNo) { warning("Sound::startSpeech: COW file isn't open"); return false; } + + uint32 locIndex = 0xFFFFFFFF; + uint32 sampleSize = 0; + uint32 index = 0; - uint32 locIndex = _cowHeader[roomNo] >> 2; - uint32 sampleSize = _cowHeader[locIndex + (localNo * 2)]; - uint32 index = _cowHeader[locIndex + (localNo * 2) - 1]; + if (_cowMode == CowPSX) { + Common::File file; + uint16 i; + + if (!file.open("speech.lis")) { + warning ("Could not open speech.lis"); + return false; + } + + for (i = 0; !file.eos() && !file.err(); i++) + if (file.readUint16LE() == roomNo) { + locIndex = i; + break; + } + file.close(); + + if (locIndex == 0xFFFFFFFF) { + warning ("Could not find room %d in speech.lis", roomNo); + return false; + } + + if (!file.open("speech.inf")) { + warning ("Could not open speech.inf"); + return false; + } + + file.seek(locIndex * 4 + 2); // 4 bytes per room, skip first 2 bytes + + uint16 numLines = file.readUint16LE(); + uint16 roomOffset = file.readUint16LE(); + + file.seek(0x112 + roomOffset * 2); // The offset is in terms of uint16's, so multiply by 2. Skip the 0x112 byte header too. + + locIndex = 0xFFFFFFFF; + + for (i = 0; i < numLines; i++) + if (file.readUint16LE() == localNo) { + locIndex = i; + break; + } + + if (locIndex == 0xFFFFFFFF) { + warning ("Could not find local number %d in room %d in speech.inf", roomNo, localNo); + return false; + } + + file.close(); + + index = _cowHeader[(roomOffset + locIndex) * 2]; + sampleSize = _cowHeader[(roomOffset + locIndex) * 2 + 1]; + } else { + locIndex = _cowHeader[roomNo] >> 2; + sampleSize = _cowHeader[locIndex + (localNo * 2)]; + index = _cowHeader[locIndex + (localNo * 2) - 1]; + } + debug(6, "startSpeech(%d, %d): locIndex %d, sampleSize %d, index %d", roomNo, localNo, locIndex, sampleSize, index); + if (sampleSize) { uint8 speechVol = (_speechVolR + _speechVolL) / 2; int8 speechPan = (_speechVolR - _speechVolL) / 2; @@ -200,6 +266,14 @@ bool Sound::startSpeech(uint16 roomNo, uint16 localNo) { int16 *data = uncompressSpeech(index + _cowHeaderSize, sampleSize, &size); if (data) _mixer->playRaw(Audio::Mixer::kSpeechSoundType, &_speechHandle, data, size, 11025, SPEECH_FLAGS, SOUND_SPEECH_ID, speechVol, speechPan); + } else if (_cowMode == CowPSX && sampleSize != 0xffffffff) { + _cowFile.seek(index * 2048); + _mixer->playInputStream(Audio::Mixer::kSpeechSoundType, &_speechHandle, new VagStream(_cowFile.readStream(sampleSize)), SOUND_SPEECH_ID, speechVol, speechPan); + // with compressed audio, we can't calculate the wave volume. + // so default to talking. + for (int cnt = 0; cnt < 480; cnt++) + _waveVolume[cnt] = true; + _waveVolPos = 0; } #ifdef USE_FLAC else if (_cowMode == CowFlac) { @@ -419,21 +493,47 @@ void Sound::initCowSystem(void) { debug(1, "Using uncompressed Speech Cluster"); _cowMode = CowWave; } + + if (SwordEngine::isPsx()) { + // There's only one file on the PSX, so set it to the current disc. + _currentCowFile = SwordEngine::_systemVars.currentCD; + if (!_cowFile.isOpen()) { + if (!_cowFile.open("speech.dat")) + error ("Could not open speech.dat"); + _cowMode = CowPSX; + } + } + if (!_cowFile.isOpen()) _cowFile.open("speech.clu"); + if (!_cowFile.isOpen()) { _cowFile.open("cows.mad"); if (_cowFile.isOpen()) _cowMode = CowDemo; } + if (_cowFile.isOpen()) { - _cowHeaderSize = _cowFile.readUint32LE(); - _cowHeader = (uint32*)malloc(_cowHeaderSize); - if (_cowHeaderSize & 3) - error("Unexpected cow header size %d", _cowHeaderSize); - for (uint32 cnt = 0; cnt < (_cowHeaderSize / 4) - 1; cnt++) - _cowHeader[cnt] = _cowFile.readUint32LE(); - _currentCowFile = SwordEngine::_systemVars.currentCD; + if (SwordEngine::isPsx()) { + // Get data from the external table file + Common::File tableFile; + if (!tableFile.open("speech.tab")) + error ("Could not open speech.tab"); + _cowHeaderSize = tableFile.size(); + _cowHeader = (uint32 *)malloc(_cowHeaderSize); + if (_cowHeaderSize & 3) + error("Unexpected cow header size %d", _cowHeaderSize); + for (uint32 cnt = 0; cnt < _cowHeaderSize / 4; cnt++) + _cowHeader[cnt] = tableFile.readUint32LE(); + } else { + _cowHeaderSize = _cowFile.readUint32LE(); + _cowHeader = (uint32*)malloc(_cowHeaderSize); + if (_cowHeaderSize & 3) + error("Unexpected cow header size %d", _cowHeaderSize); + for (uint32 cnt = 0; cnt < (_cowHeaderSize / 4) - 1; cnt++) + _cowHeader[cnt] = _cowFile.readUint32LE(); + _currentCowFile = SwordEngine::_systemVars.currentCD; + } } else warning("Sound::initCowSystem: Can't open SPEECH%d.CLU", SwordEngine::_systemVars.currentCD); } diff --git a/engines/sword1/sound.h b/engines/sword1/sound.h index b04f6c2045..cdbdcdf6bb 100644 --- a/engines/sword1/sound.h +++ b/engines/sword1/sound.h @@ -69,7 +69,8 @@ enum CowMode { CowFlac, CowVorbis, CowMp3, - CowDemo + CowDemo, + CowPSX }; class Sound { diff --git a/engines/sword1/sword1.cpp b/engines/sword1/sword1.cpp index e0228b4c33..6c33dfab4d 100644 --- a/engines/sword1/sword1.cpp +++ b/engines/sword1/sword1.cpp @@ -84,14 +84,16 @@ Common::Error SwordEngine::init() { if ( 0 == scumm_stricmp(ConfMan.get("gameid").c_str(), "sword1mac") || 0 == scumm_stricmp(ConfMan.get("gameid").c_str(), "sword1macdemo") ) - _systemVars.isMac = true; + _systemVars.platform = Common::kPlatformMacintosh; + else if (0 == scumm_stricmp(ConfMan.get("gameid").c_str(), "sword1psx")) + _systemVars.platform = Common::kPlatformPSX; else - _systemVars.isMac = false; + _systemVars.platform = Common::kPlatformWindows; checkCdFiles(); debug(5, "Starting resource manager"); - _resMan = new ResMan("swordres.rif", _systemVars.isMac); + _resMan = new ResMan("swordres.rif", _systemVars.platform == Common::kPlatformMacintosh); debug(5, "Starting object manager"); _objectMan = new ObjectMan(_resMan); _mouse = new Mouse(_system, _resMan, _objectMan); @@ -308,12 +310,34 @@ const CdFile SwordEngine::_macCdFileList[] = { #endif }; +const CdFile SwordEngine::_psxCdFileList[] = { // PSX edition has only one cd + { "paris2.clu", FLAG_CD1 }, + { "ireland.clu", FLAG_CD1 }, + { "paris3.clu", FLAG_CD1 }, + { "paris4.clu", FLAG_CD1 }, + { "scotland.clu", FLAG_CD1 }, + { "spain.clu", FLAG_CD1 }, + { "syria.clu", FLAG_CD1 }, + { "train.clu", FLAG_CD1 }, + { "train.plx", FLAG_CD1 }, + { "compacts.clu", FLAG_CD1 | FLAG_IMMED }, + { "general.clu", FLAG_CD1 | FLAG_IMMED }, + { "maps.clu", FLAG_CD1 }, + { "paris1.clu", FLAG_CD1 }, + { "scripts.clu", FLAG_CD1 | FLAG_IMMED }, + { "swordres.rif", FLAG_CD1 | FLAG_IMMED }, + { "text.clu", FLAG_CD1 }, + { "speech.dat", FLAG_SPEECH1 }, + { "speech.tab", FLAG_SPEECH1 }, + { "speech.inf", FLAG_SPEECH1 }, + { "speech.lis", FLAG_SPEECH1 } +}; void SwordEngine::showFileErrorMsg(uint8 type, bool *fileExists) { char msg[1024]; int missCnt = 0, missNum = 0; - if (_systemVars.isMac) { + if (SwordEngine::isMac()) { for (int i = 0; i < ARRAYSIZE(_macCdFileList); i++) if (!fileExists[i]) { missCnt++; @@ -335,6 +359,27 @@ void SwordEngine::showFileErrorMsg(uint8 type, bool *fileExists) { pos += sprintf(pos, "\"%s\" (CD %d)\n", _macCdFileList[i].name, (_macCdFileList[i].flags & FLAG_CD2) ? 2 : 1); } } + } else if (SwordEngine::isPsx()) { + for (int i = 0; i < ARRAYSIZE(_psxCdFileList); i++) + if (!fileExists[i]) { + missCnt++; + missNum = i; + } + assert(missCnt > 0); // this function shouldn't get called if there's nothing missing. + warning("%d files missing", missCnt); + int msgId = (type == TYPE_IMMED) ? 0 : 2; + if (missCnt == 1) { + sprintf(msg, errorMsgs[msgId], _psxCdFileList[missNum].name, 1); + warning("%s", msg); + } else { + char *pos = msg + sprintf(msg, errorMsgs[msgId + 1], missCnt); + warning("%s", msg); + for (int i = 0; i < ARRAYSIZE(_psxCdFileList); i++) + if (!fileExists[i]) { + warning("\"%s\"", _macCdFileList[i].name); + pos += sprintf(pos, "\"%s\"\n", _macCdFileList[i].name); + } + } } else { for (int i = 0; i < ARRAYSIZE(_pcCdFileList); i++) if (!fileExists[i]) { @@ -374,7 +419,7 @@ void SwordEngine::checkCdFiles(void) { // check if we're running from cd, hdd or _systemVars.playSpeech = true; // check all files and look out if we can find a file that wouldn't exist if this was the demo version - if (_systemVars.isMac) { + if (SwordEngine::isMac()) { for (int fcnt = 0; fcnt < ARRAYSIZE(_macCdFileList); fcnt++) { if (Common::File::exists(_macCdFileList[fcnt].name)) { fileExists[fcnt] = true; @@ -388,6 +433,18 @@ void SwordEngine::checkCdFiles(void) { // check if we're running from cd, hdd or fileExists[fcnt] = false; } } + } else if (SwordEngine::isPsx()) { + for (int fcnt = 0; fcnt < ARRAYSIZE(_psxCdFileList); fcnt++) { + if (Common::File::exists(_psxCdFileList[fcnt].name)) { + fileExists[fcnt] = true; + flagsToBool(foundTypes, _psxCdFileList[fcnt].flags); + isFullVersion = true; + cd2FilesFound = true; + } else { + flagsToBool(missingTypes, _psxCdFileList[fcnt].flags); + fileExists[fcnt] = false; + } + } } else { for (int fcnt = 0; fcnt < ARRAYSIZE(_pcCdFileList); fcnt++) { if (Common::File::exists(_pcCdFileList[fcnt].name)) { @@ -422,7 +479,7 @@ void SwordEngine::checkCdFiles(void) { // check if we're running from cd, hdd or somethingMissing |= missingTypes[i]; if (somethingMissing) { // okay, there *are* files missing // first, update the fileExists[] array depending on our changed missingTypes - if (_systemVars.isMac) { + if (SwordEngine::isMac()) { for (int fileCnt = 0; fileCnt < ARRAYSIZE(_macCdFileList); fileCnt++) if (!fileExists[fileCnt]) { fileExists[fileCnt] = true; @@ -430,6 +487,14 @@ void SwordEngine::checkCdFiles(void) { // check if we're running from cd, hdd or if (missingTypes[flagCnt] && ((_macCdFileList[fileCnt].flags & (1 << flagCnt)) != 0)) fileExists[fileCnt] = false; // this is one of the files we were looking for } + } else if (SwordEngine::isPsx()) { + for (int fileCnt = 0; fileCnt < ARRAYSIZE(_psxCdFileList); fileCnt++) + if (!fileExists[fileCnt]) { + fileExists[fileCnt] = true; + for (int flagCnt = 0; flagCnt < 8; flagCnt++) + if (missingTypes[flagCnt] && ((_psxCdFileList[fileCnt].flags & (1 << flagCnt)) != 0)) + fileExists[fileCnt] = false; // this is one of the files we were looking for + } } else { for (int fileCnt = 0; fileCnt < ARRAYSIZE(_pcCdFileList); fileCnt++) if (!fileExists[fileCnt]) { diff --git a/engines/sword1/sword1.h b/engines/sword1/sword1.h index ca5c958bf8..775eb33749 100644 --- a/engines/sword1/sword1.h +++ b/engines/sword1/sword1.h @@ -66,7 +66,7 @@ struct SystemVars { uint8 showText; uint8 language; bool isDemo; - bool isMac; + Common::Platform platform; }; class SwordEngine : public Engine { @@ -79,6 +79,9 @@ public: uint32 _features; bool mouseIsActive(); + + static bool isMac() { return _systemVars.platform == Common::kPlatformMacintosh; } + static bool isPsx() { return _systemVars.platform == Common::kPlatformPSX; } protected: // Engine APIs @@ -119,6 +122,7 @@ private: static const uint8 _cdList[TOTAL_SECTIONS]; static const CdFile _pcCdFileList[]; static const CdFile _macCdFileList[]; + static const CdFile _psxCdFileList[]; }; } // End of namespace Sword1 diff --git a/engines/sword1/text.cpp b/engines/sword1/text.cpp index 499133dd97..fd9b9dea28 100644 --- a/engines/sword1/text.cpp +++ b/engines/sword1/text.cpp @@ -32,6 +32,8 @@ #include "sword1/objectman.h" #include "sword1/swordres.h" #include "sword1/sworddefs.h" +#include "sword1/screen.h" +#include "sword1/sword1.h" namespace Sword1 { @@ -78,12 +80,13 @@ uint32 Text::lowTextManager(uint8 *ascii, int32 width, uint8 pen) { void Text::makeTextSprite(uint8 slot, uint8 *text, uint16 maxWidth, uint8 pen) { LineInfo lines[MAX_LINES]; uint16 numLines = analyzeSentence(text, maxWidth, lines); - + uint16 sprWidth = 0; uint16 lineCnt; for (lineCnt = 0; lineCnt < numLines; lineCnt++) if (lines[lineCnt].width > sprWidth) sprWidth = lines[lineCnt].width; + uint16 sprHeight = _charHeight * numLines; uint32 sprSize = sprWidth * sprHeight; assert(!_textBlocks[slot]); // if this triggers, the speechDriver failed to call Text::releaseText. @@ -100,10 +103,13 @@ void Text::makeTextSprite(uint8 slot, uint8 *text, uint16 maxWidth, uint8 pen) { memset(linePtr, NO_COL, sprSize); for (lineCnt = 0; lineCnt < numLines; lineCnt++) { uint8 *sprPtr = linePtr + (sprWidth - lines[lineCnt].width) / 2; // center the text - for (uint16 pos = 0; pos < lines[lineCnt].length; pos++) + for (uint16 pos = 0; pos < lines[lineCnt].length; pos++) sprPtr += copyChar(*text++, sprPtr, sprWidth, pen) - OVERLAP; text++; // skip space at the end of the line - linePtr += _charHeight * sprWidth; + if(SwordEngine::isPsx()) //Chars are half height in psx version + linePtr += (_charHeight / 2) * sprWidth; + else + linePtr += _charHeight * sprWidth; } } @@ -157,16 +163,34 @@ uint16 Text::copyChar(uint8 ch, uint8 *sprPtr, uint16 sprWidth, uint8 pen) { FrameHeader *chFrame = _resMan->fetchFrame(_font, ch - SPACE); uint8 *chData = ((uint8*)chFrame) + sizeof(FrameHeader); uint8 *dest = sprPtr; - for (uint16 cnty = 0; cnty < _resMan->getUint16(chFrame->height); cnty++) { + uint8 *decBuf = NULL; + uint8 *decChr; + uint16 frameHeight = 0; + + if(SwordEngine::isPsx()) { + frameHeight = _resMan->getUint16(chFrame->height)/2; + if(_fontId == CZECH_GAME_FONT) { //Czech game fonts are compressed + decBuf = (uint8*) malloc((_resMan->getUint16(chFrame->width))*(_resMan->getUint16(chFrame->height)/2)); + Screen::decompressHIF(chData, decBuf); + decChr = decBuf; + } else //Normal game fonts are not compressed + decChr = chData; + } else { + frameHeight = _resMan->getUint16(chFrame->height); + decChr = chData; + } + + for (uint16 cnty = 0; cnty < frameHeight; cnty++) { for (uint16 cntx = 0; cntx < _resMan->getUint16(chFrame->width); cntx++) { - if (*chData == LETTER_COL) + if (*decChr == LETTER_COL) dest[cntx] = pen; - else if ((*chData == BORDER_COL) && (!dest[cntx])) // don't do a border if there's already a color underneath (chars can overlap) + else if (((*decChr == BORDER_COL) || (*decChr == BORDER_COL_PSX)) && (!dest[cntx])) // don't do a border if there's already a color underneath (chars can overlap) dest[cntx] = BORDER_COL; - chData++; + decChr++; } dest += sprWidth; } + free(decBuf); return _resMan->getUint16(chFrame->width); } diff --git a/engines/sword1/text.h b/engines/sword1/text.h index 6a15585148..803d8c3fd0 100644 --- a/engines/sword1/text.h +++ b/engines/sword1/text.h @@ -34,6 +34,7 @@ namespace Sword1 { #define MAX_TEXT_OBS 3 #define BORDER_COL 200 +#define BORDER_COL_PSX 199 #define LETTER_COL 193 #define NO_COL 0 // sprite background - 0 for transparency -- cgit v1.2.3