aboutsummaryrefslogtreecommitdiff
path: root/engines/sword1
diff options
context:
space:
mode:
authorEugene Sandulenko2009-02-28 10:46:33 +0000
committerEugene Sandulenko2009-02-28 10:46:33 +0000
commit5aa50ec889109864cbdd13298b4026c8ed78f484 (patch)
treef5cdded9d2cd6e5742152df99d176641551b03de /engines/sword1
parentc62834cc0c4033acb2d37a65dbcd6a998b88cc76 (diff)
downloadscummvm-rg350-5aa50ec889109864cbdd13298b4026c8ed78f484.tar.gz
scummvm-rg350-5aa50ec889109864cbdd13298b4026c8ed78f484.tar.bz2
scummvm-rg350-5aa50ec889109864cbdd13298b4026c8ed78f484.zip
Patch #2638336: Broken Sword PSX Support
svn-id: r38956
Diffstat (limited to 'engines/sword1')
-rw-r--r--engines/sword1/control.cpp127
-rw-r--r--engines/sword1/detection.cpp25
-rw-r--r--engines/sword1/logic.cpp4
-rw-r--r--engines/sword1/module.mk3
-rw-r--r--engines/sword1/mouse.cpp61
-rw-r--r--engines/sword1/music.cpp43
-rw-r--r--engines/sword1/music.h1
-rw-r--r--engines/sword1/screen.cpp539
-rw-r--r--engines/sword1/screen.h9
-rw-r--r--engines/sword1/sound.cpp144
-rw-r--r--engines/sword1/sound.h3
-rw-r--r--engines/sword1/sword1.cpp77
-rw-r--r--engines/sword1/sword1.h6
-rw-r--r--engines/sword1/text.cpp38
-rw-r--r--engines/sword1/text.h1
15 files changed, 947 insertions, 134 deletions
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