mirror of
https://github.com/libretro/scummvm.git
synced 2024-12-26 19:55:44 +00:00
449 lines
13 KiB
C++
449 lines
13 KiB
C++
/* ScummVM - Graphic Adventure Engine
|
|
*
|
|
* ScummVM is the legal property of its developers, whose names
|
|
* are too numerous to list here. Please refer to the COPYRIGHT
|
|
* file distributed with this source distribution.
|
|
*
|
|
* Additional copyright for this file:
|
|
* Copyright (C) 1994-1998 Revolution Software Ltd.
|
|
*
|
|
* This program is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU General Public License
|
|
* as published by the Free Software Foundation; either version 2
|
|
* of the License, or (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|
*/
|
|
|
|
#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 {
|
|
|
|
/**
|
|
* Returns a pointer to the first palette entry, given the pointer to the start
|
|
* of the screen file.
|
|
*/
|
|
|
|
void Sword2Engine::fetchPalette(byte *screenFile, byte *palBuffer) {
|
|
byte *palette;
|
|
|
|
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;
|
|
|
|
mscreenHeader.read(screenFile + ResHeader::size());
|
|
palette = screenFile + ResHeader::size() + mscreenHeader.palette;
|
|
}
|
|
|
|
// Always set color 0 to black, because while most background screen
|
|
// palettes have a bright color 0 it should come out as black in the
|
|
// game.
|
|
|
|
palBuffer[0] = 0;
|
|
palBuffer[1] = 0;
|
|
palBuffer[2] = 0;
|
|
|
|
for (uint i = 4, j = 3; i < 4 * 256; i += 4, j += 3) {
|
|
palBuffer[j + 0] = palette[i + 0];
|
|
palBuffer[j + 1] = palette[i + 1];
|
|
palBuffer[j + 2] = palette[i + 2];
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 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());
|
|
|
|
return screenFile + ResHeader::size() + mscreenHeader.paletteTable;
|
|
}
|
|
|
|
/**
|
|
* Returns a pointer to the screen header, given the pointer to the start of
|
|
* the screen file.
|
|
*/
|
|
|
|
byte *Sword2Engine::fetchScreenHeader(byte *screenFile) {
|
|
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;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Returns a pointer to the requested layer header, given the pointer to the
|
|
* start of the screen file. Drops out if the requested layer number exceeds
|
|
* the number of layers on this screen.
|
|
*/
|
|
|
|
byte *Sword2Engine::fetchLayerHeader(byte *screenFile, uint16 layerNo) {
|
|
#ifdef SWORD2_DEBUG
|
|
ScreenHeader screenHead;
|
|
|
|
screenHead.read(fetchScreenHeader(screenFile));
|
|
assert(layerNo < screenHead.noLayers);
|
|
#endif
|
|
|
|
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());
|
|
|
|
return screenFile + ResHeader::size() + mscreenHeader.maskOffset;
|
|
}
|
|
|
|
/**
|
|
* Returns a pointer to the anim header, given the pointer to the start of the
|
|
* anim file.
|
|
*/
|
|
|
|
byte *Sword2Engine::fetchAnimHeader(byte *animFile) {
|
|
return animFile + ResHeader::size();
|
|
}
|
|
|
|
/**
|
|
* Returns a pointer to the requested frame number's cdtEntry, given the
|
|
* pointer to the start of the anim file. Drops out if the requested frame
|
|
* number exceeds the number of frames in this anim.
|
|
*/
|
|
|
|
byte *Sword2Engine::fetchCdtEntry(byte *animFile, uint16 frameNo) {
|
|
#ifdef SWORD2_DEBUG
|
|
AnimHeader animHead;
|
|
|
|
animHead.read(fetchAnimHeader(animFile));
|
|
|
|
if (frameNo > animHead->noAnimFrames - 1)
|
|
error("fetchCdtEntry(animFile,%d) - anim only %d frames", frameNo, animHead->noAnimFrames);
|
|
#endif
|
|
|
|
return fetchAnimHeader(animFile) + AnimHeader::size() + frameNo * CdtEntry::size();
|
|
}
|
|
|
|
/**
|
|
* Returns a pointer to the requested frame number's header, given the pointer
|
|
* to the start of the anim file. Drops out if the requested frame number
|
|
* exceeds the number of frames in this anim
|
|
*/
|
|
|
|
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));
|
|
|
|
return animFile + ResHeader::size() + cdt.frameOffset;
|
|
}
|
|
|
|
/**
|
|
* Returns a pointer to the requested parallax layer data.
|
|
*/
|
|
|
|
byte *Sword2Engine::fetchBackgroundParallaxLayer(byte *screenFile, int layer) {
|
|
if (isPsx()) {
|
|
byte *psxParallax = _screen->getPsxScrCache(0);
|
|
|
|
// 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();
|
|
|
|
// 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) {
|
|
if (isPsx()) {
|
|
byte *psxBackground = _screen->getPsxScrCache(1);
|
|
|
|
// Manage cache for psx backgrounds
|
|
if (psxBackground) { // Background is cached
|
|
return psxBackground;
|
|
} else { // Background not cached
|
|
uint32 locNo = _logic->getLocationNum();
|
|
|
|
// 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) {
|
|
if (isPsx()) {
|
|
byte *psxParallax = _screen->getPsxScrCache(2);
|
|
|
|
// 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();
|
|
|
|
// 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) {
|
|
TextHeader text_header;
|
|
static byte errorLine[128];
|
|
|
|
text_header.read(file + ResHeader::size());
|
|
|
|
if (text_line >= text_header.noOfLines) {
|
|
sprintf((char *)errorLine, "xxMissing line %d of %s (only 0..%d)", text_line, _resman->fetchName(file), text_header.noOfLines - 1);
|
|
|
|
// first 2 chars are NULL so that actor-number comes out as '0'
|
|
errorLine[0] = 0;
|
|
errorLine[1] = 0;
|
|
return errorLine;
|
|
}
|
|
|
|
// The "number of lines" field is followed by a lookup table
|
|
|
|
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 II: 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 II: 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) {
|
|
TextHeader text_header;
|
|
|
|
text_header.read(file + ResHeader::size());
|
|
|
|
return text_line < text_header.noOfLines;
|
|
}
|
|
|
|
} // End of namespace Sword2
|