mirror of
https://github.com/libretro/scummvm.git
synced 2025-01-13 13:10:53 +00:00
a39711e256
svn-id: r4877
775 lines
20 KiB
C++
775 lines
20 KiB
C++
/* ScummVM - Scumm Interpreter
|
|
* Copyright (C) 2001/2002 The ScummVM project
|
|
*
|
|
* 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
|
*
|
|
* $Header$
|
|
*
|
|
*/
|
|
|
|
#include <stdafx.h>
|
|
#include "common/util.h"
|
|
#include "common/engine.h" // for debug, warning, error
|
|
|
|
#include "player.h"
|
|
|
|
#include "mixer.h"
|
|
#include "renderer.h"
|
|
#include "frenderer.h"
|
|
#include "channel.h"
|
|
#include "chunk_type.h"
|
|
#include "blitter.h"
|
|
|
|
#include <assert.h>
|
|
#include <stdlib.h>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <ctype.h>
|
|
|
|
const int WAIT = 100;
|
|
|
|
/*! @brief parser and map of string resources
|
|
|
|
This class implements a parser for the string resource format of SMUSH animations.
|
|
It then allows the player to get the string corresponding to a particular identifier.
|
|
|
|
@bug some of The Dig strings are not completely parsed (in titles)
|
|
*/
|
|
|
|
const int32 MAX_STRINGS = 200;
|
|
|
|
class StringResource {
|
|
private:
|
|
struct {
|
|
int32 id;
|
|
char * string;
|
|
} _strings[MAX_STRINGS];
|
|
int32 _nbStrings;
|
|
int32 _lastId;
|
|
char * _lastString;
|
|
public:
|
|
StringResource() : _nbStrings(0), _lastId(-1) {};
|
|
~StringResource() {
|
|
for(int32 i = 0; i < _nbStrings; i++) {
|
|
delete []_strings[i].string;
|
|
}
|
|
}
|
|
/*! @brief parse the given buffer
|
|
|
|
@param buffer the buffer that contain the resource (in lucasart format)
|
|
@param length the length of the buffer
|
|
|
|
@return \c true if the parsing went fine, \c false otherwise
|
|
*/
|
|
bool init(char * buffer, int32 length) {
|
|
debug(9, "parsing string resources...");
|
|
char * def_start = strchr(buffer, '#');
|
|
while(def_start != NULL) {
|
|
char * def_end = strchr(def_start, '\n');
|
|
assert(def_end != NULL); // def_end is just before the start of the string [def_start,def_end] correspond to the definition text
|
|
char * id_end = def_end;
|
|
while(id_end >= def_start && !isdigit(*(id_end-1))) id_end--;
|
|
assert(id_end > def_start);
|
|
char * id_start = id_end;
|
|
while(isdigit(*(id_start - 1))) id_start--;
|
|
// [id_start-id_end] is the id number
|
|
char idstring[32];
|
|
memcpy(idstring, id_start, id_end - id_start);
|
|
idstring[id_end - id_start] = 0;
|
|
int32 id = atoi(idstring);
|
|
char * data_start = def_end;
|
|
|
|
while(*data_start == '\n' || *data_start == '\r')
|
|
data_start++;
|
|
char * data_end = data_start;
|
|
|
|
while(1) {
|
|
if(data_end[-2] == '\r' && data_end[1] == '\n' && data_end[-1] == '\n' && data_end[0] == '\r')
|
|
break;
|
|
data_end++;
|
|
if(data_end >= buffer + length) {
|
|
data_end = buffer + length;
|
|
break;
|
|
}
|
|
}
|
|
|
|
data_end -= 2;
|
|
assert(data_end > data_start);
|
|
char * value = new char[data_end - data_start + 1];
|
|
assert(value);
|
|
memcpy(value, data_start, data_end - data_start);
|
|
value[data_end - data_start] = 0;
|
|
char * line_start = value;
|
|
char * line_end;
|
|
|
|
while ((line_end = strchr(line_start, '\n'))) {
|
|
line_start = line_end+1;
|
|
if (line_start[0] == '/' && line_start[1] == '/') {
|
|
line_start += 2;
|
|
if (line_end[-1] == '\r')
|
|
line_end[-1] = ' ';
|
|
else
|
|
*line_end++ = ' ';
|
|
memmove(line_end, line_start, strlen(line_start)+1);
|
|
}
|
|
}
|
|
#ifdef DEBUG
|
|
debug(9, "Inserting (%s)%d == \"%s\"", idstring, id, value);
|
|
#endif
|
|
_strings[_nbStrings].id = id;
|
|
_strings[_nbStrings].string = value;
|
|
_nbStrings ++;
|
|
def_start = strchr(data_end + 2, '#');
|
|
}
|
|
return true;
|
|
}
|
|
/*! @brief extract a string
|
|
|
|
@param id the resource identifier
|
|
|
|
@return the corresponding string.
|
|
*/
|
|
const char * get(int32 id) {
|
|
if(id == _lastId) return _lastString;
|
|
for(int i = 0; i < _nbStrings; i++)
|
|
{
|
|
if(_strings[i].id == id) {
|
|
_lastId = id;
|
|
_lastString = _strings[i].string;
|
|
return _strings[i].string;
|
|
}
|
|
}
|
|
warning("invalid string id : %d", id);
|
|
_lastId = -1;
|
|
_lastString = "unknown string";
|
|
return _lastString;
|
|
}
|
|
};
|
|
|
|
void SmushPlayer::show(const char * p) {
|
|
if(strcmp(p, "subtitles") == 0)
|
|
_subtitles = true;
|
|
else if(strcmp(p, "bgmusic") == 0)
|
|
_bgmusic = true;
|
|
else if(strcmp(p, "voices") == 0)
|
|
_voices = true;
|
|
else {
|
|
int id = atoi(p);
|
|
if(id < 0 || id > 36) error("invalid parameter to show");
|
|
_skips[id] = true;
|
|
}
|
|
}
|
|
|
|
void SmushPlayer::hide(const char * p) {
|
|
if(strcmp(p, "subtitles") == 0)
|
|
_subtitles = false;
|
|
else if(strcmp(p, "bgmusic") == 0)
|
|
_bgmusic = false;
|
|
else if(strcmp(p, "voices") == 0)
|
|
_voices = false;
|
|
else {
|
|
int32 id = atoi(p);
|
|
if(id < 0 || id > 36) error("invalid parameter to hide");
|
|
_skips[id] = false;
|
|
}
|
|
}
|
|
|
|
SmushPlayer::SmushPlayer(Renderer * renderer, bool wait, bool sound) :
|
|
_version(-1),
|
|
_secondaryVersion(0),
|
|
_soundFrequency(0),
|
|
_nbframes(0),
|
|
_mixer(0),
|
|
_renderer(renderer),
|
|
_strings(0),
|
|
_frameSize(-1, -1),
|
|
_frame(0),
|
|
_outputSound(sound),
|
|
_wait(wait),
|
|
_alreadyInit(false),
|
|
_codec37Called(false),
|
|
_skipNext(false),
|
|
_subtitles(true),
|
|
_bgmusic(true),
|
|
_voices(true) {
|
|
_fr[0] = _fr[1] = _fr[2] = _fr[3] = 0;
|
|
assert(_renderer != 0);
|
|
}
|
|
|
|
SmushPlayer::~SmushPlayer() {
|
|
clean();
|
|
//~ if(_mixer) delete _mixer;
|
|
}
|
|
|
|
void SmushPlayer::updatePalette(void) {
|
|
_renderer->setPalette(_pal);
|
|
}
|
|
|
|
void SmushPlayer::clean() {
|
|
if(_strings)
|
|
delete _strings;
|
|
if(_fr[0]) delete _fr[0];
|
|
if(_fr[1]) delete _fr[1];
|
|
if(_fr[2]) delete _fr[2];
|
|
if(_fr[3]) delete _fr[3];
|
|
}
|
|
|
|
void SmushPlayer::checkBlock(const Chunk & b, Chunk::type type_expected, uint32 min_size) {
|
|
if(type_expected != b.getType()) {
|
|
error("Chunk type is different from expected : %d != %d", b.getType(), type_expected);
|
|
}
|
|
if(min_size > b.getSize()) {
|
|
error( "Chunk size is inferior than minimum required size : %d < %d", b.getSize(), min_size);
|
|
}
|
|
}
|
|
|
|
void SmushPlayer::handleSoundBuffer(int32 track_id, int32 index, int32 max_frames, int32 flags, int32 vol, int32 bal, Chunk & b, int32 size) {
|
|
debug(6, "smush_player::handleSoundBuffer(%d)", track_id);
|
|
if(!_voices && (flags & 128) == 128) return;
|
|
if(!_bgmusic && (flags & 64) == 64) return;
|
|
_Channel * c = _mixer->findChannel(track_id);
|
|
if(c == 0) {
|
|
c = new SaudChannel(track_id, _soundFrequency);
|
|
_mixer->addChannel(c);
|
|
}
|
|
if(index == 0)
|
|
c->setParameters(max_frames, flags, vol, bal);
|
|
else
|
|
c->checkParameters(index, max_frames, flags, vol, bal);
|
|
c->appendData(b, size);
|
|
}
|
|
|
|
void SmushPlayer::handleSoundFrame(Chunk & b) {
|
|
checkBlock(b, TYPE_PSAD);
|
|
debug(6, "SmushPlayer::handleSoundFrame()");
|
|
if(!_outputSound) return;
|
|
int32 track_id = b.getWord();
|
|
int32 index = b.getWord();
|
|
int32 max_frames = b.getWord();
|
|
int32 flags = b.getWord();
|
|
int32 vol = b.getByte();
|
|
int32 bal = b.getChar();
|
|
#ifdef DEBUG
|
|
if(index == 0) {
|
|
debug(5, "track_id == %d, max_frames == %d, %d, %d, %d", track_id, max_frames, flags, vol, bal);
|
|
}
|
|
#endif
|
|
int32 size = b.getSize() - 10;
|
|
handleSoundBuffer(track_id, index, max_frames, flags, vol, bal, b, size);
|
|
}
|
|
|
|
void SmushPlayer::handleSkip(Chunk & b) {
|
|
checkBlock(b, TYPE_SKIP, 4);
|
|
int32 code = b.getDword();
|
|
debug(6, "SmushPlayer::handleSkip(%d)", code);
|
|
if(code >= 0 && code < 37)
|
|
_skipNext = _skips[code];
|
|
else
|
|
_skipNext = true;
|
|
}
|
|
|
|
void SmushPlayer::handleStore(Chunk & b) {
|
|
checkBlock(b, TYPE_STOR, 4);
|
|
debug(6, "SmushPlayer::handleStore()");
|
|
}
|
|
|
|
void SmushPlayer::handleFetch(Chunk & b) {
|
|
checkBlock(b, TYPE_FTCH, 6);
|
|
debug(6, "SmushPlayer::handleFetch()");
|
|
}
|
|
|
|
void SmushPlayer::handleImuseBuffer(int32 track_id, int32 index, int32 nbframes, int32 size, int32 unk1, int32 unk2, Chunk & b, int32 bsize) {
|
|
_Channel * c = _mixer->findChannel(track_id);
|
|
if(c == 0) {
|
|
c = new ImuseChannel(track_id, _soundFrequency);
|
|
_mixer->addChannel(c);
|
|
}
|
|
if(index == 0)
|
|
c->setParameters(nbframes, size, unk1, unk2);
|
|
else
|
|
c->checkParameters(index, nbframes, size, unk1, unk2);
|
|
c->appendData(b, bsize);
|
|
}
|
|
|
|
void SmushPlayer::handleImuseAction8(Chunk & b, int32 flags, int32 unknown, int32 track_id) {
|
|
assert(flags == 46 && unknown == 0);
|
|
int32 unknown2 = b.getWord();
|
|
track_id |= unknown2 << 16;
|
|
int32 index = b.getWord();
|
|
int32 nbframes = b.getWord();
|
|
int32 size = b.getDword();
|
|
int32 bsize = b.getSize() - 18;
|
|
handleImuseBuffer(track_id, index, nbframes, size, unknown, unknown2, b, bsize);
|
|
}
|
|
|
|
void SmushPlayer::handleImuseAction(Chunk & b) {
|
|
checkBlock(b, TYPE_IACT, 8);
|
|
debug(6, "SmushPlayer::handleImuseAction()");
|
|
if(!_outputSound) return;
|
|
int32 code = b.getWord();
|
|
int32 flags = b.getWord();
|
|
int32 unknown = b.getShort();
|
|
int32 track_id = b.getWord();
|
|
#ifdef DEBUG
|
|
debug(5, "handleImuseAction(%d, %d, %d, %d)", code, flags, unknown, track_id);
|
|
#endif
|
|
switch(code) {
|
|
case 8:
|
|
handleImuseAction8(b, flags, unknown, track_id);
|
|
break;
|
|
#ifdef DEBUG
|
|
default: {
|
|
debug(9, "%5.5d %d %8.8d %4.4d", track_id, flags, unknown);
|
|
}
|
|
#endif
|
|
}
|
|
}
|
|
|
|
void SmushPlayer::handleTextResource(Chunk & b) {
|
|
checkBlock(b, TYPE_TRES, 18);
|
|
int32 pos_x = b.getShort();
|
|
int32 pos_y = b.getShort();
|
|
int32 flags = b.getShort();
|
|
int32 left = b.getShort();
|
|
int32 top = b.getShort();
|
|
int32 width = b.getShort();
|
|
int32 height = b.getShort();
|
|
int32 unk2 = b.getWord();
|
|
int32 string_id = b.getWord();
|
|
debug(6, "SmushPlayer::handleTextResource(%d)", string_id);
|
|
if(!_strings) return;
|
|
|
|
// if subtitles disabled and bit 3 is set, then do not draw
|
|
if((!_subtitles) && ((flags & 8) == 8))
|
|
return;
|
|
const char * str = _strings->get(string_id);
|
|
|
|
FontRenderer * fr = _fr[0];
|
|
int32 color = 15;
|
|
while(*str == '/') str++; // For Full Throttle text resources
|
|
while(str[0] == '^') {
|
|
switch(str[1]) {
|
|
case 'f':
|
|
{
|
|
int id = str[3] - '0';
|
|
str += 4;
|
|
fr = _fr[id];
|
|
} break;
|
|
case 'c':
|
|
{
|
|
color = str[4] - '0' + 10 *(str[3] - '0');
|
|
str += 5;
|
|
} break;
|
|
default:
|
|
error("invalid escape code in text string");
|
|
}
|
|
}
|
|
assert(fr != 0);
|
|
fr->setColor(color);
|
|
if(!_curBuffer) { _curBuffer = _renderer->lockFrame(_frame); }
|
|
if(flags == 0 || flags == 4) {
|
|
fr->drawStringAbsolute(str, _curBuffer, _frameSize, pos_x, pos_y);
|
|
} else {
|
|
fr->drawStringCentered(str, _curBuffer, _frameSize, MAX(pos_y, top), left, width, pos_x);
|
|
}
|
|
}
|
|
|
|
void SmushPlayer::readPalette(Palette & out, Chunk & in) {
|
|
byte buffer[768];
|
|
in.read(buffer, 768);
|
|
out = Palette(buffer);
|
|
}
|
|
|
|
void SmushPlayer::handleDeltaPalette(Chunk & b) {
|
|
checkBlock(b, TYPE_XPAL);
|
|
debug(6, "SmushPlayer::handleDeltaPalette()");
|
|
if(b.getSize() == 768 * 3 + 4) {
|
|
int32 unk1, num;
|
|
unk1 = b.getWord();
|
|
num = b.getWord();
|
|
for(int32 i = 0; i < 768; i++) {
|
|
_deltaPal[i] = b.getWord();
|
|
}
|
|
readPalette(_pal, b);
|
|
updatePalette();
|
|
} else if(b.getSize() == 6) {
|
|
int32 unk1, num, unk2;
|
|
unk1 = b.getWord();
|
|
num = b.getWord();
|
|
unk2 = b.getWord();
|
|
for(int32 i = 0; i < 256; i++) {
|
|
_pal[i].delta(_deltaPal + 3 * i);
|
|
}
|
|
updatePalette();
|
|
} else {
|
|
error("wrong size for DeltaPalette");
|
|
}
|
|
}
|
|
|
|
void SmushPlayer::handleNewPalette(Chunk & b) {
|
|
checkBlock(b, TYPE_NPAL, 768);
|
|
debug(6, "SmushPlayer::handleNewPalette()");
|
|
readPalette(_pal, b);
|
|
updatePalette();
|
|
}
|
|
|
|
void SmushPlayer::decodeCodec(Chunk & b, const Rect & r, Decoder & codec) {
|
|
assert(_curBuffer);
|
|
Blitter blit((byte*)_curBuffer, _frameSize, r);
|
|
codec.decode(blit, b);
|
|
}
|
|
|
|
void SmushPlayer::initSize(const Rect & r, bool always, bool transparent) {
|
|
if(_codec37Called) _alreadyInit = true;
|
|
|
|
if(!_alreadyInit || _frameSize.getX() < r.right() || _frameSize.getY() < r.bottom() || always) {
|
|
if(_curBuffer) {
|
|
_renderer->unlockFrame();
|
|
_curBuffer = 0;
|
|
}
|
|
_frameSize = r.bottomRight();
|
|
_renderer->initFrame(_frameSize);
|
|
}
|
|
|
|
if(_curBuffer) {
|
|
_renderer->unlockFrame();
|
|
_curBuffer = 0;
|
|
}
|
|
|
|
_curBuffer = _renderer->lockFrame(_frame);
|
|
if(!_alreadyInit && transparent) {
|
|
memset(_curBuffer, 0, _frameSize.getX()*_frameSize.getY());
|
|
}
|
|
|
|
_codec1.initSize(_frameSize, r);
|
|
_codec37.initSize(_frameSize, r);
|
|
_codec44.initSize(_frameSize, r);
|
|
_codecd.initSize(_frameSize, r);
|
|
_alreadyInit = true;
|
|
}
|
|
|
|
void SmushPlayer::handleFrameObject(Chunk & b) {
|
|
checkBlock(b, TYPE_FOBJ, 14);
|
|
if(_skipNext) {
|
|
_skipNext = false;
|
|
return;
|
|
}
|
|
int codec = b.getWord();
|
|
debug(6, "SmushPlayer::handleFrameObject(%d)", codec);
|
|
uint16 left = b.getWord();
|
|
uint16 top = b.getWord();
|
|
uint16 width = b.getWord();
|
|
uint16 height = b.getWord();
|
|
Rect r(left, top, left + width, top + height);
|
|
uint16 data[2];
|
|
data[1] = b.getWord();
|
|
data[0] = b.getWord();
|
|
#ifdef DEBUG
|
|
debug(5, "Frame pos : %d, %d", left, top);
|
|
debug(5, "Frame size : %dx%d", width, height);
|
|
debug(5, "Codec : %d", codec);
|
|
#endif
|
|
switch (codec) {
|
|
case 3:
|
|
case 1:
|
|
initSize(r, false, true);
|
|
decodeCodec(b, r, _codec1);
|
|
break;
|
|
case 37:
|
|
assert(left == 0 && top == 0);
|
|
initSize(r, true, false);
|
|
decodeCodec(b, r, _codec37);
|
|
_codec37Called = true;
|
|
break;
|
|
case 47:
|
|
initSize(r, false, true);
|
|
decodeCodec(b, r, _codecd);
|
|
break;
|
|
case 21:
|
|
case 44:
|
|
initSize(r, true, true);
|
|
decodeCodec(b, r, _codec44);
|
|
break;
|
|
default:
|
|
error("Invalid codec for frame object : %d", (int32)codec);
|
|
}
|
|
}
|
|
|
|
void SmushPlayer::handleFrame(Chunk & b) {
|
|
checkBlock(b, TYPE_FRME);
|
|
debug(6, "SmushPlayer::handleFrame(%d)", _frame);
|
|
_alreadyInit = false;
|
|
_skipNext = false;
|
|
|
|
while(!b.eof()) {
|
|
Chunk * sub = b.subBlock();
|
|
if(sub->getSize() & 1) b.seek(1);
|
|
switch(sub->getType()) {
|
|
case TYPE_NPAL:
|
|
handleNewPalette(*sub);
|
|
break;
|
|
case TYPE_FOBJ:
|
|
handleFrameObject(*sub);
|
|
break;
|
|
case TYPE_PSAD:
|
|
handleSoundFrame(*sub);
|
|
break;
|
|
case TYPE_TRES:
|
|
handleTextResource(*sub);
|
|
break;
|
|
case TYPE_XPAL:
|
|
handleDeltaPalette(*sub);
|
|
break;
|
|
case TYPE_IACT:
|
|
handleImuseAction(*sub);
|
|
break;
|
|
case TYPE_STOR:
|
|
handleStore(*sub);
|
|
break;
|
|
case TYPE_FTCH:
|
|
handleFetch(*sub);
|
|
break;
|
|
case TYPE_SKIP:
|
|
handleSkip(*sub);
|
|
break;
|
|
default:
|
|
error("Unknown frame subChunk found : %s, %d", Chunk::ChunkString(sub->getType()), sub->getSize());
|
|
}
|
|
delete sub;
|
|
}
|
|
if(_curBuffer) {
|
|
_renderer->unlockFrame();
|
|
_curBuffer = 0;
|
|
}
|
|
if(_outputSound)
|
|
_mixer->handleFrame();
|
|
#ifdef DEBUG
|
|
debug(5, "===================END OF FRAME========================");
|
|
#endif
|
|
_renderer->flipFrame();
|
|
if(_wait)
|
|
_renderer->wait(WAIT);
|
|
_frame++;
|
|
}
|
|
|
|
void SmushPlayer::handleAnimHeader(Chunk & b) {
|
|
checkBlock(b, TYPE_AHDR, 774);
|
|
debug(6, "SmushPlayer::handleAnimHeader()");
|
|
_version = b.getWord();
|
|
_nbframes = b.getWord();
|
|
int32 unknown = b.getWord();
|
|
#ifdef DEBUG
|
|
debug(5, "SMUSH HEADER : version == %d, nbframes == %d, unknown == %d", _version, _nbframes, unknown);
|
|
#else
|
|
unknown = unknown;
|
|
#endif
|
|
_renderer->startDecode(_fname, _version, _nbframes);
|
|
readPalette(_pal, b);
|
|
updatePalette();
|
|
if(_version == 1) {
|
|
_soundFrequency = 22050;
|
|
}
|
|
if(_version == 2) {
|
|
_secondaryVersion = b.getDword();
|
|
int32 unknown2 = b.getDword();
|
|
_soundFrequency = b.getDword();
|
|
#ifdef DEBUG
|
|
debug(5, "SMUSH HEADER : secondary version == %d, unknown2 == %d, sound frequency == %d", _secondaryVersion, unknown2, _soundFrequency);
|
|
int32 i = 0, c;
|
|
while(!b.eof()) {
|
|
c = b.getByte();
|
|
if(c) debug(9, "SMUSH HEADER : remaining bytes : %d == %d", i, c);
|
|
i++;
|
|
}
|
|
#else
|
|
unknown2 = unknown2;
|
|
#endif
|
|
if(_secondaryVersion != 10 && _secondaryVersion != 0 && _secondaryVersion != 12 && _secondaryVersion != 15 && _secondaryVersion != 14)
|
|
error("Wrong secondary version number for SMUSH animation");
|
|
if(_soundFrequency != 0 && _soundFrequency != 11025 && _soundFrequency != 22050)
|
|
error("Wrong _sound_frequency number for SMUSH animation");
|
|
} else if(_version > 2) {
|
|
error("Wrong primary version number for SMUSH animation");
|
|
}
|
|
if(_outputSound && _soundFrequency) {
|
|
if(_soundFrequency != 22050) _soundFrequency = 22050;
|
|
_mixer = _renderer->getMixer();
|
|
if(_mixer) {
|
|
_mixer->init();
|
|
} else {
|
|
_outputSound = false;
|
|
}
|
|
}
|
|
}
|
|
|
|
static StringResource * getStrings(const char * file, bool is_encoded) {
|
|
debug(7, "trying to read text ressources from %s", file);
|
|
FILE * is;
|
|
is = fopen(file, "rb");
|
|
if(is == NULL) return 0;
|
|
fseek(is, 0, SEEK_END);
|
|
int32 length = ftell(is);
|
|
fseek(is, 0, SEEK_SET);
|
|
char * filebuffer = new char [length + 1];
|
|
assert(filebuffer);
|
|
fread (filebuffer, length, 1, is);
|
|
filebuffer[length] = 0;
|
|
fclose(is);
|
|
if(is_encoded) {
|
|
static const int32 ETRS_HEADER_LENGTH = 16;
|
|
assert(length > ETRS_HEADER_LENGTH);
|
|
Chunk::type type = READ_BE_UINT32(filebuffer);
|
|
if(type != TYPE_ETRS) {
|
|
delete [] filebuffer;
|
|
return getStrings(file, false);
|
|
}
|
|
char * old = filebuffer;
|
|
filebuffer = new char[length - ETRS_HEADER_LENGTH];
|
|
for(int32 i = ETRS_HEADER_LENGTH; i < length; i++)
|
|
filebuffer[i - ETRS_HEADER_LENGTH] = old[i] ^ 0xCC;
|
|
delete []old;
|
|
length -= ETRS_HEADER_LENGTH;
|
|
}
|
|
StringResource * sr = new StringResource;
|
|
assert(sr);
|
|
sr->init(filebuffer, length);
|
|
delete []filebuffer;
|
|
return sr;
|
|
}
|
|
|
|
bool SmushPlayer::readString(const char * file, bool & ft) {
|
|
const char * i = strrchr(file, '.');
|
|
if(i == NULL) error("invalid filename : %s", file);
|
|
char fname[260];
|
|
memcpy(fname, file, i - file);
|
|
strcpy(fname + (i - file), ".trs");
|
|
if((_strings = getStrings(fname, false)) != 0) {
|
|
ft = true;
|
|
return true;
|
|
}
|
|
i = strrchr(file, '\\');
|
|
if(i == NULL) i = strrchr(file, '/');
|
|
else {
|
|
char * j = strrchr(file, '/');
|
|
if(j > i) i = j;
|
|
}
|
|
if(i == NULL) error("invalid filename : %s", file);
|
|
|
|
memcpy(fname, file, i - file + 1);
|
|
strcpy(fname + (i - file + 1), "digtxt.trs");
|
|
if((_strings = getStrings(fname, true)) != 0) {
|
|
ft = false;
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
static FontRenderer * loadFont(const char * file, bool original = false) {
|
|
#ifdef DEBUG
|
|
debug(5, "loading font from \"%s\"", file);
|
|
#endif
|
|
FontRenderer * fr = new FontRenderer(original);
|
|
SmushPlayer p(fr, false, false);
|
|
p.play(file);
|
|
return fr;
|
|
}
|
|
|
|
bool SmushPlayer::play(const char * file) {
|
|
#ifdef DEBUG
|
|
debug(5, "start of animation : %s", file);
|
|
#endif
|
|
char * i = strrchr(file, '\\');
|
|
if(i == NULL)
|
|
{
|
|
i = strrchr(file, '/');
|
|
} else {
|
|
char * j = strrchr(i, '/');
|
|
if(j != NULL)
|
|
i = j;
|
|
}
|
|
char directory[260];
|
|
if(i != NULL) {
|
|
strcpy(directory, file);
|
|
directory[i-file] = 0;
|
|
//! @todo remove this...
|
|
_fname = strdup(i);
|
|
} else {
|
|
directory[0] = 0;
|
|
_fname = strdup(file);
|
|
}
|
|
clean();
|
|
|
|
if(_wait) {
|
|
bool isFullthrottle;
|
|
if(!readString(file, isFullthrottle))
|
|
warning("unable to read text information for \"%s\"", file);
|
|
if(_strings) {
|
|
if(isFullthrottle) {
|
|
if(strcmp(directory, "") == 0) {
|
|
strcpy(directory, "../data/");
|
|
} else {
|
|
char * i = strrchr(directory, '\\');
|
|
char * j = strrchr(directory, '/');
|
|
if(j > i) i = j;
|
|
if(i == NULL) {
|
|
strcpy(directory, "data/");
|
|
} else {
|
|
*i = 0;
|
|
strcat(directory, "/data/");
|
|
}
|
|
}
|
|
char file[260];
|
|
strcpy(file, directory); strcat(file, "scummfnt.nut");
|
|
_fr[0] = loadFont(file, true);
|
|
strcpy(file, directory); strcat(file, "titlfnt.nut");
|
|
_fr[2] = loadFont(file, true);
|
|
} else {
|
|
for(int32 i = 0; i < 4; i++) {
|
|
char file[260];
|
|
sprintf(file, "%s/font%d.nut",directory, i);
|
|
_fr[i] = loadFont(file, i != 0);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
FileChunk base = FileChunk(file);
|
|
|
|
checkBlock(base, TYPE_ANIM);
|
|
|
|
while(!base.eof()) {
|
|
Chunk * sub = base.subBlock();
|
|
switch(sub->getType()) {
|
|
case TYPE_AHDR:
|
|
handleAnimHeader(*sub);
|
|
break;
|
|
case TYPE_FRME:
|
|
handleFrame(*sub);
|
|
break;
|
|
default:
|
|
error("Unknown Chunk found : %d, %d", sub->getType(), sub->getSize());
|
|
}
|
|
delete sub;
|
|
if(_renderer->prematureClose())
|
|
break;
|
|
}
|
|
#ifdef DEBUG
|
|
debug(5, "end of animation");
|
|
#endif
|
|
if(_outputSound) {
|
|
_mixer->stop();
|
|
}
|
|
return true;
|
|
}
|