[Scummvm-cvs-logs] SF.net SVN: scummvm:[39896] scummvm/trunk/engines/sword2

Hkz at users.sourceforge.net Hkz at users.sourceforge.net
Tue Apr 7 21:52:46 CEST 2009


Revision: 39896
          http://scummvm.svn.sourceforge.net/scummvm/?rev=39896&view=rev
Author:   Hkz
Date:     2009-04-07 19:52:46 +0000 (Tue, 07 Apr 2009)

Log Message:
-----------
Sword2: PSX version support, and GMM loading/saving

Modified Paths:
--------------
    scummvm/trunk/engines/sword2/animation.cpp
    scummvm/trunk/engines/sword2/anims.cpp
    scummvm/trunk/engines/sword2/console.cpp
    scummvm/trunk/engines/sword2/controls.cpp
    scummvm/trunk/engines/sword2/function.cpp
    scummvm/trunk/engines/sword2/header.h
    scummvm/trunk/engines/sword2/icons.cpp
    scummvm/trunk/engines/sword2/layers.cpp
    scummvm/trunk/engines/sword2/logic.cpp
    scummvm/trunk/engines/sword2/logic.h
    scummvm/trunk/engines/sword2/maketext.cpp
    scummvm/trunk/engines/sword2/menu.cpp
    scummvm/trunk/engines/sword2/mouse.cpp
    scummvm/trunk/engines/sword2/mouse.h
    scummvm/trunk/engines/sword2/music.cpp
    scummvm/trunk/engines/sword2/palette.cpp
    scummvm/trunk/engines/sword2/protocol.cpp
    scummvm/trunk/engines/sword2/render.cpp
    scummvm/trunk/engines/sword2/resman.cpp
    scummvm/trunk/engines/sword2/resman.h
    scummvm/trunk/engines/sword2/screen.cpp
    scummvm/trunk/engines/sword2/screen.h
    scummvm/trunk/engines/sword2/sound.cpp
    scummvm/trunk/engines/sword2/sound.h
    scummvm/trunk/engines/sword2/sprite.cpp
    scummvm/trunk/engines/sword2/sword2.cpp
    scummvm/trunk/engines/sword2/sword2.h

Modified: scummvm/trunk/engines/sword2/animation.cpp
===================================================================
--- scummvm/trunk/engines/sword2/animation.cpp	2009-04-07 17:43:49 UTC (rev 39895)
+++ scummvm/trunk/engines/sword2/animation.cpp	2009-04-07 19:52:46 UTC (rev 39896)
@@ -35,6 +35,7 @@
 #include "sword2/maketext.h"
 #include "sword2/resman.h"
 #include "sword2/sound.h"
+#include "sword2/screen.h"
 #include "sword2/animation.h"
 
 #include "gui/message.h"
@@ -207,6 +208,7 @@
 		text->_textSprite.h = frame.height;
 		text->_textSprite.type = RDSPR_DISPLAYALIGN | RDSPR_NOCOMPRESSION;
 		text->_textSprite.data = text->_textMem + FrameHeader::size();
+		text->_textSprite.isText = true;
 		_vm->_screen->createSurface(&text->_textSprite, &_textSurface);
 
 		_textX = 320 - text->_textSprite.w / 2;
@@ -239,6 +241,14 @@
 		uint16 width = text->_textSprite.w;
 		uint16 height = text->_textSprite.h;
 
+		// Resize text sprites for PSX version
+		if (Sword2Engine::isPsx()) { 
+			height *= 2;
+			byte *buffer = (byte *)malloc(width * height);
+			Screen::resizePsxSprite(buffer, src, width, height);
+			src = buffer;
+		}
+
 		byte *dst = screen + _textY * _decoder->getWidth() + _textX;
 
 		for (int y = 0; y < height; y++) {

Modified: scummvm/trunk/engines/sword2/anims.cpp
===================================================================
--- scummvm/trunk/engines/sword2/anims.cpp	2009-04-07 17:43:49 UTC (rev 39895)
+++ scummvm/trunk/engines/sword2/anims.cpp	2009-04-07 19:52:46 UTC (rev 39896)
@@ -103,7 +103,7 @@
 
 		// point to anim header
 		anim_head.read(_vm->fetchAnimHeader(anim_file));
-
+		
 		// now running an anim, looping back to this call again
 		obLogic.setLooping(1);
 		obGraph.setAnimResource(animRes);

Modified: scummvm/trunk/engines/sword2/console.cpp
===================================================================
--- scummvm/trunk/engines/sword2/console.cpp	2009-04-07 17:43:49 UTC (rev 39895)
+++ scummvm/trunk/engines/sword2/console.cpp	2009-04-07 19:52:46 UTC (rev 39896)
@@ -274,7 +274,7 @@
 
 bool Debugger::Cmd_ResList(int argc, const char **argv) {
 	// By default, list only resources that are being held open.
-	uint minCount = 1;
+	uint32 minCount = 1;
 
 	if (argc > 1)
 		minCount = atoi(argv[1]);

Modified: scummvm/trunk/engines/sword2/controls.cpp
===================================================================
--- scummvm/trunk/engines/sword2/controls.cpp	2009-04-07 17:43:49 UTC (rev 39895)
+++ scummvm/trunk/engines/sword2/controls.cpp	2009-04-07 19:52:46 UTC (rev 39896)
@@ -268,6 +268,13 @@
 
 	// Usually the mouse pointer will already be "normal", but not always.
 	_vm->_mouse->setMouse(NORMAL_MOUSE_ID);
+
+	// Force mouse mode as system menu: normally not needed, 
+	// but value is not correct in case of game start dialog 
+	// (when asking to restart or load a game). 
+	// This is forced to avoid GMM loading/saving being enabled
+	// during initial dialog.
+	_vm->_mouse->setMouseMode(MOUSE_system_menu);
 }
 
 Dialog::~Dialog() {

Modified: scummvm/trunk/engines/sword2/function.cpp
===================================================================
--- scummvm/trunk/engines/sword2/function.cpp	2009-04-07 17:43:49 UTC (rev 39895)
+++ scummvm/trunk/engines/sword2/function.cpp	2009-04-07 19:52:46 UTC (rev 39896)
@@ -71,8 +71,11 @@
 
 	// params:	0 res id of normal background layer - cannot be 0
 	//		1 1 yes 0 no for a new palette
-
-	_vm->_screen->initBackground(params[0], params[1]);
+	
+	if (Sword2Engine::isPsx())
+		_vm->_screen->initPsxBackground(params[0], params[1]);
+	else
+		_vm->_screen->initBackground(params[0], params[1]);
 	return IR_CONT;
 }
 
@@ -392,13 +395,13 @@
 	assert(_vm->_resman->fetchType(res) == ANIMATION_FILE);
 
 	// set up pointer to the animation header
-	AnimHeader anim_head;
-
+	AnimHeader anim_head;	
+	
 	anim_head.read(_vm->fetchAnimHeader(anim_file));
-
+	
 	// set up anim resource in graphic object
 	ObjectGraphic obGraph(decodePtr(params[0]));
-
+	
 	obGraph.setAnimResource(res);
 	obGraph.setAnimPc(params[2] ? anim_head.noAnimFrames - 1 : 0);
 

Modified: scummvm/trunk/engines/sword2/header.h
===================================================================
--- scummvm/trunk/engines/sword2/header.h	2009-04-07 17:43:49 UTC (rev 39895)
+++ scummvm/trunk/engines/sword2/header.h	2009-04-07 19:52:46 UTC (rev 39896)
@@ -159,15 +159,27 @@
 	void read(byte *addr) {
 		Common::MemoryReadStream readS(addr, size());
 
-		runTimeComp = readS.readByte();
-		noAnimFrames = readS.readUint16LE();
-		feetStartX = readS.readUint16LE();
-		feetStartY = readS.readUint16LE();
-		feetStartDir = readS.readByte();
-		feetEndX = readS.readUint16LE();
-		feetEndY = readS.readUint16LE();
-		feetEndDir = readS.readByte();
-		blend = readS.readUint16LE();
+		if (Sword2Engine::isPsx()) {
+			noAnimFrames = readS.readUint16LE();
+			feetStartX = readS.readUint16LE();
+			feetStartY = readS.readUint16LE();
+			feetEndX = readS.readUint16LE();
+			feetEndY = readS.readUint16LE();
+			blend = readS.readUint16LE();
+			runTimeComp = readS.readByte();
+			feetStartDir = readS.readByte();
+			feetEndDir = readS.readByte();	
+		} else {
+			runTimeComp = readS.readByte();
+			noAnimFrames = readS.readUint16LE();
+			feetStartX = readS.readUint16LE();
+			feetStartY = readS.readUint16LE();
+			feetStartDir = readS.readByte();
+			feetEndX = readS.readUint16LE();
+			feetEndY = readS.readUint16LE();
+			feetEndDir = readS.readByte();
+			blend = readS.readUint16LE();
+		}
 	}
 
 	void write(byte *addr) {
@@ -210,16 +222,27 @@
 				// corner at (x,y), otherwise see below...
 
 	static int size() {
-		return 9;
+		if (Sword2Engine::isPsx())
+			return 12;
+		else
+			return 9;
 	}
 
 	void read(byte *addr) {
 		Common::MemoryReadStream readS(addr, size());
 
-		x = readS.readUint16LE();
-		y = readS.readUint16LE();
-		frameOffset = readS.readUint32LE();
-		frameType = readS.readByte();
+		if (Sword2Engine::isPsx()) {
+			readS.readByte(); // Skip a byte in psx version
+			x = readS.readUint16LE();
+			y = readS.readUint16LE();
+			frameOffset = readS.readUint32LE();
+			frameType = readS.readByte();
+		} else {
+			x = readS.readUint16LE();
+			y = readS.readUint16LE();
+			frameOffset = readS.readUint32LE();
+			frameType = readS.readByte();
+		}
 	}
 
 	void write(byte *addr) {
@@ -260,6 +283,11 @@
 		compSize = readS.readUint32LE();
 		width = readS.readUint16LE();
 		height = readS.readUint16LE();
+
+		if (Sword2Engine::isPsx()) { // In PSX version, frames are half height
+			height *= 2;
+			width = (width % 2) ? width + 1 : width;
+		}
 	}
 
 	void write(byte *addr) {
@@ -504,6 +532,108 @@
 //	line of text,0
 //	line of text,0
 
+//----------------------------------------------------------
+// SCREENS.CLU file
+//----------------------------------------------------------
+// This file is present in PSX version of the game only.
+// It keeps parallax and background images, aligned at 1024 bytes
+// for faster access by the psx cd drive.
+//
+// SCREENS.CLU structure:
+// In first 2048 Bytes there's an offset table. Each entry is an
+// 32bit offset for a background/parallax group. If entry is 0, screen
+// does not exist.
+// To find matching screen for the location, you must count LOCATION_NO
+// words and then go to the corresponding offset indicated by last 32bit
+// word.
+// Each screen then begins with a PSXScreensEntry entry:
+
+struct PSXScreensEntry {
+	uint16 fgPlxXres; // If these values are 0, subsequent fgPlx* values must be
+	uint16 fgPlxYres; // ignored, as this means foreground parallax is not present.
+	uint32 fgPlxOffset; // This offset is relative, counting from the beginning of Resource Header
+	uint32 fgPlxSize; // Size of parallax, the size is aligned at 1024 bytes.
+					  // fgPlxSize/1024 gives number of sector the parallax is divided into.
+	uint16 bgXres;
+	uint16 bgYres;
+	uint32 bgOffset; // relative
+	uint32 bgSize;
+	uint16 bgPlxXres; // same considerations for fg parallaxes apply
+	uint16 bgPlxYres;
+	uint32 bgPlxOffset; // relative
+	uint32 bgPlxSize;
+
+	static int size() {
+		return 36;
+	}
+
+	void read(byte *addr) {
+		Common::MemoryReadStream readS(addr, size());
+
+		bgPlxXres = readS.readUint16LE();
+		bgPlxYres = readS.readUint16LE();
+		bgPlxOffset = readS.readUint32LE();
+		bgPlxSize = readS.readUint32LE();
+		bgXres = readS.readUint16LE();
+		bgYres = readS.readUint16LE();
+		bgOffset = readS.readUint32LE();
+		bgSize = readS.readUint32LE();
+		fgPlxXres = readS.readUint16LE();
+		fgPlxYres = readS.readUint16LE();
+		fgPlxOffset = readS.readUint32LE();
+		fgPlxSize = readS.readUint32LE();
+	}
+
+	void write(byte *addr) {
+		Common::MemoryWriteStream writeS(addr, size());
+
+		writeS.writeUint16LE(bgPlxXres);
+		writeS.writeUint16LE(bgPlxYres);
+		writeS.writeUint32LE(bgPlxOffset);
+		writeS.writeUint32LE(bgPlxSize);
+		writeS.writeUint16LE(bgXres);
+		writeS.writeUint16LE(bgYres);
+		writeS.writeUint32LE(bgOffset);
+		writeS.writeUint32LE(bgSize);
+		writeS.writeUint16LE(fgPlxXres);
+		writeS.writeUint16LE(fgPlxYres);
+		writeS.writeUint32LE(fgPlxOffset);
+		writeS.writeUint32LE(fgPlxSize);
+	}
+};
+
+// PSXFontEntry is present in font resource file, it is used
+// to address a single char in the character atlas image.
+
+struct PSXFontEntry {
+	uint16 offset;
+	uint16 skipLines;
+	uint16 charWidth;
+	uint16 charHeight;
+
+	static int size() {
+		return 8;
+	}
+
+	void read(byte *addr) {
+		Common::MemoryReadStream readS(addr, size());
+
+		offset = readS.readUint16LE() / 2;
+		skipLines = readS.readUint16LE();
+		charWidth = readS.readUint16LE() / 2;
+		charHeight = readS.readUint16LE();
+	}
+
+	void write(byte *addr) {
+		Common::MemoryWriteStream writeS(addr, size());
+
+		writeS.writeUint16LE(offset);
+		writeS.writeUint16LE(skipLines);
+		writeS.writeUint16LE(charWidth);
+		writeS.writeUint16LE(charHeight);
+	}	
+};
+
 } // End of namespace Sword2
 
 #endif

Modified: scummvm/trunk/engines/sword2/icons.cpp
===================================================================
--- scummvm/trunk/engines/sword2/icons.cpp	2009-04-07 17:43:49 UTC (rev 39895)
+++ scummvm/trunk/engines/sword2/icons.cpp	2009-04-07 19:52:46 UTC (rev 39896)
@@ -77,7 +77,13 @@
 
 void Mouse::buildMenu() {
 	uint32 i, j;
+	byte menuIconWidth;
 
+	if (Sword2Engine::isPsx())
+		menuIconWidth = RDMENU_PSXICONWIDE;
+	else
+		menuIconWidth = RDMENU_ICONWIDE;
+
 	// Clear the temporary inventory list, since we are going to build a
 	// new one from scratch.
 
@@ -178,7 +184,7 @@
 			// greyed out one.
 
 			if (icon_coloured)
-				icon += (RDMENU_ICONWIDE * RDMENU_ICONDEEP);
+				icon += (menuIconWidth * RDMENU_ICONDEEP);
 		}
 
 		setMenuIcon(RDMENU_BOTTOM, i, icon);
@@ -203,6 +209,13 @@
 		RESTART_ICON
 	};
 
+	byte menuIconWidth;
+
+	if (Sword2Engine::isPsx())
+		menuIconWidth = RDMENU_PSXICONWIDE;
+	else
+		menuIconWidth = RDMENU_ICONWIDE;
+
 	// Build them all high in full colour - when one is clicked on all the
 	// rest will grey out.
 
@@ -213,7 +226,7 @@
 		// is dead. Then SAVE is not available.
 
 		if (!_vm->_logic->readVar(DEAD) || icon_list[i] != SAVE_ICON)
-			icon += (RDMENU_ICONWIDE * RDMENU_ICONDEEP);
+			icon += (menuIconWidth * RDMENU_ICONDEEP);
 
 		setMenuIcon(RDMENU_TOP, i, icon);
 		_vm->_resman->closeResource(icon_list[i]);

Modified: scummvm/trunk/engines/sword2/layers.cpp
===================================================================
--- scummvm/trunk/engines/sword2/layers.cpp	2009-04-07 17:43:49 UTC (rev 39895)
+++ scummvm/trunk/engines/sword2/layers.cpp	2009-04-07 19:52:46 UTC (rev 39896)
@@ -179,7 +179,7 @@
 	}
 
 	// Background parallax layers
