Patch #2638336: Broken Sword PSX Support

svn-id: r38956
This commit is contained in:
Eugene Sandulenko 2009-02-28 10:46:33 +00:00
parent c62834cc0c
commit 5aa50ec889
17 changed files with 949 additions and 134 deletions

View File

@ -271,6 +271,7 @@ const PlatformDescription g_platforms[] = {
{"nes", "nes", "nes", "NES", kPlatformNES},
{"segacd", "segacd", "sega", "SegaCD", kPlatformSegaCD},
{"windows", "win", "win", "Windows", kPlatformWindows},
{"playstation", "psx", "PSX", "Playstation", kPlatformPSX},
{0, 0, 0, "Default", kPlatformUnknown}

View File

@ -201,6 +201,7 @@ enum Platform {
kPlatformApple2GS,
kPlatformPC98,
kPlatformWii,
kPlatformPSX,
kPlatformUnknown = -1
};

View File

@ -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);
}
}

View File

@ -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);

View File

@ -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

View File

@ -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)

View File

@ -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);
}

View File

@ -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();

View File

@ -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();

View File

@ -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);

View File

@ -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

View File

@ -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);
}

View File

@ -69,7 +69,8 @@ enum CowMode {
CowFlac,
CowVorbis,
CowMp3,
CowDemo
CowDemo,
CowPSX
};
class Sound {

View File

@ -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]) {

View File

@ -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

View File

@ -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);
}

View File

@ -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