Initial commit
This commit is contained in:
691
engines/touche/resource.cpp
Normal file
691
engines/touche/resource.cpp
Normal file
@@ -0,0 +1,691 @@
|
||||
/* 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.
|
||||
*
|
||||
* 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#include "common/textconsole.h"
|
||||
|
||||
#include "audio/decoders/flac.h"
|
||||
#include "audio/mixer.h"
|
||||
#include "audio/decoders/mp3.h"
|
||||
#include "audio/decoders/voc.h"
|
||||
#include "audio/decoders/vorbis.h"
|
||||
#include "audio/decoders/raw.h"
|
||||
#include "audio/audiostream.h"
|
||||
|
||||
#include "touche/midi.h"
|
||||
#include "touche/touche.h"
|
||||
#include "touche/graphics.h"
|
||||
|
||||
namespace Touche {
|
||||
|
||||
enum {
|
||||
kCurrentSpeechDataVersion = 1,
|
||||
kSpeechDataFileHeaderSize = 4
|
||||
};
|
||||
|
||||
struct CompressedSpeechFile {
|
||||
const char *filename;
|
||||
Audio::SeekableAudioStream *(*makeStream)(
|
||||
Common::SeekableReadStream *stream,
|
||||
DisposeAfterUse::Flag disposeAfterUse);
|
||||
};
|
||||
|
||||
static const CompressedSpeechFile compressedSpeechFilesTable[] = {
|
||||
#ifdef USE_FLAC
|
||||
{ "TOUCHE.SOF", Audio::makeFLACStream },
|
||||
#endif
|
||||
#ifdef USE_VORBIS
|
||||
{ "TOUCHE.SOG", Audio::makeVorbisStream },
|
||||
#endif
|
||||
#ifdef USE_MAD
|
||||
{ "TOUCHE.SO3", Audio::makeMP3Stream },
|
||||
#endif
|
||||
{ 0, 0 }
|
||||
};
|
||||
|
||||
void ToucheEngine::res_openDataFile() {
|
||||
if (!_fData.open("TOUCHE.DAT")) {
|
||||
error("Unable to open 'TOUCHE.DAT' for reading");
|
||||
}
|
||||
for (int i = 0; compressedSpeechFilesTable[i].filename; ++i) {
|
||||
if (_fSpeech[0].open(compressedSpeechFilesTable[i].filename)) {
|
||||
int version = _fSpeech[0].readUint16LE();
|
||||
if (version == kCurrentSpeechDataVersion) {
|
||||
_compressedSpeechData = i;
|
||||
return;
|
||||
}
|
||||
warning("Unhandled version %d for compressed sound file '%s'", version, compressedSpeechFilesTable[i].filename);
|
||||
return;
|
||||
}
|
||||
}
|
||||
// _fSpeech[0] opening/closing is driven by the scripts
|
||||
_fSpeech[1].open("OBJ");
|
||||
_compressedSpeechData = -1;
|
||||
}
|
||||
|
||||
void ToucheEngine::res_closeDataFile() {
|
||||
res_stopSpeech(); // stop any pending speech before closing the underlying streams
|
||||
_fData.close();
|
||||
_fSpeech[0].close();
|
||||
_fSpeech[1].close();
|
||||
}
|
||||
|
||||
void ToucheEngine::res_allocateTables() {
|
||||
_fData.seek(64);
|
||||
uint32 textDataOffs = _fData.readUint32LE();
|
||||
uint32 textDataSize = _fData.readUint32LE();
|
||||
_textData = (uint8 *)malloc(textDataSize);
|
||||
if (!_textData) {
|
||||
error("Unable to allocate memory for text data");
|
||||
}
|
||||
_fData.seek(textDataOffs);
|
||||
_fData.read(_textData, textDataSize);
|
||||
|
||||
_fData.seek(2);
|
||||
const int bw = _fData.readUint16LE();
|
||||
const int bh = _fData.readUint16LE();
|
||||
uint32 size = bw * bh;
|
||||
_backdropBuffer = (uint8 *)malloc(size);
|
||||
if (!_backdropBuffer) {
|
||||
error("Unable to allocate memory for backdrop buffer");
|
||||
}
|
||||
|
||||
_menuKitData = (uint8 *)malloc(42 * 120);
|
||||
if (!_menuKitData) {
|
||||
error("Unable to allocate memory for menu kit data");
|
||||
}
|
||||
|
||||
_convKitData = (uint8 *)malloc(152 * 80);
|
||||
if (!_convKitData) {
|
||||
error("Unable to allocate memory for conv kit data");
|
||||
}
|
||||
|
||||
for (int i = 0; i < NUM_SEQUENCES; ++i) {
|
||||
_sequenceDataTable[i] = (uint8 *)malloc(16384);
|
||||
if (!_sequenceDataTable[i]) {
|
||||
error("Unable to allocate memory for sequence data %d", i);
|
||||
}
|
||||
}
|
||||
|
||||
_programData = (uint8 *)malloc(kMaxProgramDataSize);
|
||||
if (!_programData) {
|
||||
error("Unable to allocate memory for program data");
|
||||
}
|
||||
|
||||
_mouseData = (uint8 *)malloc(kCursorWidth * kCursorHeight);
|
||||
if (!_mouseData) {
|
||||
error("Unable to allocate memory for mouse data");
|
||||
}
|
||||
|
||||
_iconData = (uint8 *)malloc(kIconWidth * kIconHeight);
|
||||
if (!_iconData) {
|
||||
error("Unable to allocate memory for object data");
|
||||
}
|
||||
|
||||
memset(_spritesTable, 0, sizeof(_spritesTable));
|
||||
|
||||
_offscreenBuffer = (uint8 *)malloc(kScreenWidth * kScreenHeight);
|
||||
if (!_offscreenBuffer) {
|
||||
error("Unable to allocate memory for offscreen buffer");
|
||||
}
|
||||
}
|
||||
|
||||
void ToucheEngine::res_deallocateTables() {
|
||||
free(_textData);
|
||||
_textData = nullptr;
|
||||
|
||||
free(_backdropBuffer);
|
||||
_backdropBuffer = nullptr;
|
||||
|
||||
free(_menuKitData);
|
||||
_menuKitData = nullptr;
|
||||
|
||||
free(_convKitData);
|
||||
_convKitData = nullptr;
|
||||
|
||||
for (int i = 0; i < NUM_SEQUENCES; ++i) {
|
||||
free(_sequenceDataTable[i]);
|
||||
_sequenceDataTable[i] = nullptr;
|
||||
}
|
||||
|
||||
free(_programData);
|
||||
_programData = nullptr;
|
||||
|
||||
free(_mouseData);
|
||||
_mouseData = nullptr;
|
||||
|
||||
free(_iconData);
|
||||
_iconData = nullptr;
|
||||
|
||||
for (int i = 0; i < NUM_SPRITES; ++i) {
|
||||
free(_spritesTable[i].ptr);
|
||||
_spritesTable[i].ptr = nullptr;
|
||||
}
|
||||
|
||||
free(_offscreenBuffer);
|
||||
_offscreenBuffer = nullptr;
|
||||
}
|
||||
|
||||
uint32 ToucheEngine::res_getDataOffset(ResourceType type, int num, uint32 *size) {
|
||||
debugC(9, kDebugResource, "ToucheEngine::res_getDataOffset() type=%d num=%d", type, num);
|
||||
static const struct ResourceData {
|
||||
int offs;
|
||||
int count;
|
||||
int type;
|
||||
} dataTypesTable[] = {
|
||||
{ 0x048, 100, kResourceTypeRoomImage },
|
||||
{ 0x228, 30, kResourceTypeSequence },
|
||||
{ 0x2A0, 50, kResourceTypeSpriteImage },
|
||||
{ 0x390, 100, kResourceTypeIconImage },
|
||||
{ 0x6B0, 80, kResourceTypeRoomInfo },
|
||||
{ 0x908, 150, kResourceTypeProgram },
|
||||
{ 0xB60, 50, kResourceTypeMusic },
|
||||
{ 0xC28, 120, kResourceTypeSound }
|
||||
};
|
||||
|
||||
const ResourceData *rd = NULL;
|
||||
for (unsigned int i = 0; i < ARRAYSIZE(dataTypesTable); ++i) {
|
||||
if (dataTypesTable[i].type == type) {
|
||||
rd = &dataTypesTable[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (rd == NULL) {
|
||||
error("Invalid resource type %d", type);
|
||||
}
|
||||
if (num < 0 || num > rd->count) {
|
||||
error("Invalid resource number %d (type %d)", num, type);
|
||||
}
|
||||
_fData.seek(rd->offs + num * 4);
|
||||
uint32 offs = _fData.readUint32LE();
|
||||
assert(offs != 0);
|
||||
if (size) {
|
||||
uint32 nextOffs = _fData.readUint32LE();
|
||||
*size = nextOffs - offs;
|
||||
}
|
||||
return offs;
|
||||
}
|
||||
|
||||
void ToucheEngine::res_loadSpriteImage(int num, uint8 *dst) {
|
||||
debugC(9, kDebugResource, "ToucheEngine::res_loadSpriteImage() num=%d", num);
|
||||
const uint32 offs = res_getDataOffset(kResourceTypeSpriteImage, num);
|
||||
_fData.seek(offs);
|
||||
_currentImageWidth = _fData.readUint16LE();
|
||||
_currentImageHeight = _fData.readUint16LE();
|
||||
for (int i = 0; i < _currentImageHeight; ++i) {
|
||||
res_decodeScanLineImageRLE(dst + _currentImageWidth * i, _currentImageWidth);
|
||||
}
|
||||
}
|
||||
|
||||
void ToucheEngine::res_loadProgram(int num) {
|
||||
debugC(9, kDebugResource, "ToucheEngine::res_loadProgram() num=%d", num);
|
||||
const uint32 offs = res_getDataOffset(kResourceTypeProgram, num, &_programDataSize);
|
||||
_fData.seek(offs);
|
||||
assert(_programDataSize <= kMaxProgramDataSize);
|
||||
_fData.read(_programData, _programDataSize);
|
||||
}
|
||||
|
||||
void ToucheEngine::res_decodeProgramData() {
|
||||
debugC(9, kDebugResource, "ToucheEngine::res_decodeProgramData()");
|
||||
|
||||
uint8 *p;
|
||||
uint8 *programDataEnd = _programData + _programDataSize;
|
||||
|
||||
p = _programData + READ_LE_UINT32(_programData + 32);
|
||||
_script.init(p);
|
||||
|
||||
p = _programData + READ_LE_UINT32(_programData + 4);
|
||||
_programTextDataPtr = p;
|
||||
|
||||
_programRectsTable.clear();
|
||||
p = _programData + READ_LE_UINT32(_programData + 20);
|
||||
while (p < programDataEnd) {
|
||||
int16 x = READ_LE_UINT16(p); p += 2;
|
||||
int16 y = READ_LE_UINT16(p); p += 2;
|
||||
int16 w = READ_LE_UINT16(p); p += 2;
|
||||
int16 h = READ_LE_UINT16(p); p += 2;
|
||||
_programRectsTable.push_back(Common::Rect(x, y, x + w, y + h));
|
||||
if (x == -1) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
_programPointsTable.clear();
|
||||
p = _programData + READ_LE_UINT32(_programData + 24);
|
||||
while (p < programDataEnd) {
|
||||
ProgramPointData ppd;
|
||||
ppd.x = READ_LE_UINT16(p); p += 2;
|
||||
ppd.y = READ_LE_UINT16(p); p += 2;
|
||||
ppd.z = READ_LE_UINT16(p); p += 2;
|
||||
ppd.order = READ_LE_UINT16(p); p += 2;
|
||||
_programPointsTable.push_back(ppd);
|
||||
if (ppd.x == -1) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
_programWalkTable.clear();
|
||||
p = _programData + READ_LE_UINT32(_programData + 28);
|
||||
while (p < programDataEnd) {
|
||||
ProgramWalkData pwd;
|
||||
pwd.point1 = READ_LE_UINT16(p); p += 2;
|
||||
if (pwd.point1 == -1) {
|
||||
break;
|
||||
}
|
||||
assert((uint16)pwd.point1 < _programPointsTable.size());
|
||||
pwd.point2 = READ_LE_UINT16(p); p += 2;
|
||||
assert((uint16)pwd.point2 < _programPointsTable.size());
|
||||
pwd.clippingRect = READ_LE_UINT16(p); p += 2;
|
||||
pwd.area1 = READ_LE_UINT16(p); p += 2;
|
||||
pwd.area2 = READ_LE_UINT16(p); p += 2;
|
||||
p += 12; // unused
|
||||
_programWalkTable.push_back(pwd);
|
||||
}
|
||||
|
||||
_programAreaTable.clear();
|
||||
p = _programData + READ_LE_UINT32(_programData + 8);
|
||||
while (p < programDataEnd) {
|
||||
ProgramAreaData pad;
|
||||
int16 x = READ_LE_UINT16(p); p += 2;
|
||||
if (x == -1) {
|
||||
break;
|
||||
}
|
||||
int16 y = READ_LE_UINT16(p); p += 2;
|
||||
int16 w = READ_LE_UINT16(p); p += 2;
|
||||
int16 h = READ_LE_UINT16(p); p += 2;
|
||||
pad.area.r = Common::Rect(x, y, x + w, y + h);
|
||||
pad.area.srcX = READ_LE_UINT16(p); p += 2;
|
||||
pad.area.srcY = READ_LE_UINT16(p); p += 2;
|
||||
pad.id = READ_LE_UINT16(p); p += 2;
|
||||
pad.state = READ_LE_UINT16(p); p += 2;
|
||||
pad.animCount = READ_LE_UINT16(p); p += 2;
|
||||
pad.animNext = READ_LE_UINT16(p); p += 2;
|
||||
_programAreaTable.push_back(pad);
|
||||
}
|
||||
|
||||
_programBackgroundTable.clear();
|
||||
p = _programData + READ_LE_UINT32(_programData + 12);
|
||||
while (p < programDataEnd) {
|
||||
ProgramBackgroundData pbd;
|
||||
int16 x = READ_LE_UINT16(p); p += 2;
|
||||
if (x == -1) {
|
||||
break;
|
||||
}
|
||||
int16 y = READ_LE_UINT16(p); p += 2;
|
||||
int16 w = READ_LE_UINT16(p); p += 2;
|
||||
int16 h = READ_LE_UINT16(p); p += 2;
|
||||
pbd.area.r = Common::Rect(x, y, x + w, y + h);
|
||||
pbd.area.srcX = READ_LE_UINT16(p); p += 2;
|
||||
pbd.area.srcY = READ_LE_UINT16(p); p += 2;
|
||||
pbd.type = READ_LE_UINT16(p); p += 2;
|
||||
pbd.offset = READ_LE_UINT16(p); p += 2;
|
||||
pbd.scaleMul = READ_LE_UINT16(p); p += 2;
|
||||
pbd.scaleDiv = READ_LE_UINT16(p); p += 2;
|
||||
_programBackgroundTable.push_back(pbd);
|
||||
}
|
||||
|
||||
_programHitBoxTable.clear();
|
||||
p = _programData + READ_LE_UINT32(_programData + 16);
|
||||
while (p < programDataEnd) {
|
||||
ProgramHitBoxData phbd;
|
||||
phbd.item = READ_LE_UINT16(p); p += 2;
|
||||
if (phbd.item == 0) {
|
||||
break;
|
||||
}
|
||||
phbd.talk = READ_LE_UINT16(p); p += 2;
|
||||
phbd.state = READ_LE_UINT16(p); p += 2;
|
||||
phbd.str = READ_LE_UINT16(p); p += 2;
|
||||
phbd.defaultStr = READ_LE_UINT16(p); p += 2;
|
||||
for (int i = 0; i < 8; ++i) {
|
||||
phbd.actions[i] = READ_LE_UINT16(p); p += 2;
|
||||
}
|
||||
for (int i = 0; i < 2; ++i) {
|
||||
int16 x = READ_LE_UINT16(p); p += 2;
|
||||
int16 y = READ_LE_UINT16(p); p += 2;
|
||||
int16 w = READ_LE_UINT16(p); p += 2;
|
||||
int16 h = READ_LE_UINT16(p); p += 2;
|
||||
phbd.hitBoxes[i].left = x;
|
||||
phbd.hitBoxes[i].top = y;
|
||||
phbd.hitBoxes[i].right = x + w;
|
||||
phbd.hitBoxes[i].bottom = y + h;
|
||||
}
|
||||
p += 8; // unused
|
||||
_programHitBoxTable.push_back(phbd);
|
||||
}
|
||||
|
||||
_programActionScriptOffsetTable.clear();
|
||||
p = _programData + READ_LE_UINT32(_programData + 36);
|
||||
while (p < programDataEnd) {
|
||||
ProgramActionScriptOffsetData pasod;
|
||||
pasod.object1 = READ_LE_UINT16(p); p += 2;
|
||||
if (pasod.object1 == 0) {
|
||||
break;
|
||||
}
|
||||
pasod.action = READ_LE_UINT16(p); p += 2;
|
||||
pasod.object2 = READ_LE_UINT16(p); p += 2;
|
||||
pasod.offset = READ_LE_UINT16(p); p += 2;
|
||||
_programActionScriptOffsetTable.push_back(pasod);
|
||||
}
|
||||
|
||||
_programConversationTable.clear();
|
||||
int count = (READ_LE_UINT32(_programData + 44) - READ_LE_UINT32(_programData + 40)) / 6;
|
||||
assert(count >= 0);
|
||||
p = _programData + READ_LE_UINT32(_programData + 40);
|
||||
while (p < programDataEnd && count != 0) {
|
||||
ProgramConversationData pcd;
|
||||
pcd.num = READ_LE_UINT16(p); p += 2;
|
||||
pcd.offset = READ_LE_UINT16(p); p += 2;
|
||||
pcd.msg = READ_LE_UINT16(p); p += 2;
|
||||
_programConversationTable.push_back(pcd);
|
||||
--count;
|
||||
}
|
||||
|
||||
_programKeyCharScriptOffsetTable.clear();
|
||||
p = _programData + READ_LE_UINT32(_programData + 44);
|
||||
while (p < programDataEnd) {
|
||||
ProgramKeyCharScriptOffsetData pksod;
|
||||
pksod.keyChar = READ_LE_UINT16(p); p += 2;
|
||||
if (pksod.keyChar == 0) {
|
||||
break;
|
||||
}
|
||||
pksod.offset = READ_LE_UINT16(p); p += 2;
|
||||
_programKeyCharScriptOffsetTable.push_back(pksod);
|
||||
}
|
||||
}
|
||||
|
||||
void ToucheEngine::res_loadRoom(int num) {
|
||||
debugC(9, kDebugResource, "ToucheEngine::res_loadRoom() num=%d flag115=%d", num, _flagsTable[115]);
|
||||
|
||||
debug(0, "Setting up room %d", num);
|
||||
|
||||
const uint32 offsInfo = res_getDataOffset(kResourceTypeRoomInfo, num);
|
||||
_fData.seek(offsInfo);
|
||||
_fData.skip(2);
|
||||
const int roomImageNum = _fData.readUint16LE();
|
||||
_fData.skip(2);
|
||||
_fData.read(_paletteBuffer, 3 * 256);
|
||||
|
||||
const uint32 offsImage = res_getDataOffset(kResourceTypeRoomImage, roomImageNum);
|
||||
_fData.seek(offsImage);
|
||||
res_loadBackdrop();
|
||||
|
||||
bool updateScreenPalette = _flagsTable[115] == 0;
|
||||
|
||||
// Workaround to what appears to be a scripting bug. Scripts 27 and 100 triggers
|
||||
// a palette fading just after loading a room. Catch this, so that only *one*
|
||||
// palette refresh occurs.
|
||||
if ((_currentEpisodeNum == 27 && _currentRoomNum == 56 && num == 34) ||
|
||||
(_currentEpisodeNum == 100 && _currentRoomNum == 2 && num == 1)) {
|
||||
updateScreenPalette = false;
|
||||
}
|
||||
|
||||
if (updateScreenPalette) {
|
||||
updatePalette();
|
||||
} else {
|
||||
setPalette(0, 255, 0, 0, 0);
|
||||
}
|
||||
|
||||
_currentRoomNum = num;
|
||||
_updatedRoomAreasTable[0] = 1;
|
||||
|
||||
_fullRedrawCounter = 1;
|
||||
_roomNeedRedraw = true;
|
||||
|
||||
_sequenceEntryTable[5].sprNum = -1;
|
||||
_sequenceEntryTable[5].seqNum = -1;
|
||||
_sequenceEntryTable[6].sprNum = -1;
|
||||
_sequenceEntryTable[6].seqNum = -1;
|
||||
}
|
||||
|
||||
void ToucheEngine::res_loadSprite(int num, int index) {
|
||||
debugC(9, kDebugResource, "ToucheEngine::res_loadSprite() num=%d index=%d", num, index);
|
||||
assert(index >= 0 && index < NUM_SEQUENCES);
|
||||
_sequenceEntryTable[index].sprNum = num;
|
||||
SpriteData *spr = &_spritesTable[index];
|
||||
const uint32 offs = res_getDataOffset(kResourceTypeSpriteImage, num);
|
||||
_fData.seek(offs);
|
||||
_currentImageWidth = _fData.readUint16LE();
|
||||
_currentImageHeight = _fData.readUint16LE();
|
||||
const uint32 size = _currentImageWidth * _currentImageHeight;
|
||||
if (size > spr->size) {
|
||||
debug(8, "Reallocating memory for sprite %d (index %d), %d bytes needed", num, index, size - spr->size);
|
||||
spr->size = size;
|
||||
|
||||
uint8 *buffer = NULL;
|
||||
if (spr->ptr)
|
||||
buffer = (uint8 *)realloc(spr->ptr, size);
|
||||
|
||||
if (!buffer) {
|
||||
// Free previously allocated sprite (when realloc failed)
|
||||
free(spr->ptr);
|
||||
|
||||
buffer = (uint8 *)malloc(size);
|
||||
}
|
||||
|
||||
if (!buffer)
|
||||
error("[ToucheEngine::res_loadSprite] Unable to reallocate memory for sprite %d (%d bytes)", num, size);
|
||||
|
||||
spr->ptr = buffer;
|
||||
}
|
||||
for (int i = 0; i < _currentImageHeight; ++i) {
|
||||
res_decodeScanLineImageRLE(spr->ptr + _currentImageWidth * i, _currentImageWidth);
|
||||
}
|
||||
spr->bitmapWidth = _currentImageWidth;
|
||||
spr->bitmapHeight = _currentImageHeight;
|
||||
if (_flagsTable[268] == 0) {
|
||||
res_loadImageHelper(spr->ptr, _currentImageWidth, _currentImageHeight);
|
||||
}
|
||||
spr->w = _currentImageWidth;
|
||||
spr->h = _currentImageHeight;
|
||||
}
|
||||
|
||||
void ToucheEngine::res_loadSequence(int num, int index) {
|
||||
debugC(9, kDebugResource, "ToucheEngine::res_loadSequence() num=%d index=%d", num, index);
|
||||
assert(index < NUM_SEQUENCES);
|
||||
_sequenceEntryTable[index].seqNum = num;
|
||||
const uint32 offs = res_getDataOffset(kResourceTypeSequence, num);
|
||||
_fData.seek(offs);
|
||||
_fData.read(_sequenceDataTable[index], 16000);
|
||||
}
|
||||
|
||||
void ToucheEngine::res_decodeScanLineImageRLE(uint8 *dst, int lineWidth) {
|
||||
int w = 0;
|
||||
while (w < lineWidth) {
|
||||
uint8 code = _fData.readByte();
|
||||
if ((code & 0xC0) == 0xC0) {
|
||||
int len = code & 0x3F;
|
||||
uint8 color = _fData.readByte();
|
||||
memset(dst, color, len);
|
||||
dst += len;
|
||||
w += len;
|
||||
} else {
|
||||
*dst = code;
|
||||
++dst;
|
||||
++w;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ToucheEngine::res_loadBackdrop() {
|
||||
debugC(9, kDebugResource, "ToucheEngine::res_loadBackdrop()");
|
||||
_currentBitmapWidth = _fData.readUint16LE();
|
||||
_currentBitmapHeight = _fData.readUint16LE();
|
||||
for (int i = 0; i < _currentBitmapHeight; ++i) {
|
||||
res_decodeScanLineImageRLE(_backdropBuffer + _currentBitmapWidth * i, _currentBitmapWidth);
|
||||
}
|
||||
_roomWidth = _currentBitmapWidth;
|
||||
for (int i = 0; i < _currentBitmapWidth; ++i) {
|
||||
if (_backdropBuffer[i] == 255) {
|
||||
_roomWidth = i;
|
||||
_backdropBuffer[i] = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
// Workaround for bug #3305 (original bitmap has a white pixel in its transparent area).
|
||||
if (_currentRoomNum == 8 && _currentBitmapWidth == 860) {
|
||||
_backdropBuffer[120 * _currentBitmapWidth + 734] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void ToucheEngine::res_loadImage(int num, uint8 *dst) {
|
||||
debugC(9, kDebugResource, "ToucheEngine::res_loadImage() num=%d", num);
|
||||
const uint32 offsInfo = res_getDataOffset(kResourceTypeIconImage, num);
|
||||
_fData.seek(offsInfo);
|
||||
_currentImageWidth = _fData.readUint16LE();
|
||||
_currentImageHeight = _fData.readUint16LE();
|
||||
for (int i = 0; i < _currentImageHeight; ++i) {
|
||||
res_decodeScanLineImageRLE(dst + _currentImageWidth * i, _currentImageWidth);
|
||||
}
|
||||
res_loadImageHelper(dst, _currentImageWidth, _currentImageHeight);
|
||||
}
|
||||
|
||||
void ToucheEngine::res_loadImageHelper(uint8 *imgData, int imgWidth, int imgHeight) {
|
||||
uint8 *p = imgData;
|
||||
for (_currentImageHeight = 0; _currentImageHeight < imgHeight; ++_currentImageHeight, p += imgWidth) {
|
||||
if (*p == 64 || *p == 255) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
p = imgData;
|
||||
for (_currentImageWidth = 0; _currentImageWidth < imgWidth; ++_currentImageWidth, ++p) {
|
||||
if (*p == 64 || *p == 255) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (_flagsTable[267] == 0) {
|
||||
for (int i = 0; i < imgWidth * imgHeight; ++i) {
|
||||
uint8 color = imgData[i];
|
||||
if (color != 0) {
|
||||
if (color < 64) {
|
||||
color += 192;
|
||||
} else {
|
||||
color = 0;
|
||||
}
|
||||
}
|
||||
imgData[i] = color;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ToucheEngine::res_loadSound(int priority, int num) {
|
||||
debugC(9, kDebugResource, "ToucheEngine::res_loadSound() num=%d", num);
|
||||
if (priority >= 0) {
|
||||
uint32 size;
|
||||
const uint32 offs = res_getDataOffset(kResourceTypeSound, num, &size);
|
||||
Common::SeekableReadStream *datastream = SearchMan.createReadStreamForMember("TOUCHE.DAT");
|
||||
if (!datastream) {
|
||||
warning("res_loadSound: Could not open TOUCHE.DAT");
|
||||
return;
|
||||
}
|
||||
|
||||
datastream->seek(offs);
|
||||
Audio::AudioStream *stream = Audio::makeVOCStream(datastream, Audio::FLAG_UNSIGNED, DisposeAfterUse::YES);
|
||||
if (stream) {
|
||||
_mixer->playStream(Audio::Mixer::kSFXSoundType, &_sfxHandle, stream);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ToucheEngine::res_stopSound() {
|
||||
_mixer->stopHandle(_sfxHandle);
|
||||
}
|
||||
|
||||
void ToucheEngine::res_loadMusic(int num) {
|
||||
debugC(9, kDebugResource, "ToucheEngine::res_loadMusic() num=%d", num);
|
||||
startMusic(num);
|
||||
}
|
||||
|
||||
void ToucheEngine::res_loadSpeech(int num) {
|
||||
debugC(9, kDebugResource, "ToucheEngine::res_loadSpeech() num=%d", num);
|
||||
if (num == -1) {
|
||||
res_stopSpeech();
|
||||
} else {
|
||||
if (_compressedSpeechData < 0) { // uncompressed speech data
|
||||
if (_fSpeech[0].isOpen()) {
|
||||
_fSpeech[0].close();
|
||||
}
|
||||
Common::String filenameStr = Common::String::format("V%d", num);
|
||||
_fSpeech[0].open(filenameStr.c_str());
|
||||
}
|
||||
if (_fSpeech[0].isOpen()) {
|
||||
_flagsTable[617] = num;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ToucheEngine::res_loadSpeechSegment(int num) {
|
||||
debugC(9, kDebugResource, "ToucheEngine::res_loadSpeechSegment() num=%d", num);
|
||||
if (_talkTextMode != kTalkModeTextOnly && _flagsTable[617] != 0) {
|
||||
Audio::AudioStream *stream = 0;
|
||||
if (_compressedSpeechData < 0) { // uncompressed speech data
|
||||
int i = 0;
|
||||
if (num >= 750) {
|
||||
num -= 750;
|
||||
i = 1;
|
||||
}
|
||||
if (!_fSpeech[i].isOpen()) {
|
||||
return;
|
||||
}
|
||||
_fSpeech[i].seek(num * 8);
|
||||
uint32 offs = _fSpeech[i].readUint32LE();
|
||||
uint32 size = _fSpeech[i].readUint32LE();
|
||||
if (size == 0) {
|
||||
return;
|
||||
}
|
||||
_fSpeech[i].seek(offs);
|
||||
stream = Audio::makeVOCStream(&_fSpeech[i], Audio::FLAG_UNSIGNED);
|
||||
} else {
|
||||
if (num >= 750) {
|
||||
num -= 750;
|
||||
_fSpeech[0].seek(kSpeechDataFileHeaderSize);
|
||||
} else {
|
||||
assert(_flagsTable[617] > 0 && _flagsTable[617] < 140);
|
||||
_fSpeech[0].seek(kSpeechDataFileHeaderSize + _flagsTable[617] * 4);
|
||||
}
|
||||
uint32 dataOffs = _fSpeech[0].readUint32LE();
|
||||
if (dataOffs == 0) {
|
||||
return;
|
||||
}
|
||||
_fSpeech[0].seek(dataOffs + num * 8);
|
||||
uint32 offs = _fSpeech[0].readUint32LE();
|
||||
uint32 size = _fSpeech[0].readUint32LE();
|
||||
if (size == 0) {
|
||||
return;
|
||||
}
|
||||
_fSpeech[0].seek(offs);
|
||||
Common::SeekableReadStream *tmp = _fSpeech[0].readStream(size);
|
||||
if (tmp)
|
||||
stream = (compressedSpeechFilesTable[_compressedSpeechData].makeStream)(tmp, DisposeAfterUse::YES);
|
||||
}
|
||||
if (stream) {
|
||||
_speechPlaying = true;
|
||||
_mixer->playStream(Audio::Mixer::kSpeechSoundType, &_speechHandle, stream);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ToucheEngine::res_stopSpeech() {
|
||||
debugC(9, kDebugResource, "ToucheEngine::res_stopSpeech()");
|
||||
_mixer->stopHandle(_speechHandle);
|
||||
_speechPlaying = false;
|
||||
}
|
||||
|
||||
} // namespace Touche
|
||||
Reference in New Issue
Block a user