-
+	
 	for (i = 0; i < 2; i++) {
 		if (screenLayerTable.bg_parallax[i])
 			initialiseBackgroundLayer(_vm->fetchBackgroundParallaxLayer(file, i));
@@ -188,11 +188,11 @@
 	}
 
 	// Normal backround layer
-
+	
 	initialiseBackgroundLayer(_vm->fetchBackgroundLayer(file));
 
 	// Foreground parallax layers
-
+	
 	for (i = 0; i < 2; i++) {
 		if (screenLayerTable.fg_parallax[i])
 			initialiseBackgroundLayer(_vm->fetchForegroundParallaxLayer(file, i));
@@ -203,4 +203,96 @@
 	_vm->_resman->closeResource(_thisScreen.background_layer_id);
 }
 
+/**
+ * This function is called when entering a new room, PSX edition
+ * @param res resource id of the normal background layer
+ * @param new_palette 1 for new palette, otherwise 0
+ */
+
+void Screen::initPsxBackground(int32 res, int32 new_palette) {
+	int i;
+
+	assert(res);
+
+	_vm->_sound->clearFxQueue(false);
+	waitForFade();
+
+	debug(1, "CHANGED TO LOCATION \"%s\"", _vm->_resman->fetchName(res));
+
+	_vm->_logic->writeVar(EXIT_CLICK_ID, 0);
+
+	// Close the previous screen, if one is open
+	if (_thisScreen.background_layer_id)
+		closeBackgroundLayer();
+
+	_thisScreen.background_layer_id = res;
+	_thisScreen.new_palette = new_palette;
+
+	// ok, now read the resource and pull out all the normal sort layer
+	// info/and set them up at the beginning of the sort list - why do it
+	// each cycle
+
+	byte *file = _vm->_resman->openResource(_thisScreen.background_layer_id);
+	ScreenHeader screen_head;
+
+	screen_head.read(_vm->fetchScreenHeader(file));
+	screen_head.height *= 2;
+
+	// set number of special sort layers
+	_thisScreen.number_of_layers = screen_head.noLayers;
+	_thisScreen.screen_wide = screen_head.width;
+	_thisScreen.screen_deep = screen_head.height;
+
+	debug(2, "layers=%d width=%d depth=%d", screen_head.noLayers, screen_head.width, screen_head.height);
+
+	// initialise the driver back buffer
+	setLocationMetrics(screen_head.width, screen_head.height);
+
+	for (i = 0; i < screen_head.noLayers; i++) {
+		debug(3, "init layer %d", i);
+
+		LayerHeader layer;
+
+		layer.read(_vm->fetchLayerHeader(file, i));
+		_sortList[i].layer_number = i + 1;
+		_sortList[i].sort_y = layer.y + layer.height;
+	}
+
+	// reset scroll offsets
+	_thisScreen.scroll_offset_x = 0;
+	_thisScreen.scroll_offset_y = 0;
+
+	if (screen_head.width > _screenWide || screen_head.height > _screenDeep) {
+		_thisScreen.scroll_flag = 2;
+
+		_thisScreen.max_scroll_offset_x = screen_head.width - _screenWide;
+		_thisScreen.max_scroll_offset_y = screen_head.height - (_screenDeep - (MENUDEEP * 2));
+	} else {
+		// The later fits on the phyiscal screen. Switch off scrolling.
+		_thisScreen.scroll_flag = 0;
+	}
+
+	resetRenderEngine();
+
+	// These are the physical screen coords where the system will try to
+	// maintain George's actual feet coords.
+
+	_thisScreen.feet_x = 320;
+	_thisScreen.feet_y = 340;
+
+	// Background parallax layers
+	initialisePsxParallaxLayer(_vm->fetchBackgroundParallaxLayer(file, 0));
+	initialisePsxParallaxLayer(NULL);
+
+	// Normal backround layer
+	initialisePsxBackgroundLayer(_vm->fetchBackgroundLayer(file));
+
+	// Foreground parallax layers
+	initialisePsxParallaxLayer(_vm->fetchForegroundParallaxLayer(file, 1));
+	initialisePsxParallaxLayer(NULL);
+
+	_vm->_resman->closeResource(_thisScreen.background_layer_id);
+
+}
+
 } // End of namespace Sword2

Modified: scummvm/trunk/engines/sword2/logic.cpp
===================================================================
--- scummvm/trunk/engines/sword2/logic.cpp	2009-04-07 17:43:49 UTC (rev 39895)
+++ scummvm/trunk/engines/sword2/logic.cpp	2009-04-07 19:52:46 UTC (rev 39896)
@@ -284,4 +284,12 @@
 		_moviePlayer->pauseMovie(pause);
 }
 
+/**
+ * Read current location number from script vars
+ */
+
+uint32 Logic::getLocationNum() {
+	return readVar(LOCATION);
+}
+
 } // End of namespace Sword2

Modified: scummvm/trunk/engines/sword2/logic.h
===================================================================
--- scummvm/trunk/engines/sword2/logic.h	2009-04-07 17:43:49 UTC (rev 39895)
+++ scummvm/trunk/engines/sword2/logic.h	2009-04-07 19:52:46 UTC (rev 39896)
@@ -314,6 +314,9 @@
 	void resetKillList();
 
 	void pauseMovie(bool pause);
+
+	// Read location number from script vars
+	uint32 getLocationNum();
 };
 
 } // End of namespace Sword2

Modified: scummvm/trunk/engines/sword2/maketext.cpp
===================================================================
--- scummvm/trunk/engines/sword2/maketext.cpp	2009-04-07 17:43:49 UTC (rev 39895)
+++ scummvm/trunk/engines/sword2/maketext.cpp	2009-04-07 19:52:46 UTC (rev 39896)
@@ -59,8 +59,11 @@
 #define MAX_LINES	30	// max character lines in output sprite
 
 #define BORDER_COL	200	// source colour for character border (only
-				// needed for remapping colours)
+						// needed for remapping colours)
+
 #define LETTER_COL	193	// source colour for bulk of character ( " )
+#define LETTER_COL_PSX1 33
+#define LETTER_COL_PSX2 34
 #define SPACE		' '
 #define FIRST_CHAR	SPACE	// first character in character set
 #define LAST_CHAR	255	// last character in character set
@@ -91,7 +94,10 @@
 	// of the resource.
 
 	if (fontRes == _vm->_speechFontId) {
-		_lineSpacing = -6;
+		if (Sword2Engine::isPsx())
+			_lineSpacing = -4; // Text would be unreadable with psx font if linespacing is higher
+		else
+			_lineSpacing = -6;
 		_charSpacing = -3;
 	} else if (fontRes == CONSOLE_FONT_ID) {
 		_lineSpacing = 0;
@@ -214,6 +220,13 @@
 		if (line[i].width > spriteWidth)
 			spriteWidth = line[i].width;
 
+
+	// Check that text sprite has even horizontal resolution in PSX version
+	// (needed to work around a problem in some sprites, which reports an odd
+	// number as horiz resolution, but then have the next even number as true width)
+	if (Sword2Engine::isPsx()) 
+		spriteWidth = (spriteWidth % 2) ? spriteWidth + 1 : spriteWidth;
+
 	// Find the total height of the text sprite: the total height of the
 	// text lines, plus the total height of the spacing between them.
 
@@ -234,6 +247,14 @@
 	frame_head.width = spriteWidth;
 	frame_head.height = spriteHeight;
 
+	// Normally for PSX frame header we double the height
+	// of the sprite artificially to regain correct aspect
+	// ratio, but this is an "artificially generated" text
+	// sprite, which gets created with correct aspect, so
+	// fix the height.
+	if (Sword2Engine::isPsx())
+		frame_head.height /= 2;
+
 	frame_head.write(textSprite);
 
 	debug(4, "Text sprite size: %ux%u", spriteWidth, spriteHeight);
@@ -264,13 +285,23 @@
 
 			assert(frame_head.height == char_height);
 			copyChar(charPtr, spritePtr, spriteWidth, pen);
+
+			// We must remember to free memory for generated character in psx,
+			// as it is extracted differently than pc version (copyed from a
+			// char atlas).
+			if (Sword2Engine::isPsx())
+				free(charPtr);
+
 			spritePtr += frame_head.width + _charSpacing;
 		}
 
 		// Skip space at end of last word in this line
 		pos++;
-
-		linePtr += (char_height + _lineSpacing) * spriteWidth;
+		
+		if (Sword2Engine::isPsx())
+			linePtr += (char_height / 2 + _lineSpacing) * spriteWidth;
+		else
+			linePtr += (char_height + _lineSpacing) * spriteWidth;
 	}
 
 	_vm->_resman->closeResource(fontRes);
@@ -286,10 +317,17 @@
 
 uint16 FontRenderer::charWidth(byte ch, uint32 fontRes) {
 	byte *charSet = _vm->_resman->openResource(fontRes);
+	byte *charBuf;
 
 	FrameHeader frame_head;
 
-	frame_head.read(findChar(ch, charSet));
+	charBuf = findChar(ch, charSet);
+
+	frame_head.read(charBuf);
+
+	if(Sword2Engine::isPsx())
+		free(charBuf);
+
 	_vm->_resman->closeResource(fontRes);
 
 	return frame_head.width;
@@ -307,10 +345,17 @@
 
 uint16 FontRenderer::charHeight(uint32 fontRes) {
 	byte *charSet = _vm->_resman->openResource(fontRes);
+	byte *charbuf;
 
 	FrameHeader frame_head;
 
-	frame_head.read(findChar(FIRST_CHAR, charSet));
+	charbuf = findChar(FIRST_CHAR, charSet);
+
+	frame_head.read(charbuf);
+
+	if(Sword2Engine::isPsx())
+		free(charbuf);
+
 	_vm->_resman->closeResource(fontRes);
 
 	return frame_head.height;
@@ -324,9 +369,77 @@
  */
 
 byte *FontRenderer::findChar(byte ch, byte *charSet) {
-	if (ch < FIRST_CHAR)
-		ch = DUD;
-	return _vm->fetchFrameHeader(charSet, ch - FIRST_CHAR);
+	
+	// PSX version doesn't use an animation table to keep all letters,
+	// instead a big sprite (char atlas) is used, and the single char
+	// must be extracted from that.
+	
+	if (Sword2Engine::isPsx()) {
+		byte *buffer;
+		PSXFontEntry header;
+		FrameHeader bogusHeader;
+
+		charSet += ResHeader::size() + 2;
+
+		if (ch < FIRST_CHAR)
+			ch = DUD;
+
+		// Read font entry of the corresponding char.
+		header.read(charSet + PSXFontEntry::size() * (ch - 32));
+
+		// We have no such character, generate an empty one
+		// on the fly, size 6x12.
+		if (header.charWidth == 0) {
+			
+			// Prepare a "bogus" FrameHeader to be returned with
+			// "empty" character data.
+			bogusHeader.compSize = 0;
+			bogusHeader.width = 6;
+			bogusHeader.height = 12;
+
+			buffer = (byte *)malloc(24 * 3 + FrameHeader::size());
+			memset(buffer, 0, 24 * 3 + FrameHeader::size());
+			bogusHeader.write(buffer);
+			
+			return buffer;
+		}
+
+		buffer = (byte *)malloc(FrameHeader::size() + header.charWidth * header.charHeight * 4);
+		byte *tempchar = (byte *)malloc(header.charWidth * header.charHeight);
+
+		// Prepare the "bogus" header to be returned with character
+		bogusHeader.compSize = 0;
+		bogusHeader.width = header.charWidth * 2;
+		bogusHeader.height = header.charHeight;
+
+		// Go to the beginning of char atlas
+		charSet += 2062;
+
+		memset(buffer, 0, FrameHeader::size() + header.charWidth * header.charHeight * 4);
+
+		bogusHeader.write(buffer);
+
+		// Copy and stretch the char into destination buffer
+		for (int idx = 0; idx < header.charHeight; idx++) {
+			memcpy(tempchar + header.charWidth * idx, charSet + header.offset + 128 * (header.skipLines + idx), header.charWidth);
+		}
+
+		for (int line = 0; line < header.charHeight; line++) {
+			for (int col = 0; col < header.charWidth; col++) {
+				*(buffer + FrameHeader::size() + line * bogusHeader.width + col * 2) = *(tempchar + line * header.charWidth + col);
+				*(buffer + FrameHeader::size() + line * bogusHeader.width + col * 2 + 1) = *(tempchar + line * header.charWidth + col);
+			}
+		}
+
+		free(tempchar);
+
+		return buffer;
+
+	} else {
+		if (ch < FIRST_CHAR)
+			ch = DUD;
+		return _vm->fetchFrameHeader(charSet, ch - FIRST_CHAR);
+	}
 }
 
 /**
@@ -354,20 +467,23 @@
 			// Use the specified colours
 			for (uint j = 0; j < frame.width; j++) {
 				switch (*source++) {
+				case 0:
+					// Do nothing if source pixel is zero,
+					// ie. transparent
+					break;
+				case LETTER_COL_PSX1: // Values for colored zone 
+				case LETTER_COL_PSX2: 
 				case LETTER_COL:
 					*dest = pen;
 					break;
 				case BORDER_COL:
+				default:
 					// Don't do a border pixel if there's
 					// already a bit of another character
 					// underneath (for overlapping!)
 					if (!*dest)
 						*dest = _borderPen;
 					break;
-				default:
-					// Do nothing if source pixel is zero,
-					// ie. transparent
-					break;
 				}
 				dest++;
 			}
@@ -400,7 +516,7 @@
 	assert(i < MAX_text_blocs);
 
 	// Create and position the sprite
-
+	
 	_blocList[i].text_mem = makeTextSprite(ascii, width, pen, fontRes);
 
 	// 'NO_JUSTIFICATION' means print sprite with top-left at (x,y)
@@ -498,6 +614,7 @@
 			spriteInfo.blend = 0;
 			spriteInfo.data = _blocList[i].text_mem + FrameHeader::size();
 			spriteInfo.colourTable = 0;
+			spriteInfo.isText = true;
 
 			uint32 rv = _vm->_screen->drawSprite(&spriteInfo);
 			if (rv)

Modified: scummvm/trunk/engines/sword2/menu.cpp
===================================================================
--- scummvm/trunk/engines/sword2/menu.cpp	2009-04-07 17:43:49 UTC (rev 39895)
+++ scummvm/trunk/engines/sword2/menu.cpp	2009-04-07 19:52:46 UTC (rev 39896)
@@ -42,16 +42,25 @@
 void Mouse::clearIconArea(int menu, int pocket, Common::Rect *r) {
 	byte *buf = _vm->_screen->getScreen();
 	int16 screenWide = _vm->_screen->getScreenWide();
+	byte menuIconWidth;
+	
+	// Initialize menu icon width at correct size
+	// depending if we are using pc or psx version.
+	if (Sword2Engine::isPsx())
+		menuIconWidth = RDMENU_PSXICONWIDE;
+	else
+		menuIconWidth = RDMENU_ICONWIDE;
 
+
 	r->top = menu * (RENDERDEEP + MENUDEEP) + (MENUDEEP - RDMENU_ICONDEEP) / 2;
 	r->bottom = r->top + RDMENU_ICONDEEP;
-	r->left = RDMENU_ICONSTART + pocket * (RDMENU_ICONWIDE + RDMENU_ICONSPACING);
-	r->right = r->left + RDMENU_ICONWIDE;
+	r->left = RDMENU_ICONSTART + pocket * (menuIconWidth + RDMENU_ICONSPACING);
+	r->right = r->left + menuIconWidth;
 
 	byte *dst = buf + r->top * screenWide + r->left;
 
 	for (int i = 0; i < RDMENU_ICONDEEP; i++) {
-		memset(dst, 0, RDMENU_ICONWIDE);
+		memset(dst, 0, menuIconWidth);
 		dst += screenWide;
 	}
 }
@@ -71,7 +80,14 @@
 
 	byte *buf = _vm->_screen->getScreen();
 	int16 screenWide = _vm->_screen->getScreenWide();
+	byte menuIconWidth; 
+	
+	if (Sword2Engine::isPsx())
+		menuIconWidth = RDMENU_PSXICONWIDE;
+	else
+		menuIconWidth = RDMENU_ICONWIDE;
 
+
 	if (lastTime == 0) {
 		lastTime = _vm->getMillis();
 		frameCount = 1;
@@ -141,7 +157,7 @@
 			_menuStatus[menu] = RDMENU_HIDDEN;
 
 		// Draw the menu here.
-		int32 curx = RDMENU_ICONSTART + RDMENU_ICONWIDE / 2;
+		int32 curx = RDMENU_ICONSTART + menuIconWidth / 2;
 		int32 cury = (MENUDEEP / 2) + (RENDERDEEP + MENUDEEP) * menu;
 
 		for (i = 0; i < RDMENU_MAXPOCKETS; i++) {
@@ -154,14 +170,14 @@
 				clearIconArea(menu, i, &r1);
 
 				if (_pocketStatus[menu][i] == MAXMENUANIMS) {
-					xoff = (RDMENU_ICONWIDE / 2);
+					xoff = (menuIconWidth / 2);
 					r2.left = curx - xoff;
-					r2.right = r2.left + RDMENU_ICONWIDE;
+					r2.right = r2.left + menuIconWidth;
 					yoff = (RDMENU_ICONDEEP / 2);
 					r2.top = cury - yoff;
 					r2.bottom = r2.top + RDMENU_ICONDEEP;
 				} else {
-					xoff = (RDMENU_ICONWIDE / 2) * _pocketStatus[menu][i] / MAXMENUANIMS;
+					xoff = (menuIconWidth / 2) * _pocketStatus[menu][i] / MAXMENUANIMS;
 					r2.left = curx - xoff;
 					r2.right = curx + xoff;
 					yoff = (RDMENU_ICONDEEP / 2) * _pocketStatus[menu][i] / MAXMENUANIMS;
@@ -176,18 +192,18 @@
 					if (_pocketStatus[menu][i] != MAXMENUANIMS) {
 						_vm->_screen->scaleImageFast(
 							dst, screenWide, r2.right - r2.left, r2.bottom - r2.top,
-							src, RDMENU_ICONWIDE, RDMENU_ICONWIDE, RDMENU_ICONDEEP);
+							src, menuIconWidth, menuIconWidth, RDMENU_ICONDEEP);
 					} else {
 						for (j = 0; j < RDMENU_ICONDEEP; j++) {
-							memcpy(dst, src, RDMENU_ICONWIDE);
-							src += RDMENU_ICONWIDE;
+							memcpy(dst, src, menuIconWidth);
+							src += menuIconWidth;
 							dst += screenWide;
 						}
 					}
 				}
 				_vm->_screen->updateRect(&r1);
 			}
-			curx += (RDMENU_ICONSPACING + RDMENU_ICONWIDE);
+			curx += (RDMENU_ICONSPACING + menuIconWidth);
 		}
 	}
 }
@@ -199,6 +215,13 @@
  */
 
 int32 Mouse::showMenu(uint8 menu) {
+
+	// Do not show menu in PSX version, as there was really
+	// nothing similar in the original game (menu was started
+	// using SELECT button in psx pad)
+	if (Sword2Engine::isPsx() && menu == RDMENU_TOP) 
+		return RD_OK;
+
 	// Check for invalid menu parameter
 	if (menu > RDMENU_BOTTOM)
 		return RDERR_INVALIDMENU;
@@ -219,6 +242,11 @@
  */
 
 int32 Mouse::hideMenu(uint8 menu) {
+
+	// In PSX version, do nothing. There is no such menu.
+	if (Sword2Engine::isPsx() && menu == RDMENU_TOP)
+		return RD_OK;
+
 	// Check for invalid menu parameter
 	if (menu > RDMENU_BOTTOM)
 		return RDERR_INVALIDMENU;
@@ -267,6 +295,12 @@
 
 int32 Mouse::setMenuIcon(uint8 menu, uint8 pocket, byte *icon) {
 	Common::Rect r;
+	byte menuIconWidth;
+	
+	if (Sword2Engine::isPsx())
+		menuIconWidth = RDMENU_PSXICONWIDE;
+	else
+		menuIconWidth = RDMENU_ICONWIDE;
 
 	// Check for invalid menu parameter.
 	if (menu > RDMENU_BOTTOM)
@@ -288,10 +322,10 @@
 	// Only put the icon in the pocket if it is not NULL
 	if (icon != NULL) {
 		_iconCount++;
-		_icons[menu][pocket] = (byte *)malloc(RDMENU_ICONWIDE * RDMENU_ICONDEEP);
+		_icons[menu][pocket] = (byte *)malloc(menuIconWidth * RDMENU_ICONDEEP);
 		if (_icons[menu][pocket] == NULL)
 			return RDERR_OUTOFMEMORY;
-		memcpy(_icons[menu][pocket], icon, RDMENU_ICONWIDE * RDMENU_ICONDEEP);
+		memcpy(_icons[menu][pocket], icon, menuIconWidth * RDMENU_ICONDEEP);
 	}
 
 	return RD_OK;

Modified: scummvm/trunk/engines/sword2/mouse.cpp
===================================================================
--- scummvm/trunk/engines/sword2/mouse.cpp	2009-04-07 17:43:49 UTC (rev 39895)
+++ scummvm/trunk/engines/sword2/mouse.cpp	2009-04-07 19:52:46 UTC (rev 39896)
@@ -280,14 +280,21 @@
 
 int Mouse::menuClick(int menu_items) {
 	int x = getX();
+	byte menuIconWidth;
 
+	if (Sword2Engine::isPsx())
+		menuIconWidth = RDMENU_PSXICONWIDE;
+	else
+		menuIconWidth = RDMENU_ICONWIDE;
+
+
 	if (x < RDMENU_ICONSTART)
 		return -1;
 
-	if (x > RDMENU_ICONSTART + menu_items * (RDMENU_ICONWIDE + RDMENU_ICONSPACING) - RDMENU_ICONSPACING)
+	if (x > RDMENU_ICONSTART + menu_items * (menuIconWidth + RDMENU_ICONSPACING) - RDMENU_ICONSPACING)
 		return -1;
 
-	return (x - RDMENU_ICONSTART) / (RDMENU_ICONWIDE + RDMENU_ICONSPACING);
+	return (x - RDMENU_ICONSTART) / (menuIconWidth + RDMENU_ICONSPACING);
 }
 
 void Mouse::systemMenuMouse() {
@@ -330,6 +337,13 @@
 	if (hit < 0)
 		return;
 
+	// Do nothing if using PSX version and are on TOP menu.
+
+	if ((icon_list[hit] == OPTIONS_ICON || icon_list[hit] == QUIT_ICON 
+		|| icon_list[hit] == SAVE_ICON || icon_list[hit] == RESTORE_ICON 
+		|| icon_list[hit] == RESTART_ICON ) && Sword2Engine::isPsx() )
+		return;
+
 	// No save when dead
 
 	if (icon_list[hit] == SAVE_ICON && _vm->_logic->readVar(DEAD))
@@ -857,6 +871,14 @@
 	// Unlike the other mouse "engines", this one is called directly by the
 	// fnChoose() opcode.
 
+	byte menuIconWidth;
+
+	if (Sword2Engine::isPsx())
+		menuIconWidth = RDMENU_PSXICONWIDE;
+	else
+		menuIconWidth = RDMENU_ICONWIDE;
+
+
 	uint i;
 
 	_vm->_logic->writeVar(AUTO_SELECTED, 0);
@@ -912,7 +934,7 @@
 			error("fnChoose with no subjects");
 
 		for (i = 0; i < in_subject; i++) {
-			icon = _vm->_resman->openResource(_subjectList[i].res) + ResHeader::size() + RDMENU_ICONWIDE * RDMENU_ICONDEEP;
+			icon = _vm->_resman->openResource(_subjectList[i].res) + ResHeader::size() + menuIconWidth * RDMENU_ICONDEEP;
 			setMenuIcon(RDMENU_BOTTOM, i, icon);
 			_vm->_resman->closeResource(_subjectList[i].res);
 		}
@@ -1485,23 +1507,41 @@
 	int x = 0;
 	int y = 0;
 
-	comp = comp + READ_LE_UINT32(comp + frame * 4) - MOUSE_ANIM_HEADER_SIZE;
+	if (Sword2Engine::isPsx()) {
+		comp = comp + READ_LE_UINT32(comp + 2 + frame * 4) - MOUSE_ANIM_HEADER_SIZE;
 
-	while (i < size) {
-		if (*comp > 183) {
-			decomp[(y + yOff) * pitch + x + xOff] = *comp++;
-			if (++x >= width) {
-				x = 0;
-				y++;
+		yOff /= 2; // Without this, distance of object from cursor is too big.
+
+		byte *buffer;
+
+		buffer = (byte *)malloc(size);
+		Screen::decompressHIF(comp, buffer);
+
+		for (int line = 0; line < height; line++) {
+			memcpy(decomp + (line + yOff) * pitch + xOff, buffer + line * width, width);
+		}
+
+		free(buffer);
+	
+	} else {
+		comp = comp + READ_LE_UINT32(comp + frame * 4) - MOUSE_ANIM_HEADER_SIZE;	
+
+		while (i < size) {
+			if (*comp > 183) {
+				decomp[(y + yOff) * pitch + x + xOff] = *comp++;
+				if (++x >= width) {
+					x = 0;
+					y++;
+				}
+				i++;
+			} else {
+				x += *comp;
+				while (x >= width) {
+					y++;
+					x -= width;
+				}
+				i += *comp++;
 			}
-			i++;
-		} else {
-			x += *comp;
-			while (x >= width) {
-				y++;
-				x -= width;
-			}
-			i += *comp++;
 		}
 	}
 }
@@ -1563,6 +1603,17 @@
 		decompressMouse(mouseData, _mouseAnim.data, _mouseFrame,
 			_mouseAnim.mousew, _mouseAnim.mouseh, mouse_width);
 
+	// Fix height for mouse sprite in PSX version
+	if (Sword2Engine::isPsx()) { 
+		mouse_height *= 2;
+		
+		byte *buffer = (byte *)malloc(mouse_width * mouse_height);
+		Screen::resizePsxSprite(buffer, mouseData, mouse_width, mouse_height);
+		
+		free(mouseData);
+		mouseData = buffer;
+	}
+
 	CursorMan.replaceCursor(mouseData, mouse_width, mouse_height, hotspot_x, hotspot_y, 0);
 
 	free(mouseData);
@@ -1675,4 +1726,12 @@
 	return RD_OK;
 }
 
+int Mouse::getMouseMode() {
+	return _mouseMode;
+}
+
+void Mouse::setMouseMode(int mouseMode) {
+	_mouseMode = mouseMode;
+}
+
 } // End of namespace Sword2

Modified: scummvm/trunk/engines/sword2/mouse.h
===================================================================
--- scummvm/trunk/engines/sword2/mouse.h	2009-04-07 17:43:49 UTC (rev 39895)
+++ scummvm/trunk/engines/sword2/mouse.h	2009-04-07 19:52:46 UTC (rev 39896)
@@ -70,6 +70,7 @@
 };
 
 #define RDMENU_ICONWIDE		35
+#define RDMENU_PSXICONWIDE	36
 #define RDMENU_ICONDEEP		30
 #define RDMENU_ICONSTART	24
 #define RDMENU_ICONSPACING	5
@@ -269,6 +270,10 @@
 	uint32 chooseMouse();
 
 	int menuClick(int menu_items);
+
+	int getMouseMode();
+
+	void setMouseMode(int mouseMode); // Used to force mouse mode
 };
 
 } // End of namespace Sword2

Modified: scummvm/trunk/engines/sword2/music.cpp
===================================================================
--- scummvm/trunk/engines/sword2/music.cpp	2009-04-07 17:43:49 UTC (rev 39895)
+++ scummvm/trunk/engines/sword2/music.cpp	2009-04-07 19:52:46 UTC (rev 39896)
@@ -40,6 +40,7 @@
 #include "sound/flac.h"
 #include "sound/rate.h"
 #include "sound/wave.h"
+#include "sound/vag.h"
 
 #include "sword2/sword2.h"
 #include "sword2/defs.h"
@@ -83,6 +84,7 @@
 }
 
 static Audio::AudioStream *makeCLUStream(Common::File *fp, int size);
+static Audio::AudioStream *makePSXCLUStream(Common::File *fp, int size);
 
 static Audio::AudioStream *getAudioStream(SoundFileHandle *fh, const char *base, int cd, uint32 id, uint32 *numSamples) {
 	bool alreadyOpen;
@@ -189,7 +191,10 @@
 
 	switch (fh->fileType) {
 	case kCLUMode:
-		return makeCLUStream(&fh->file, enc_len);
+		if (Sword2Engine::isPsx())
+			return makePSXCLUStream(&fh->file, enc_len);
+		else
+			return makeCLUStream(&fh->file, enc_len);
 #ifdef USE_MAD
 	case kMP3Mode:
 		tmp = new SafeSubReadStream(&fh->file, pos, pos + enc_len, false);
@@ -289,6 +294,16 @@
 	return new CLUInputStream(file, size);
 }
 
+Audio::AudioStream *makePSXCLUStream(Common::File *file, int size) {
+	
+	// Buffer audio file data, and ask MemoryReadStream to dispose of it
+	// when not needed anymore.
+
+	byte *buffer = (byte *)malloc(size);
+	file->read(buffer, size);
+	return new Audio::VagStream(new Common::MemoryReadStream(buffer, size, true));
+}
+
 // ----------------------------------------------------------------------------
 // Another custom AudioStream class, to wrap around the various AudioStream
 // classes used for music decompression, and to add looping, fading, etc.

Modified: scummvm/trunk/engines/sword2/palette.cpp
===================================================================
--- scummvm/trunk/engines/sword2/palette.cpp	2009-04-07 17:43:49 UTC (rev 39895)
+++ scummvm/trunk/engines/sword2/palette.cpp	2009-04-07 19:52:46 UTC (rev 39896)
@@ -48,7 +48,11 @@
 
 	byte *screenFile = _vm->_resman->openResource(_thisScreen.background_layer_id);
 
-	memcpy(_paletteMatch, _vm->fetchPaletteMatchTable(screenFile), PALTABLESIZE);
+	// Don't fetch palette match table while using PSX version,
+	// because it is not present.
+	if(!Sword2Engine::isPsx()) 
+		memcpy(_paletteMatch, _vm->fetchPaletteMatchTable(screenFile), PALTABLESIZE);
+	
 	setPalette(0, 256, _vm->fetchPalette(screenFile), RDPAL_FADE);
 
 	// Indicating that it's a screen palette
@@ -116,7 +120,12 @@
 	} else {
 		if (_thisScreen.background_layer_id) {
 			byte *data = _vm->_resman->openResource(_thisScreen.background_layer_id);
-			memcpy(_paletteMatch, _vm->fetchPaletteMatchTable(data), PALTABLESIZE);
+			
+			// Do not fetch palette match table when using PSX version,
+			// because it is not present.
+			if (!Sword2Engine::isPsx())  
+				memcpy(_paletteMatch, _vm->fetchPaletteMatchTable(data), PALTABLESIZE);
+			
 			setPalette(0, 256, _vm->fetchPalette(data), RDPAL_INSTANT);
 			_vm->_resman->closeResource(_thisScreen.background_layer_id);
 		} else

Modified: scummvm/trunk/engines/sword2/protocol.cpp
===================================================================
--- scummvm/trunk/engines/sword2/protocol.cpp	2009-04-07 17:43:49 UTC (rev 39895)
+++ scummvm/trunk/engines/sword2/protocol.cpp	2009-04-07 19:52:46 UTC (rev 39896)
@@ -25,11 +25,13 @@
  * $Id$
  */
 
+#include "common/file.h"
+#include "common/endian.h"
 
-
 #include "sword2/sword2.h"
 #include "sword2/header.h"
 #include "sword2/resman.h"
+#include "sword2/logic.h"
 
 namespace Sword2 {
 
@@ -39,11 +41,16 @@
  */
 
 byte *Sword2Engine::fetchPalette(byte *screenFile) {
-	MultiScreenHeader mscreenHeader;
+	byte *palette;
 
-	mscreenHeader.read(screenFile + ResHeader::size());
+	if(isPsx()) { // PSX version doesn't have a "MultiScreenHeader", instead there's a ScreenHeader and a tag
+		palette = screenFile + ResHeader::size() + ScreenHeader::size() + 2;
+	} else {
+		MultiScreenHeader mscreenHeader;
 
-	byte *palette = screenFile + ResHeader::size() + mscreenHeader.palette;
+		mscreenHeader.read(screenFile + ResHeader::size());
+		palette = screenFile + ResHeader::size() + mscreenHeader.palette;
+	}
 
 	// Always set colour 0 to black, because while most background screen
 	// palettes have a bright colour 0 it should come out as black in the
@@ -60,9 +67,14 @@
 /**
  * Returns a pointer to the start of the palette match table, given the pointer
  * to the start of the screen file.
+ * It returns NULL when used with PSX version, as there are no palette match tables in
+ * the resource files.
  */
 
 byte *Sword2Engine::fetchPaletteMatchTable(byte *screenFile) {
+	
+	if (isPsx()) return NULL;
+
 	MultiScreenHeader mscreenHeader;
 
 	mscreenHeader.read(screenFile + ResHeader::size());
@@ -76,11 +88,14 @@
  */
 
 byte *Sword2Engine::fetchScreenHeader(byte *screenFile) {
-	MultiScreenHeader mscreenHeader;
+	if (isPsx()) { // In PSX version there's no MultiScreenHeader, so just skip resource header
+		return screenFile + ResHeader::size();
+	} else { 
+		MultiScreenHeader mscreenHeader;
 
-	mscreenHeader.read(screenFile + ResHeader::size());
-
-	return screenFile + ResHeader::size() + mscreenHeader.screen;
+		mscreenHeader.read(screenFile + ResHeader::size());
+		return screenFile + ResHeader::size() + mscreenHeader.screen;
+	}
 }
 
 /**
@@ -97,19 +112,25 @@
 	assert(layerNo < screenHead.noLayers);
 #endif
 
-	MultiScreenHeader mscreenHeader;
-
-	mscreenHeader.read(screenFile + ResHeader::size());
-
-	return screenFile + ResHeader::size() + mscreenHeader.layers + layerNo * LayerHeader::size();
+	if (isPsx()) {
+		return screenFile + ResHeader::size() + ScreenHeader::size() + 2 + 0x400 + layerNo * LayerHeader::size();
+	} else {
+		MultiScreenHeader mscreenHeader;
+	
+		mscreenHeader.read(screenFile + ResHeader::size());
+		return screenFile + ResHeader::size() + mscreenHeader.layers + layerNo * LayerHeader::size();
+	}
 }
 
 /**
  * Returns a pointer to the start of the shading mask, given the pointer to the
  * start of the screen file.
+ * If we are non PSX, this will return NULL, as we don't have shading masks.
  */
 
 byte *Sword2Engine::fetchShadingMask(byte *screenFile) {
+	if (isPsx()) return NULL;
+
 	MultiScreenHeader mscreenHeader;
 
 	mscreenHeader.read(screenFile + ResHeader::size());
@@ -139,7 +160,7 @@
 	animHead.read(fetchAnimHeader(animFile));
 
 	if (frameNo > animHead->noAnimFrames - 1)
-		error("fetchCdtEntry(animFile,%d) - anim only %d frames", frameNo, animHead.noAnimFrames);
+		error("fetchCdtEntry(animFile,%d) - anim only %d frames", frameNo, animHead->noAnimFrames);
 #endif
 
 	return fetchAnimHeader(animFile) + AnimHeader::size() + frameNo * CdtEntry::size();
@@ -153,6 +174,7 @@
 
 byte *Sword2Engine::fetchFrameHeader(byte *animFile, uint16 frameNo) {
 	// required address = (address of the start of the anim header) + frameOffset
+	
 	CdtEntry cdt;
 
 	cdt.read(fetchCdtEntry(animFile, frameNo));
@@ -165,30 +187,86 @@
  */
 
 byte *Sword2Engine::fetchBackgroundParallaxLayer(byte *screenFile, int layer) {
-	MultiScreenHeader mscreenHeader;
+	if (isPsx()) {
+		byte *psxParallax = _screen->getPsxScrCache(0);
 
-	mscreenHeader.read(screenFile + ResHeader::size());
-	assert(mscreenHeader.bg_parallax[layer]);
+		// Manage cache for background psx parallaxes
+		if (!_screen->getPsxScrCacheStatus(0)) { // This parallax layer is not present
+			return NULL;
+		} else if (psxParallax != NULL) { // Parallax layer present, and already in cache
+			return psxParallax;
+		} else { // Present, but not cached
+			uint32 locNo = _logic->getLocationNum();
 
-	return screenFile + ResHeader::size() + mscreenHeader.bg_parallax[layer];
+			// At game startup, we have a wrong location number stored
+			// in game vars (0, instead of 3), work around this.
+			locNo = (locNo == 0) ? 3 : locNo;
+
+			psxParallax = fetchPsxParallax(locNo, 0);
+			_screen->setPsxScrCache(psxParallax, 0);
+			return psxParallax;
+		}
+	} else {
+		MultiScreenHeader mscreenHeader;
+
+		mscreenHeader.read(screenFile + ResHeader::size());
+		assert(mscreenHeader.bg_parallax[layer]);
+		return screenFile + ResHeader::size() + mscreenHeader.bg_parallax[layer];
+	}
 }
 
 byte *Sword2Engine::fetchBackgroundLayer(byte *screenFile) {
-	MultiScreenHeader mscreenHeader;
+	if (isPsx()) {
+		byte *psxBackground = _screen->getPsxScrCache(1);
 
-	mscreenHeader.read(screenFile + ResHeader::size());
-	assert(mscreenHeader.screen);
+		// Manage cache for psx backgrounds
+		if (psxBackground) { // Background is cached
+			return psxBackground;
+		} else { // Background not cached
+			uint32 locNo = _logic->getLocationNum();
 
-	return screenFile + ResHeader::size() + mscreenHeader.screen + ScreenHeader::size();
+			// We have a wrong location number at start, fix that
+			locNo = (locNo == 0) ? 3 : locNo;
+
+			psxBackground = fetchPsxBackground(locNo);
+			_screen->setPsxScrCache(psxBackground, 1);
+			return psxBackground;
+		}		
+	} else {
+		MultiScreenHeader mscreenHeader;
+
+		mscreenHeader.read(screenFile + ResHeader::size());
+		assert(mscreenHeader.screen);
+		return screenFile + ResHeader::size() + mscreenHeader.screen + ScreenHeader::size();
+	}
 }
 
 byte *Sword2Engine::fetchForegroundParallaxLayer(byte *screenFile, int layer) {
-	MultiScreenHeader mscreenHeader;
+	if (isPsx()) {
+		byte *psxParallax = _screen->getPsxScrCache(2);
 
-	mscreenHeader.read(screenFile + ResHeader::size());
-	assert(mscreenHeader.fg_parallax[layer]);
+		// Manage cache for psx parallaxes
+		if (!_screen->getPsxScrCacheStatus(2)) { // This parallax layer is not present
+			return NULL;
+		} else if (psxParallax) { // Parallax layer present and cached
+			return psxParallax;
+		} else { // Present, but still not cached
+			uint32 locNo = _logic->getLocationNum();
 
-	return screenFile + ResHeader::size() + mscreenHeader.fg_parallax[layer];
+			// We have a wrong location number at start, fix that
+			locNo = (locNo == 0) ? 3 : locNo;
+
+			psxParallax = fetchPsxParallax(locNo, 1);
+			_screen->setPsxScrCache(psxParallax, 2);
+			return psxParallax;
+		}		
+	} else {
+		MultiScreenHeader mscreenHeader;
+
+		mscreenHeader.read(screenFile + ResHeader::size());
+		assert(mscreenHeader.fg_parallax[layer]);
+		return screenFile + ResHeader::size() + mscreenHeader.fg_parallax[layer];
+	}
 }
 
 byte *Sword2Engine::fetchTextLine(byte *file, uint32 text_line) {
@@ -211,6 +289,152 @@
 	return file + READ_LE_UINT32(file + ResHeader::size() + 4 + 4 * text_line);
 }
 
+/**
+ * Returns a pointer to psx background data for passed location number
+ * At the beginning of the passed data there's an artificial header composed by
+ * uint16: background X resolution
+ * uint16: background Y resolution
+ * uint32: offset to subtract from offset table entries
+ */
+
+byte *Sword2Engine::fetchPsxBackground(uint32 location) {
+	Common::File file;
+	PSXScreensEntry header;
+	uint32 screenOffset, dataOffset;
+	uint32 totSize; // Total size of background, counting data, offset table and additional header
+	byte *buffer;
+
+	if (!file.open("screens.clu")) {
+		GUIErrorMessage("Broken Sword 2: Cannot open screens.clu");
+		return NULL;
+	}
+
+	file.seek(location * 4, SEEK_SET);
+	screenOffset = file.readUint32LE();	
+
+	if (screenOffset == 0) { // We don't have screen data for this location number.
+		file.close();
+		return NULL;
+	}
+
+	// Get to the beginning of PSXScreensEntry
+	file.seek(screenOffset + ResHeader::size(), SEEK_SET);
+	
+	buffer = (byte *)malloc(PSXScreensEntry::size());
+	file.read(buffer, PSXScreensEntry::size());
+	
+	// Prepare the header
+	header.read(buffer);
+	free(buffer);
+
+	file.seek(screenOffset + header.bgOffset + 4, SEEK_SET);
+	dataOffset = file.readUint32LE();
+
+	file.seek(screenOffset + header.bgOffset, SEEK_SET);
+
+	totSize = header.bgSize + (dataOffset - header.bgOffset) + 8;
+	buffer = (byte *)malloc(totSize);
+
+	// Write some informations before background data
+	WRITE_LE_UINT16(buffer, header.bgXres); 
+	WRITE_LE_UINT16(buffer + 2, header.bgYres);
+	WRITE_LE_UINT32(buffer + 4, header.bgOffset);
+
+	file.read(buffer + 8, totSize - 8); // Do not write on the header
+	file.close();
+
+	return buffer;
+}
+
+/**
+ * Returns a pointer to selected psx parallax data for passed location number
+ * At the beginning of the passed data there's an artificial header composed by
+ * uint16: parallax X resolution
+ * uint16: parallax Y resolution
+ * uint16: width in 64x16 tiles of parallax
+ * uint16: height in 64x16 tiles of parallax
+ */
+
+byte *Sword2Engine::fetchPsxParallax(uint32 location, uint8 level) {
+	Common::File file;
+	PSXScreensEntry header;
+	uint32 screenOffset;
+	uint16 horTiles; // Number of horizontal tiles in the parallax grid
+	uint16 verTiles; // Number of vertical tiles in parallax grid
+	uint32 totSize; // Total size of parallax, counting data, grid, and additional header
+	byte *buffer;
+
+	uint16 plxXres;
+	uint16 plxYres;
+	uint32 plxOffset;
+	uint32 plxSize;
+
+	if (level > 1)
+		return NULL;
+
+	if (!file.open("screens.clu")) {
+		GUIErrorMessage("Broken Sword 2: Cannot open screens.clu");
+		return NULL;
+	}
+
+	file.seek(location * 4, SEEK_SET);
+	screenOffset = file.readUint32LE();	
+
+	if (screenOffset == 0) // There is no screen here
+		return NULL;
+
+	// Get to the beginning of PSXScreensEntry
+	file.seek(screenOffset + ResHeader::size(), SEEK_SET);
+	
+	buffer = (byte *)malloc(PSXScreensEntry::size());
+	file.read(buffer, PSXScreensEntry::size());
+	
+	// Initialize the header
+	header.read(buffer);
+	free(buffer);
+
+	// We are fetching...
+	if (level == 0) { // a background parallax
+		plxXres = header.bgPlxXres;
+		plxYres = header.bgPlxYres;
+		plxOffset = header.bgPlxOffset;
+		plxSize = header.bgPlxSize;
+	} else {  // a foreground parallax
+		plxXres = header.fgPlxXres;
+		plxYres = header.fgPlxYres;
+		plxOffset = header.fgPlxOffset;
+		plxSize = header.fgPlxSize;
+	}
+
+	if (plxXres == 0 || plxYres == 0 || plxSize == 0) // This screen has no parallax data.
+		return NULL;
+
+	debug(2, "fetchPsxParallax() -> %s parallax, xRes: %u, yRes: %u", (level == 0) ? "Background" : "Foreground", plxXres, plxYres);
+
+	// Calculate the number of tiles which compose the parallax grid.
+	horTiles = plxXres % 64 ? (plxXres / 64) + 1 : plxXres / 64;
+	verTiles = plxYres % 16 ? (plxYres / 16) + 1 : plxYres / 16;
+
+	totSize = plxSize + horTiles * verTiles * 4 + 8;
+
+	file.seek(screenOffset + plxOffset, SEEK_SET);
+	buffer = (byte *)malloc(totSize);
+
+	// Insert parallax resolution information in the buffer,
+	// preceding parallax data.
+	WRITE_LE_UINT16(buffer, plxXres); 
+	WRITE_LE_UINT16(buffer + 2, plxYres);
+	WRITE_LE_UINT16(buffer + 4, horTiles); 
+	WRITE_LE_UINT16(buffer + 6, verTiles);
+
+	// Read parallax data from file and store it inside the buffer,
+	// skipping the generated header.
+	file.read(buffer + 8, totSize - 8); 
+	file.close();
+
+	return buffer;
+}
+
 // Used for testing text & speech (see fnISpeak in speech.cpp)
 
 bool Sword2Engine::checkTextLine(byte *file, uint32 text_line) {

Modified: scummvm/trunk/engines/sword2/render.cpp
===================================================================
--- scummvm/trunk/engines/sword2/render.cpp	2009-04-07 17:43:49 UTC (rev 39895)
+++ scummvm/trunk/engines/sword2/render.cpp	2009-04-07 19:52:46 UTC (rev 39896)
@@ -252,21 +252,35 @@
  */
 
 void Screen::renderParallax(byte *ptr, int16 l) {
-	Parallax p;
 	int16 x, y;
+	uint16 xRes, yRes;
 	Common::Rect r;
 
-	p.read(ptr);
+	if (!ptr)
+		return;
 
+	// Fetch resolution data from parallax
+	
+	if (Sword2Engine::isPsx()) {
+		xRes = READ_LE_UINT16(ptr);
+		yRes = READ_LE_UINT16(ptr + 2) * 2;
+	} else {
+		Parallax p;
+		
+		p.read(ptr);
+		xRes = p.w;
+		yRes = p.h;
+	}
+
 	if (_locationWide == _screenWide)
 		x = 0;
 	else
-		x = ((int32)((p.w - _screenWide) * _scrollX) / (int32)(_locationWide - _screenWide));
+		x = ((int32)((xRes - _screenWide) * _scrollX) / (int32)(_locationWide - _screenWide));
 
 	if (_locationDeep == _screenDeep - MENUDEEP * 2)
 		y = 0;
 	else
-		y = ((int32)((p.h - (_screenDeep - MENUDEEP * 2)) * _scrollY) / (int32)(_locationDeep - (_screenDeep - MENUDEEP * 2)));
+		y = ((int32)((yRes - (_screenDeep - MENUDEEP * 2)) * _scrollY) / (int32)(_locationDeep - (_screenDeep - MENUDEEP * 2)));
 
 	Common::Rect clipRect;
 
@@ -537,12 +551,265 @@
 }
 
 /**
+ * This converts PSX format background data into a format that
+ * can be understood by renderParallax functions.
+ * PSX Backgrounds are divided into tiles of 64x32 (with aspect
+ * ratio correction), while PC backgrounds are in tiles of 64x64.
+ */
+
+int32 Screen::initialisePsxBackgroundLayer(byte *parallax) {
+	uint16 bgXres, bgYres;
+	uint16 trueXres, stripeNumber, totStripes;
+	uint32 baseAddress, stripePos;
+	uint16 i, j;
+	byte *dst;
+
+	debug(2, "initialisePsxBackgroundLayer");
+
+	assert(_layer < MAXLAYERS);
+
+	if (!parallax) {
+		_layer++;
+		return RD_OK;
+	}
+
+	// Fetch data from buffer
+
+	bgXres = READ_LE_UINT16(parallax);
+	bgYres = READ_LE_UINT16(parallax + 2) * 2;
+	baseAddress = READ_LE_UINT32(parallax + 4);
+	parallax += 8;
+
+	// Calculate TRUE resolution of background, must be
+	// a multiple of 64
+
+	trueXres = (bgXres % 64) ? ((bgXres/64) + 1) * 64 : bgXres;
+	totStripes = trueXres / 64;
+
+	_xBlocks[_layer] = (bgXres + BLOCKWIDTH - 1) / BLOCKWIDTH;
+	_yBlocks[_layer] = (bgYres + BLOCKHEIGHT - 1) / BLOCKHEIGHT;
+	
+	uint16 remLines = bgYres % 64;
+
+	byte *tileChunk = (byte *)malloc(BLOCKHEIGHT * BLOCKWIDTH);
+	if (!tileChunk)
+		return RDERR_OUTOFMEMORY;
+
+	_blockSurfaces[_layer] = (BlockSurface **)calloc(_xBlocks[_layer] * _yBlocks[_layer], sizeof(BlockSurface *));
+	if (!_blockSurfaces[_layer])
+		return RDERR_OUTOFMEMORY;
+
+	// Group PSX background (64x32, when stretched vertically) tiles together,
+	// to make them compatible with pc version (composed by 64x64 tiles)
+
+	stripeNumber = 0;
+	stripePos = 0;
+	for (i = 0; i < _xBlocks[_layer] * _yBlocks[_layer]; i++) {
+		bool block_has_data = false;
+		bool block_is_transparent = false;
+
+		int posX = i / _yBlocks[_layer];
+		int posY = i % _yBlocks[_layer];
+
+		uint32 stripeOffset = READ_LE_UINT32(parallax + stripeNumber * 8 + 4) + stripePos - baseAddress;
+
+		memset(tileChunk, 1, BLOCKHEIGHT * BLOCKWIDTH);
+
+		if (!(remLines && posY == _yBlocks[_layer] - 1)) 
+			remLines = 32;
+
+		for(j = 0; j < remLines; j++) {
+			memcpy(tileChunk + j * BLOCKWIDTH * 2, parallax + stripeOffset + j * BLOCKWIDTH, BLOCKWIDTH);
+			memcpy(tileChunk + j * BLOCKWIDTH * 2 + BLOCKWIDTH, parallax + stripeOffset + j * BLOCKWIDTH, BLOCKWIDTH);
+		}
+
+		for (j = 0; j < BLOCKHEIGHT * BLOCKWIDTH; j++) {
+			if (tileChunk[j])
+				block_has_data = true;
+			else
+				block_is_transparent = true;
+		}	
+
+		int tileIndex = totStripes * posY + posX;
+
+		//  Only assign a surface to the block if it contains data.
+
+		if (block_has_data) {
+			_blockSurfaces[_layer][tileIndex] = (BlockSurface *)malloc(sizeof(BlockSurface));
+
+			//  Copy the data into the surfaces.
+			dst = _blockSurfaces[_layer][tileIndex]->data;
+			memcpy(dst, tileChunk, BLOCKWIDTH * BLOCKHEIGHT);
+
+			_blockSurfaces[_layer][tileIndex]->transparent = block_is_transparent;
+
+		} else
+			_blockSurfaces[_layer][tileIndex] = NULL;
+
+		if (posY == _yBlocks[_layer] - 1) {
+			stripeNumber++;
+			stripePos = 0;
+		} else {
+			stripePos += 0x800;
+		}
+	}
+
+	free(tileChunk);
+	_layer++;
+
+	return RD_OK;	
+}
+
+/**
+ * This converts PSX format parallax data into a format that
+ * can be understood by renderParallax functions.
+ */
+
+int32 Screen::initialisePsxParallaxLayer(byte *parallax) {
+	uint16 plxXres, plxYres;
+	uint16 xTiles, yTiles;
+	uint16 i, j, k;
+	byte *data;
+	byte *dst;
+
+	debug(2, "initialisePsxParallaxLayer");
+
+	assert(_layer < MAXLAYERS);
+
+	if (!parallax) {
+		_layer++;
+		return RD_OK;
+	}
+
+	plxXres = READ_LE_UINT16(parallax);
+	plxYres = READ_LE_UINT16(parallax + 2);
+	xTiles = READ_LE_UINT16(parallax + 4);
+	yTiles = READ_LE_UINT16(parallax + 6);
+
+	// Beginning of parallax table composed by uint32,
+	// if word is 0, corresponding tile contains no data and must be skipped,
+	// if word is 0x400 tile contains data.
+	parallax += 8;
+
+	// Beginning if tiles data.
+	data = parallax + xTiles * yTiles * 4;
+
+	_xBlocks[_layer] = xTiles;
+	_yBlocks[_layer] = (yTiles / 2) + (yTiles % 2 ? 1 : 0);
+	bool oddTiles = (yTiles % 2 ? true : false);
+
+	_blockSurfaces[_layer] = (BlockSurface **)calloc(_xBlocks[_layer] * _yBlocks[_layer], sizeof(BlockSurface *));
+	if (!_blockSurfaces[_layer])
+		return RDERR_OUTOFMEMORY;
+
+	// We have to check two tiles for every block in PSX version, if one of those
+	// has data in it, the whole block has data. Also, tiles must be doublelined to
+	// get correct aspect ratio.
+	for (i = 0; i < _xBlocks[_layer] * _yBlocks[_layer]; i++) {
+		bool block_has_data = false;
+		bool block_is_transparent = false;
+		bool firstTilePresent, secondTilePresent;
+
+		int posX = i / _yBlocks[_layer];
+		int posY = i % _yBlocks[_layer];
+
+		if (oddTiles && posY == _yBlocks[_layer] - 1) {
+			firstTilePresent = READ_LE_UINT32(parallax) == 0x400;
+			secondTilePresent = false;
+			parallax += 4;
+		} else {
+			firstTilePresent = READ_LE_UINT32(parallax) == 0x400;
+			secondTilePresent = READ_LE_UINT32(parallax + 4) == 0x400;
+			parallax += 8;
+		}
+
+		// If one of the two grouped tiles has data, then the whole block has data
+		if (firstTilePresent || secondTilePresent) {
+			block_has_data = true;
+
+			// If one of the two grouped blocks is without data, then we also have transparency
+			if(!firstTilePresent || !secondTilePresent) 
+				block_is_transparent = true;
+		}
+
+		// Now do a second check to see if we have a partially transparent block
+		if (block_has_data && !block_is_transparent) {
+			byte *block = data;
+			if (firstTilePresent) {
+				for (k = 0; k < 0x400; k++) {
+					if ( *(block + k) == 0) {
+						block_is_transparent = true;
+						break;
+					}
+				}
+				block += 0x400; // On to next block...
+			}
+
+			// If we didn't find transparency in first block and we have
+			// a second tile, check it
+			if (secondTilePresent && !block_is_transparent) { 
+				for (k = 0; k < 0x400; k++) {
+					if ( *(block + k) == 0) {
+						block_is_transparent = true;
+						break;
+					}
+				}
+			}
+		}
+
+		int tileIndex = xTiles * posY + posX;
+
+		//  Only assign a surface to the block if it contains data.
+
+		if (block_has_data) {
+			_blockSurfaces[_layer][tileIndex] = (BlockSurface *)malloc(sizeof(BlockSurface));
+			memset(_blockSurfaces[_layer][tileIndex], 0, BLOCKHEIGHT * BLOCKWIDTH);
+
+			//  Copy the data into the surfaces.
+			dst = _blockSurfaces[_layer][tileIndex]->data;
+
+			if (firstTilePresent) { //There is data in the first tile
+				for (j = 0; j < 16; j++) {
+					memcpy(dst, data, BLOCKWIDTH);
+					dst += BLOCKWIDTH;
+					memcpy(dst, data, BLOCKWIDTH);
+					dst += BLOCKWIDTH;
+					data += BLOCKWIDTH;
+				}
+			} else {
+				dst += 0x800;
+			}
+
+			if (secondTilePresent) {
+				for (j = 0; j < 16; j++) {
+					memcpy(dst, data, BLOCKWIDTH);
+					dst += BLOCKWIDTH;
+					memcpy(dst, data, BLOCKWIDTH);
+					dst += BLOCKWIDTH;
+					data += BLOCKWIDTH;
+				}
+			}
+
+			_blockSurfaces[_layer][tileIndex]->transparent = block_is_transparent;
+		} else
+			_blockSurfaces[_layer][tileIndex] = NULL;
+	}
+
+	_layer++;
+
+	return RD_OK;
+}
+
+/**
  * Should be called once after leaving the room to free up memory.
  */
 
 void Screen::closeBackgroundLayer() {
 	debug(2, "CloseBackgroundLayer");
 
+	if (Sword2Engine::isPsx())
+		flushPsxScrCache();
+
 	for (int i = 0; i < MAXLAYERS; i++) {
 		if (_blockSurfaces[i]) {
 			for (int j = 0; j < _xBlocks[i] * _yBlocks[i]; j++)

Modified: scummvm/trunk/engines/sword2/resman.cpp
===================================================================
--- scummvm/trunk/engines/sword2/resman.cpp	2009-04-07 17:43:49 UTC (rev 39895)
+++ scummvm/trunk/engines/sword2/resman.cpp	2009-04-07 19:52:46 UTC (rev 39896)
@@ -159,7 +159,10 @@
 
 	file.close();
 
-	if (!file.open("cd.inf")) {
+	// Check that we have cd.inf file, unless we are running PSX
+	// version, which has all files on one disc.
+
+	if (!file.open("cd.inf") && !Sword2Engine::isPsx()) {
 		GUIErrorMessage("Broken Sword 2: Cannot open cd.inf");
 		return false;
 	}
@@ -167,15 +170,21 @@
 	CdInf *cdInf = new CdInf[_totalClusters];
 
 	for (i = 0; i < _totalClusters; i++) {
-		file.read(cdInf[i].clusterName, sizeof(cdInf[i].clusterName));
 
-		cdInf[i].cd = file.readByte();
+		if (Sword2Engine::isPsx()) { // We are running PSX version, artificially fill CdInf structure
+			cdInf[i].cd = CD1;
+		} else { // We are running PC version, read cd.inf file
+			file.read(cdInf[i].clusterName, sizeof(cdInf[i].clusterName));
 
-		if (file.ioFailed()) {
-			delete cdInf;
-			file.close();
-			GUIErrorMessage("Broken Sword 2: Cannot read cd.inf");
-			return false;
+			cdInf[i].cd = file.readByte();
+
+			if (file.ioFailed()) {
+				delete cdInf;
+				file.close();
+				GUIErrorMessage("Broken Sword 2: Cannot read cd.inf");
+				return false;
+			}
+
 		}
 
 		// It has been reported that there are two different versions
@@ -208,19 +217,24 @@
 
 	file.close();
 
-	for (i = 0; i < _totalClusters; i++) {
-		for (j = 0; j < _totalClusters; j++) {
-			if (scumm_stricmp((char *)cdInf[j].clusterName, _resFiles[i].fileName) == 0)
-				break;
-		}
+	// We check the presence of resource files in cd.inf 
+	// This is ok in PC version, but in PSX version we don't
+	// have cd.inf so we'll have to skip this.
+	if (!Sword2Engine::isPsx()) {
+		for (i = 0; i < _totalClusters; i++) {
+			for (j = 0; j < _totalClusters; j++) {
+				if (scumm_stricmp((char *)cdInf[j].clusterName, _resFiles[i].fileName) == 0)
+					break;
+			}
 
-		if (j == _totalClusters) {
-			delete[] cdInf;
-			GUIErrorMessage(Common::String(_resFiles[i].fileName) + " is not in cd.inf");
-			return false;
+			if (j == _totalClusters) {
+				delete[] cdInf;
+				GUIErrorMessage(Common::String(_resFiles[i].fileName) + " is not in cd.inf");
+				return false;
+			}
+
+			_resFiles[i].cd = cdInf[j].cd;
 		}
-
-		_resFiles[i].cd = cdInf[j].cd;
 	}
 
 	delete[] cdInf;
@@ -247,11 +261,19 @@
 byte *ResourceManager::openResource(uint32 res, bool dump) {
 	assert(res < _totalResFiles);
 
+
+	// FIXME: In PSX edition, not all top menu icons are present (TOP menu is not used).
+	// Though, at present state, the engine still ask for the resources.
+	if (Sword2Engine::isPsx()) { // We need to "rewire" missing icons 
+		if (res == 342) res = 364; // Rewire RESTORE ICON to SAVE ICON
+	}
+
 	// Is the resource in memory already? If not, load it.
 
 	if (!_resList[res].ptr) {
 		// Fetch the correct file and read in the correct portion.
 		uint16 cluFileNum = _resConvTable[res * 2]; // points to the number of the ascii filename
+		
 		assert(cluFileNum != 0xffff);
 
 		// Relative resource within the file
@@ -264,7 +286,10 @@
 		// of the CDs, remember which one so that we can play the
 		// correct speech and music.
 
-		setCD(_resFiles[cluFileNum].cd);
+		if (Sword2Engine::isPsx()) // We have only one disk in PSX version 
+			setCD(CD1);
+		else
+			setCD(_resFiles[cluFileNum].cd);
 
 		// Actually, as long as the file can be found we don't really
 		// care which CD it's on. But if we can't find it, keep asking
@@ -481,6 +506,25 @@
 }
 
 /**
+ * Fetch resource type
+ */
+
+uint8 ResourceManager::fetchType(byte *ptr) {
+	if (!Sword2Engine::isPsx()) {
+		return ptr[0];
+	} else { // in PSX version, some files got a "garbled" resource header, with type stored in ninth byte
+		if (ptr[0]) { 
+			return ptr[0];
+		} else if (ptr[8]) {
+			return ptr[8];
+		} else  {            // In PSX version there is no resource header for audio files,
+			return WAV_FILE; // but hopefully all audio files got first 16 bytes zeroed,
+		}                    // Allowing us to check for this condition.
+
+	}
+}
+
+/**
  * Returns the total file length of a resource - i.e. all headers are included
  * too.
  */

Modified: scummvm/trunk/engines/sword2/resman.h
===================================================================
--- scummvm/trunk/engines/sword2/resman.h	2009-04-07 17:43:49 UTC (rev 39895)
+++ scummvm/trunk/engines/sword2/resman.h	2009-04-07 19:52:46 UTC (rev 39896)
@@ -64,7 +64,7 @@
 	void removeFromCacheList(Resource *res);
 	void addToCacheList(Resource *res);
 	void checkMemUsage();
-
+	
 	Sword2Engine *_vm;
 
 	int _curCD;
@@ -96,18 +96,15 @@
 
 	bool checkValid(uint32 res);
 	uint32 fetchLen(uint32 res);
+	uint8 fetchType(byte *ptr);
 	uint8 fetchType(uint32 res) {
 		byte *ptr = openResource(res);
-		uint8 type = ptr[0];
+		uint8 type = fetchType(ptr);
 		closeResource(res);
 
 		return type;
 	}
 
-	uint8 fetchType(byte *ptr) {
-		return ptr[0];
-	}
-
 	byte *fetchName(uint32 res, byte *buf = NULL) {
 		static byte tempbuf[NAME_LEN];
 

Modified: scummvm/trunk/engines/sword2/screen.cpp
===================================================================
--- scummvm/trunk/engines/sword2/screen.cpp	2009-04-07 17:43:49 UTC (rev 39895)
+++ scummvm/trunk/engines/sword2/screen.cpp	2009-04-07 19:52:46 UTC (rev 39896)
@@ -76,7 +76,7 @@
 	_needFullRedraw = false;
 
 	memset(&_thisScreen, 0, sizeof(_thisScreen));
-
+	
 	_fps = 0;
 	_frameCount = 0;
 	_cycleTime = 0;
@@ -100,9 +100,18 @@
 
 	_pauseTicks = 0;
 	_pauseStartTick = 0;
+
+	// Clean the cache for PSX version SCREENS.CLU 
+	_psxScrCache[0] = NULL;
+	_psxScrCache[1] = NULL;
+	_psxScrCache[2] = NULL;
+	_psxCacheEnabled[0] = true;
+	_psxCacheEnabled[1] = true;
+	_psxCacheEnabled[2] = true;
 }
 
 Screen::~Screen() {
+	flushPsxScrCache();
 	free(_buffer);
 	free(_dirtyGrid);
 	closeBackgroundLayer();
@@ -277,7 +286,8 @@
 
 	MultiScreenHeader screenLayerTable;
 
-	screenLayerTable.read(file + ResHeader::size());
+	if (!Sword2Engine::isPsx()) // On PSX version, there would be nothing to read here
+		screenLayerTable.read(file + ResHeader::size());
 
 	// Render at least one frame, but if the screen is scrolling, and if
 	// there is time left, we will render extra frames to smooth out the
@@ -285,13 +295,13 @@
 
 	do {
 		// first background parallax + related anims
-		if (screenLayerTable.bg_parallax[0]) {
+		if (Sword2Engine::isPsx() || screenLayerTable.bg_parallax[0]) { // No need to check on PSX version
 			renderParallax(_vm->fetchBackgroundParallaxLayer(file, 0), 0);
 			drawBackPar0Frames();
 		}
 
 		// second background parallax + related anims
-		if (screenLayerTable.bg_parallax[1]) {
+		if (!Sword2Engine::isPsx() && screenLayerTable.bg_parallax[1]) { // Nothing here in PSX version
 			renderParallax(_vm->fetchBackgroundParallaxLayer(file, 1), 1);
 			drawBackPar1Frames();
 		}
@@ -306,14 +316,14 @@
 
 		// first foreground parallax + related anims
 
-		if (screenLayerTable.fg_parallax[0]) {
+		if (Sword2Engine::isPsx() || screenLayerTable.fg_parallax[0]) {
 			renderParallax(_vm->fetchForegroundParallaxLayer(file, 0), 3);
 			drawForePar0Frames();
 		}
 
 		// second foreground parallax + related anims
 
-		if (screenLayerTable.fg_parallax[1]) {
+		if (!Sword2Engine::isPsx() && screenLayerTable.fg_parallax[1]) {
 			renderParallax(_vm->fetchForegroundParallaxLayer(file, 1), 4);
 			drawForePar1Frames();
 		}
@@ -333,6 +343,7 @@
 	} while (!endRenderCycle());
 
 	_vm->_resman->closeResource(_thisScreen.background_layer_id);
+
 }
 
 /**
@@ -381,6 +392,7 @@
 	spriteInfo.blend = 0;
 	spriteInfo.data = text_spr + FrameHeader::size();
 	spriteInfo.colourTable = 0;
+	spriteInfo.isText = true;
 
 	uint32 rv = drawSprite(&spriteInfo);
 	if (rv)
@@ -490,6 +502,7 @@
 }
 
 void Screen::processLayer(byte *file, uint32 layer_number) {
+
 	LayerHeader layer_head;
 
 	layer_head.read(_vm->fetchLayerHeader(file, layer_number));
@@ -503,9 +516,19 @@
 	spriteInfo.scaledWidth = 0;
 	spriteInfo.scaledHeight = 0;
 	spriteInfo.h = layer_head.height;
-	spriteInfo.type = RDSPR_TRANS | RDSPR_RLE256FAST;
+	spriteInfo.isText = false;
+
+	// Layers are uncompressed in PSX version, RLE256 compressed
+	// in PC version.
+	if (Sword2Engine::isPsx()) { 
+		spriteInfo.type = RDSPR_TRANS | RDSPR_NOCOMPRESSION;
+		spriteInfo.data = file + layer_head.offset;
+	} else {
+		spriteInfo.type = RDSPR_TRANS | RDSPR_RLE256FAST;
+		spriteInfo.data = file + ResHeader::size() + layer_head.offset;
+	}
+
 	spriteInfo.blend = 0;
-	spriteInfo.data = file + ResHeader::size() + layer_head.offset;
 	spriteInfo.colourTable = 0;
 
 	// check for largest layer for debug info
@@ -575,6 +598,8 @@
 			// points to just after last cdt_entry, ie.
 			// start of colour table
 			colTablePtr = _vm->fetchAnimHeader(file) + AnimHeader::size() + anim_head.noAnimFrames * CdtEntry::size();
+			if (Sword2Engine::isPsx())
+				colTablePtr++; // There is one additional byte to skip before the table in psx version
 			break;
 		}
 	}
@@ -598,6 +623,7 @@
 	// points to just after frame header, ie. start of sprite data
 	spriteInfo.data = frame + FrameHeader::size();
 	spriteInfo.colourTable = colTablePtr;
+	spriteInfo.isText = false;
 
 	// check for largest layer for debug info
 	uint32 current_sprite_area = frame_head.width * frame_head.height;
@@ -864,7 +890,7 @@
 	//     that this is a coincidence, but let's use the image palette
 	//     directly anyway, just to be safe.
 	//
-	// credits.clu  - The credits text
+	// credits.clu  - The credits text (credits.txt in PSX version)
 	//
 	//     This is simply a text file with CRLF line endings.
 	//     '^' is not shown, but used to mark the center of the line.
@@ -883,6 +909,8 @@
 	Common::File f;
 	int i;
 
+	spriteInfo.isText = false;
+
 	// Read the "Smacker" logo
 
 	uint16 logoWidth = 0;
@@ -925,9 +953,16 @@
 	int paragraphStart = 0;
 	bool hasCenterMark = false;
 
-	if (!f.open("credits.clu")) {
-		warning("Can't find credits.clu");
-		return;
+	if (Sword2Engine::isPsx()) {
+		if (!f.open("credits.txt")) {
+			warning("Can't find credits.txt");
+			return;
+		}
+	} else {
+		if (!f.open("credits.clu")) {
+			warning("Can't find credits.clu");
+			return;
+		}
 	}
 
 	while (1) {
@@ -1088,6 +1123,7 @@
 				spriteInfo.w = frame.width;
 				spriteInfo.h = frame.height;
 				spriteInfo.data = creditsLines[i]->sprite + FrameHeader::size();
+				spriteInfo.isText = true;
 
 				switch (creditsLines[i]->type) {
 				case LINE_LEFT:
@@ -1214,6 +1250,7 @@
 	barSprite.blend = 0;
 	barSprite.colourTable = 0;
 	barSprite.data = frame + FrameHeader::size();
+	barSprite.isText = false;
 
 	drawSprite(&barSprite);
 
@@ -1234,4 +1271,44 @@
 	waitForFade();
 }
 
+// Following functions are used to manage screen cache for psx version.
+
+void Screen::setPsxScrCache(byte *psxScrCache, uint8 level) {
+		if (level < 3) {
+			if (psxScrCache)
+				_psxCacheEnabled[level] = true;
+			else
+				_psxCacheEnabled[level] = false;
+
+			_psxScrCache[level] = psxScrCache;
+		}
+}
+
+byte *Screen::getPsxScrCache(uint8 level) {
+	if (level > 3) {
+		level = 0;
+	}
+
+	if (_psxCacheEnabled[level])
+		return _psxScrCache[level];
+	else
+		return NULL;
+}
+
+bool Screen::getPsxScrCacheStatus(uint8 level) {
+	if (level > 3) {
+		level = 0;
+	}
+	
+	return _psxCacheEnabled[level];
+}
+
+void Screen::flushPsxScrCache() {
+	for (uint8 i = 0; i < 3; i++) {
+		free(_psxScrCache[i]);
+		_psxScrCache[i] = NULL;
+		_psxCacheEnabled[i] = true;
+	}
+}
+
 } // End of namespace Sword2

Modified: scummvm/trunk/engines/sword2/screen.h
===================================================================
--- scummvm/trunk/engines/sword2/screen.h	2009-04-07 17:43:49 UTC (rev 39895)
+++ scummvm/trunk/engines/sword2/screen.h	2009-04-07 19:52:46 UTC (rev 39896)
@@ -173,6 +173,7 @@
 	uint16 blend;		// holds the blending values.
 	byte *data;		// pointer to the sprite data
 	byte *colourTable;	// pointer to 16-byte colour table, only applicable to 16-col compression type
+	bool isText;		// It is a engine-generated sprite containing text
 };
 
 struct BlockSurface {
@@ -214,7 +215,7 @@
 	// positions, etc.
 
 	ScreenInfo _thisScreen;
-
+	
 	int32 _renderCaps;
 	int8 _renderLevel;
 
@@ -330,15 +331,23 @@
 
 	void mirrorSprite(byte *dst, byte *src, int16 w, int16 h);
 	int32 decompressRLE256(byte *dst, byte *src, int32 decompSize);
-	void unwindRaw16(byte *dst, byte *src, uint8 blockSize, byte *colTable);
+	void unwindRaw16(byte *dst, byte *src, uint16 blockSize, byte *colTable);
 	int32 decompressRLE16(byte *dst, byte *src, int32 decompSize, byte *colTable);
 	void renderParallax(byte *ptr, int16 layer);
 
+
 	void markAsDirty(int16 x0, int16 y0, int16 x1, int16 y1);
 
 	uint8 _xBlocks[MAXLAYERS];
 	uint8 _yBlocks[MAXLAYERS];
 
+	// This is used to cache PSX backgrounds and parallaxes
+	// data, as they are kept in a file unmanageable from
+	// resource manager. These gets freed everytime an user
+	// exits from a room.
+	byte *_psxScrCache[3];
+	bool _psxCacheEnabled[3];
+
 	// An array of sub-blocks, one for each of the parallax layers.
 
 	BlockSurface **_blockSurfaces[MAXLAYERS];
@@ -396,11 +405,14 @@
 
 	void setLocationMetrics(uint16 w, uint16 h);
 	int32 initialiseBackgroundLayer(byte *parallax);
+	int32 initialisePsxParallaxLayer(byte *parallax);   // These are used to initialize psx backgrounds and
+	int32 initialisePsxBackgroundLayer(byte *parallax); // parallaxes, which are different from pc counterparts.
 	void closeBackgroundLayer();
 
 	void initialiseRenderCycle();
 
 	void initBackground(int32 res, int32 new_palette);
+	void initPsxBackground(int32 res, int32 new_palette);
 	void registerFrame(byte *ob_mouse, byte *ob_graph, byte *ob_mega);
 
 	void setScrollFraction(uint8 f) { _scrollFraction = f; }
@@ -446,6 +458,22 @@
 
 	void rollCredits();
 	void splashScreen();
+
+	// Some sprites are compressed in HIF format 
+	static uint32 decompressHIF(byte *src, byte *dst, uint32 *skipData = NULL);
+	// This is used to resize psx sprites back to original resolution
+	static void resizePsxSprite(byte *dst, byte *src, uint16 destW, uint16 destH);
+	// Some sprites are divided into 254 pixel wide stripes, this recomposes them
+	// and generates a "normal" sprite.
+	static void recomposePsxSprite(SpriteInfo *s);
+	static void recomposeCompPsxSprite(SpriteInfo *s);
+
+	// These functions manage the PSX screen cache
+	void setPsxScrCache(byte *psxScrCache, uint8 level);
+	byte *getPsxScrCache(uint8 level);
+	bool getPsxScrCacheStatus(uint8 level);
+	void flushPsxScrCache();
+
 };
 
 } // End of namespace Sword2

Modified: scummvm/trunk/engines/sword2/sound.cpp
===================================================================
--- scummvm/trunk/engines/sword2/sound.cpp	2009-04-07 17:43:49 UTC (rev 39895)
+++ scummvm/trunk/engines/sword2/sound.cpp	2009-04-07 19:52:46 UTC (rev 39896)
@@ -47,6 +47,7 @@
 #include "sword2/sound.h"
 
 #include "sound/wave.h"
+#include "sound/vag.h"
 
 namespace Sword2 {
 
@@ -209,10 +210,15 @@
 	}
 
 	byte *data = _vm->_resman->openResource(res);
-	uint32 len = _vm->_resman->fetchLen(res) - ResHeader::size();
-
+	uint32 len = _vm->_resman->fetchLen(res);
+	
 	assert(_vm->_resman->fetchType(data) == WAV_FILE);
-	data += ResHeader::size();
+	
+	// In PSX version we have nothing to skip here, as data starts right away
+	if (!Sword2Engine::isPsx()) {
+		data += ResHeader::size();
+		len -= ResHeader::size();
+	}
 
 	_vm->_sound->playFx(handle, data, len, Audio::Mixer::kMaxChannelVolume, 0, false, Audio::Mixer::kMusicSoundType);
 }
@@ -263,8 +269,12 @@
 
 			assert(_vm->_resman->fetchType(data) == WAV_FILE);
 
-			uint32 len = _vm->_resman->fetchLen(res) - ResHeader::size();
+			uint32 len = _vm->_resman->fetchLen(res);
 
+			// Skip the header if using PC version
+			if (!Sword2Engine::isPsx())
+				len -= ResHeader::size();
+
 			if (type == FX_RANDOM) {
 				// For spot effects and loops the delay is the
 				// number of frames to wait. For random
@@ -281,7 +291,12 @@
 				pan = -pan;
 
 			_fxQueue[i].resource = res;
-			_fxQueue[i].data = data + ResHeader::size();
+
+			if (Sword2Engine::isPsx())
+				_fxQueue[i].data = data;
+			else
+				_fxQueue[i].data = data + ResHeader::size();
+			
 			_fxQueue[i].len = len;
 			_fxQueue[i].delay = delay;
 			_fxQueue[i].volume = volume;
@@ -311,22 +326,27 @@
 	if (_vm->_mixer->isSoundHandleActive(*handle))
 		return RDERR_FXALREADYOPEN;
 
-	Common::MemoryReadStream stream(data, len);
+	Common::MemoryReadStream *stream = new Common::MemoryReadStream(data, len);
 	int rate, size;
 	byte flags;
 
-	if (!Audio::loadWAVFromStream(stream, size, rate, flags)) {
-		warning("playFX: Not a valid WAV file");
-		return RDERR_INVALIDWAV;
-	}
+	if (Sword2Engine::isPsx()) {
+		_vm->_mixer->playInputStream(soundType, handle, new Audio::VagStream(stream, loop), -1, vol, pan, true, false, isReverseStereo());
+	} else {
+		if (!Audio::loadWAVFromStream(*stream, size, rate, flags)) {
+			warning("playFX: Not a valid WAV file");
+			return RDERR_INVALIDWAV;
+		}
 
-	if (isReverseStereo())
-		flags |= Audio::Mixer::FLAG_REVERSE_STEREO;
+		if (isReverseStereo())
+			flags |= Audio::Mixer::FLAG_REVERSE_STEREO;
 
-	if (loop)
-		flags |= Audio::Mixer::FLAG_LOOP;
+		if (loop)
+			flags |= Audio::Mixer::FLAG_LOOP;
 
-	_vm->_mixer->playRaw(soundType, handle, data + stream.pos(), size, rate, flags, -1, vol, pan, 0, 0);
+		_vm->_mixer->playRaw(soundType, handle, data + stream->pos(), size, rate, flags, -1, vol, pan, 0, 0);
+	}
+
 	return RD_OK;
 }
 

Modified: scummvm/trunk/engines/sword2/sound.h
===================================================================
--- scummvm/trunk/engines/sword2/sound.h	2009-04-07 17:43:49 UTC (rev 39895)
+++ scummvm/trunk/engines/sword2/sound.h	2009-04-07 19:52:46 UTC (rev 39896)
@@ -231,7 +231,7 @@
 	int readBuffer(int16 *buffer, const int numSamples);
 	bool isStereo() const { return false; }
 	bool endOfData() const;
-	int getRate() const { return 22050; }
+	int getRate() const { return Sword2Engine::isPsx() ? 11025 : 22050; }
 
 	// End of AudioStream API
 

Modified: scummvm/trunk/engines/sword2/sprite.cpp
===================================================================
--- scummvm/trunk/engines/sword2/sprite.cpp	2009-04-07 17:43:49 UTC (rev 39895)
+++ scummvm/trunk/engines/sword2/sprite.cpp	2009-04-07 19:52:46 UTC (rev 39896)
@@ -25,8 +25,8 @@
  * $Id$
  */
 
+#include "common/endian.h"
 
-
 #include "sword2/sword2.h"
 #include "sword2/defs.h"
 #include "sword2/screen.h"
@@ -138,17 +138,24 @@
  * Unwinds a run of 16-colour data into 256-colour palette data.
  */
 
-void Screen::unwindRaw16(byte *dst, byte *src, uint8 blockSize, byte *colTable) {
+void Screen::unwindRaw16(byte *dst, byte *src, uint16 blockSize, byte *colTable) {
 	// for each pair of pixels
 	while (blockSize > 1) {
-		// 1st colour = number in table at position given by upper
-		// nibble of source byte
-		*dst++ = colTable[(*src) >> 4];
+		
+		if (Sword2Engine::isPsx()) {
+			// 1st colour = number in table at position given by upper
+			// nibble of source byte
+			*dst++ = colTable[(*src) & 0x0f];
 
-		// 2nd colour = number in table at position given by lower
-		// nibble of source byte
-		*dst++ = colTable[(*src) & 0x0f];
+			// 2nd colour = number in table at position given by lower
+			// nibble of source byte
+			*dst++ = colTable[(*src) >> 4];
+		} else {
+			*dst++ = colTable[(*src) >> 4];
+			*dst++ = colTable[(*src) & 0x0f];
+		}
 
+
 		// point to next source byte
 		src++;
 
@@ -245,6 +252,118 @@
 }
 
 /**
+ * This function takes a compressed HIF image and decompresses it.
+ * Used for PSX version sprites.
+ * @param dst destination buffer
+ * @param src source buffer
+ * @param skipData if pointer != NULL, value of pointed var
+ * is set to number of bytes composing the compressed data.
+ */
+
+uint32 Screen::decompressHIF(byte *src, byte *dst, uint32 *skipData) {
+	uint32 decompSize = 0;
+	uint32 readByte = 0;
+
+	for (;;) { // Main loop
+		byte control_byte = *src++;
+		readByte++;
+		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;
+				readByte += 2;
+				if (info_word == 0xFFFF) { // Got 0xFFFF code, finished.
+					if (skipData != NULL) *(skipData) = readByte;
+					return decompSize;
+				}
+
+				int32 repeat_count = (info_word >> 12) + 2; // How many time data needs to be refetched
+				while(repeat_count >= 0) {
+					uint16 refetchData = (info_word & 0xFFF) + 1;
+					if (refetchData > decompSize) return 0; // We have a problem here...
+					uint8 *old_data_src = dst - refetchData; 
+					*dst++ = *old_data_src;
+					decompSize++;
+					repeat_count--;
+				}
+			} else {
+				*dst++ = *src++;
+				readByte++;
+				decompSize++;
+			}
+			byte_count++;
+			control_byte <<= 1; // Shifting left the control code one bit
+		}
+	}
+}
+
+// Double line image to keep aspect ratio. 
+// Used in PSX version.
+void Screen::resizePsxSprite(byte *dst, byte *src, uint16 destW, uint16 destH) {
+	for (int i = 0; i < destH / 2; i++) {
+		memcpy(dst + i * destW * 2, src + i * destW, destW);
+		memcpy(dst + i * destW * 2 + destW, src + i * destW, destW);
+	}
+}
+
+// Sprites wider than 254px in PSX version are divided
+// into slices, this recomposes the image.
+void Screen::recomposePsxSprite(SpriteInfo *s) {
+	if (!s)
+		return;
+
+	uint16 noStripes = (s->w / 254) + ((s->w % 254) ? 1 : 0);
+	uint16 lastStripeSize = (s->w % 254) ? s->w % 254 : 254;
+	byte *buffer = (byte *)malloc(s->w * s->h / 2);
+
+	memset(buffer, 0, s->w * s->h / 2);
+
+	for (int idx = 0; idx < noStripes; idx++) {
+		uint16 stripeSize = (idx == noStripes - 1) ? lastStripeSize : 254;
+		for (int line = 0; line < s->h / 2; line++) {
+			memcpy(buffer + idx * 254 + line * s->w, s->data, stripeSize);
+			s->data += stripeSize;
+		}
+	}
+
+	s->data = buffer;
+
+}
+
+// Recomposes sprites wider than 254 pixels but also
+// compressed with HIF.
+// Used in PSX version.
+void Screen::recomposeCompPsxSprite(SpriteInfo *s) {
+	if (!s)
+		return;
+
+	uint16 noStripes = (s->w / 254) + ((s->w % 254) ? 1 : 0);
+	uint16 lastStripeSize = (s->w % 254) ? s->w % 254 : 254;
+	byte *buffer = (byte *)malloc(s->w * s->h / 2);
+	byte *stripeBuffer = (byte *)malloc(254 * s->h);;
+
+	memset(buffer, 0, s->w * s->h / 2);
+	uint32 skipData = 0;
+	uint32 compBytes = 0;
+
+	for (int idx = 0; idx < noStripes; idx++) {
+		uint16 stripeSize = (idx == noStripes - 1) ? lastStripeSize : 254;
+
+		decompressHIF((s->data) + skipData, stripeBuffer, &compBytes);
+		skipData += compBytes;
+
+		for (int line = 0; line < s->h / 2; line++) {
+			memcpy(buffer + idx * 254 + line * s->w, stripeBuffer + line * stripeSize, stripeSize);
+		}
+	}
+
+	free(stripeBuffer);
+	s->data = buffer;
+
+}
+
+/**
  * Creates a sprite surface. Sprite surfaces are used by the in-game dialogs
  * and for displaying cutscene subtitles, which makes them much easier to draw
  * than standard sprites.
@@ -378,23 +497,90 @@
 	// -----------------------------------------------------------------
 	// Decompression and mirroring
 	// -----------------------------------------------------------------
+	if (s->type & RDSPR_NOCOMPRESSION) {
+		if(Sword2Engine::isPsx()) { // PSX Uncompressed sprites
+			if (s->w > 254 && !s->isText) { // We need to recompose these frames
+				recomposePsxSprite(s);
+			}
 
-	if (s->type & RDSPR_NOCOMPRESSION)
-		sprite = s->data;
-	else {
-		sprite = (byte *)malloc(s->w * s->h);
+			freeSprite = true;
+			byte *tempBuf = (byte *)malloc(s->w * s->h * 2);
+			memset(tempBuf, 0, s->w * s->h * 2);
+			resizePsxSprite(tempBuf, s->data, s->w, s->h);
+
+			if (s->w > 254 && !s->isText) {
+				free(s->data);
+			}
+
+			sprite = tempBuf;
+		} else { // PC Uncompressed sprites
+			sprite = s->data;
+		}
+	} else {
 		freeSprite = true;
-		if (!sprite)
-			return RDERR_OUTOFMEMORY;
+
 		if ((s->type & 0xff00) == RDSPR_RLE16) {
-			if (decompressRLE16(sprite, s->data, s->w * s->h, s->colourTable)) {
-				free(sprite);
-				return RDERR_DECOMPRESSION;
+			if (Sword2Engine::isPsx()) { // PSX HIF16 sprites
+				uint32 decompData;
+				byte *tempBuf = (byte *)malloc(s->w * s->h);
+				memset(tempBuf, 0, s->w * s->h);
+
+				decompData = decompressHIF(s->data, tempBuf);
+
+				s->w = (decompData / (s->h / 2)) * 2;
+				byte *tempBuf2 = (byte *)malloc(s->w * s->h * 10);
+				memset(tempBuf2, 0, s->w * s->h * 2);
+
+				unwindRaw16(tempBuf2, tempBuf, (s->w * (s->h / 2)), s->colourTable);
+				sprite = (byte *)malloc(s->w * s->h);
+
+				if (!sprite)
+					return RDERR_OUTOFMEMORY;
+
+				resizePsxSprite(sprite, tempBuf2, s->w, s->h);
+
+				free(tempBuf2);
+				free(tempBuf);
+			} else { // PC RLE16 sprites
+				sprite = (byte *)malloc(s->w * s->h);
+				
+				if (!sprite)
+					return RDERR_OUTOFMEMORY;
+
+				if (decompressRLE16(sprite, s->data, s->w * s->h, s->colourTable)) {
+					free(sprite);
+					return RDERR_DECOMPRESSION;
+				}
 			}
 		} else {
-			if (decompressRLE256(sprite, s->data, s->w * s->h)) {
-				free(sprite);
-				return RDERR_DECOMPRESSION;
+			if (Sword2Engine::isPsx()) { // PSX HIF256 sprites
+				if (s->w > 255) {
+					sprite = (byte *)malloc(s->w * s->h);
+					recomposeCompPsxSprite(s);
+					resizePsxSprite(sprite, s->data, s->w, s->h);
+					free(s->data);
+				} else {
+					byte *tempBuf = (byte *)malloc(s->w * s->h);
+					uint32 decompData = decompressHIF(s->data, tempBuf);
+					s->w = (decompData / (s->h / 2));
+					sprite = (byte *)malloc(s->w * s->h);
+				
+					if (!sprite)
+						return RDERR_OUTOFMEMORY;
+
+					resizePsxSprite(sprite, tempBuf, s->w, s->h);
+					free(tempBuf);
+				}
+			} else { // PC RLE256 sprites
+				sprite = (byte *)malloc(s->w * s->h);
+
+				if (!sprite)
+					return RDERR_OUTOFMEMORY;
+
+				if (decompressRLE256(sprite, s->data, s->w * s->h)) {
+					free(sprite);
+					return RDERR_DECOMPRESSION;
+				}
 			}
 		}
 	}
@@ -500,7 +686,9 @@
 			return RDERR_OUTOFMEMORY;
 		}
 
-		if (_renderCaps & RDBLTFX_EDGEBLEND)
+		// We cannot use good scaling for PSX version, as we are missing 
+		// some required data.
+		if (_renderCaps & RDBLTFX_EDGEBLEND && !Sword2Engine::isPsx())
 			scaleImageGood(newSprite, s->scaledWidth, s->scaledWidth, s->scaledHeight, sprite, s->w, s->w, s->h, _buffer + _screenWide * rd.top + rd.left);
 		else
 			scaleImageFast(newSprite, s->scaledWidth, s->scaledWidth, s->scaledHeight, sprite, s->w, s->w, s->h);
@@ -518,8 +706,10 @@
 	// The light mask is an optional layer that covers the entire room
 	// and which is used to simulate light and shadows. Scaled sprites
 	// (actors, presumably) are always affected.
+	// Light masking makes use of palette match table, so it's unavailable
+	// in PSX version.
 
-	if ((_renderCaps & RDBLTFX_SHADOWBLEND) && _lightMask && (scale != 256 || (s->type & RDSPR_SHADOW))) {
+	if ((_renderCaps & RDBLTFX_SHADOWBLEND) && _lightMask && (scale != 256 || ((s->type & RDSPR_SHADOW) && !Sword2Engine::isPsx()) )) {
 		byte *lightMap;
 
 		// Make sure that we never apply the shadow to the original
@@ -557,7 +747,7 @@
 	src = sprite + rs.top * srcPitch + rs.left;
 	dst = _buffer + _screenWide * rd.top + rd.left;
 
-	if (s->type & RDSPR_BLEND) {
+	if (s->type & RDSPR_BLEND && !Sword2Engine::isPsx()) { // Blending is unavailable in PSX version
 		// The original code had two different blending cases. One for
 		// s->blend & 0x01 and one for s->blend & 0x02. However, the
 		// only values that actually appear in the cluster files are
@@ -639,6 +829,9 @@
 	if (!_lightMask)
 		return RDERR_OUTOFMEMORY;
 
+	if (s->data == NULL) // Check, as there's no mask in psx version
+		return RDERR_NOTOPEN;
+
 	if (decompressRLE256(_lightMask, s->data, s->w * s->h))
 		return RDERR_DECOMPRESSION;
 

Modified: scummvm/trunk/engines/sword2/sword2.cpp
===================================================================
--- scummvm/trunk/engines/sword2/sword2.cpp	2009-04-07 17:43:49 UTC (rev 39895)
+++ scummvm/trunk/engines/sword2/sword2.cpp	2009-04-07 19:52:46 UTC (rev 39896)
@@ -51,6 +51,7 @@
 #include "sword2/router.h"
 #include "sword2/screen.h"
 #include "sword2/sound.h"
+#include "sword2/saveload.h"
 
 namespace Sword2 {
 
@@ -104,7 +105,9 @@
 bool Sword2::Sword2Engine::hasFeature(EngineFeature f) const {
 	return
 		(f == kSupportsRTL) ||
-		(f == kSupportsSubtitleOptions);
+		(f == kSupportsSubtitleOptions) ||
+		(f == kSupportsSavingDuringRuntime) ||
+		(f == kSupportsLoadingDuringRuntime);
 }
 
 GameList Sword2MetaEngine::getSupportedGames() const {
@@ -299,6 +302,8 @@
 	_gameCycle = 0;
 	_gameSpeed = 1;
 
+	_gmmLoadSlot = -1; // Used to manage GMM Loading
+
 	syst->getEventManager()->registerRandomSource(_rnd, "sword2");
 }
 
@@ -429,8 +434,8 @@
 			if (!dialog.runModal())
 				startGame();
 		}
-	} else if (!_bootParam && saveExists()) {
-		int32 pars[2] = { 221, FX_LOOP };
+	} else if (!_bootParam && saveExists() && !isPsx()) { // Initial load/restart panel disabled in PSX
+		int32 pars[2] = { 221, FX_LOOP };                 // version because of missing panel resources
 		bool result;
 
 		_mouse->setMouse(NORMAL_MOUSE_ID);
@@ -465,6 +470,26 @@
 		}
 #endif
 
+		// Handle GMM Loading
+		if (_gmmLoadSlot != -1) {
+			
+			// Hide mouse cursor and fade screen
+			_mouse->hideMouse();
+			_screen->fadeDown();
+			
+			// Clean up and load game
+			_logic->_router->freeAllRouteMem();
+
+			// TODO: manage error handling
+			restoreGame(_gmmLoadSlot);
+
+			// Reset load slot
+			_gmmLoadSlot = -1;
+
+			// Show mouse
+			_mouse->addHuman();
+		}
+
 		KeyboardEvent *ke = keyboardEvent();
 
 		if (ke) {
@@ -805,4 +830,65 @@
 	return _system->getMillis();
 }
 
+Common::Error Sword2Engine::saveGameState(int slot, const char *desc) {
+	uint32 saveVal = saveGame(slot, (byte *)desc);
+
+	if (saveVal == SR_OK)
+		return Common::kNoError;
+	else if (saveVal == SR_ERR_WRITEFAIL || saveVal == SR_ERR_FILEOPEN)
+		return Common::kWritingFailed;
+	else
+		return Common::kUnknownError;
+}
+
+bool Sword2Engine::canSaveGameStateCurrently() {
+	bool canSave = true;
+
+	// No save if dead
+	if (_logic->readVar(DEAD))
+		canSave = false;
+
+	// No save if mouse not shown
+	else if (_mouse->getMouseStatus())
+		canSave = false;
+	// No save if inside a menu
+	else if (_mouse->getMouseMode() == MOUSE_system_menu)
+		canSave = false;
+
+	// No save if fading
+	else if (_screen->getFadeStatus())
+		canSave = false;
+
+	return canSave;
+}
+
+Common::Error Sword2Engine::loadGameState(int slot) {
+
+	// Prepare the game to load through GMM
+	_gmmLoadSlot = slot;
+
+	// TODO: error handling.
+	return Common::kNoError;
+}
+
+bool Sword2Engine::canLoadGameStateCurrently() {
+	bool canLoad = true;
+
+	// No load if mouse is disabled
+	if (_mouse->getMouseStatus())
+		canLoad = false;
+	// No load if mouse is in system menu
+	else if (_mouse->getMouseMode() == MOUSE_system_menu)
+		canLoad = false;
+	// No load if we are fading
+	else if (_screen->getFadeStatus())
+		canLoad = false;
+
+	// But if we are dead, ignore previous conditions
+	if (_logic->readVar(DEAD))
+		canLoad = true;
+
+	return canLoad;
+}
+
 } // End of namespace Sword2

Modified: scummvm/trunk/engines/sword2/sword2.h
===================================================================
--- scummvm/trunk/engines/sword2/sword2.h	2009-04-07 17:43:49 UTC (rev 39895)
+++ scummvm/trunk/engines/sword2/sword2.h	2009-04-07 19:52:46 UTC (rev 39896)
@@ -120,8 +120,17 @@
 	bool _useSubtitles;
 	int _gameSpeed;
 
+	// Used to trigger GMM Loading
+	int _gmmLoadSlot;
+
 	StartUp _startList[MAX_starts];
 
+	// We need these to fetch data from SCREENS.CLU, which is
+	// a resource file with custom format keeping background and
+	// parallax data (which is removed from multiscreen files).
+	byte *fetchPsxBackground(uint32 location);
+	byte *fetchPsxParallax(uint32 location, uint8 level); // level: 0 -> bg, 1 -> fg
+
 	// Original game platform (PC/PSX)
 	static Common::Platform _platform;
 
@@ -150,6 +159,12 @@
 	bool getSubtitles() { return _useSubtitles; }
 	void setSubtitles(bool b) { _useSubtitles = b; }
 
+	// GMM Loading/Saving
+	Common::Error saveGameState(int slot, const char *desc);
+	bool canSaveGameStateCurrently();
+	Common::Error loadGameState(int slot);
+	bool canLoadGameStateCurrently();	
+
 	uint32 _features;
 
 	MemoryManager *_memory;
@@ -203,7 +218,7 @@
 	byte *fetchTextLine(byte *file, uint32 text_line);
 	bool checkTextLine(byte *file, uint32 text_line);
 	byte *fetchPaletteMatchTable(byte *screenFile);
-
+	
 	uint32 saveGame(uint16 slotNo, byte *description);
 	uint32 restoreGame(uint16 slotNo);
 	uint32 getSaveDescription(uint16 slotNo, byte *description);


This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.




More information about the Scummvm-git-logs mailing list