diff options
Diffstat (limited to 'scumm/smush')
34 files changed, 4796 insertions, 0 deletions
diff --git a/scumm/smush/blitter.cpp b/scumm/smush/blitter.cpp new file mode 100644 index 0000000000..bbeb7a1864 --- /dev/null +++ b/scumm/smush/blitter.cpp @@ -0,0 +1,221 @@ +/* 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 "blitter.h" +#include "chunck.h" + +#include <assert.h> +#include <string.h> // for memcpy + +#ifndef min +#define min(x, y) ((x) > (y) ? (y) : (x)) +#endif + +Blitter::Blitter(char * ptr, const Point & dstsize, const Rect & src) : + _ptr(ptr), + _clip(dstsize), + _src(src), + _cur(src.left(), src.top()), + _outside(false) { +#ifdef DEBUG_CLIPPER + _clipped = 0; + _clippedBlock = 0; +#endif + assert(_ptr); + assert(_clip.getX() > 0 && _clip.getY() > 0); + assert(_src.width() > 0 && _src.height() > 0); + assert(_src.left() < _clip.getX() && _src.right() <= _clip.getX()); + assert(_src.top() < _clip.getY() && _src.bottom() <= _clip.getY()); + _offset = _ptr + _clip.getX() * _cur.getY() + _cur.getX(); +} + +Blitter::~Blitter() { +#ifdef DEBUG_CLIPPER + if(_clipped || _clippedBlock) { + debug(3, "blitter clipped %d pixels and %d blocks", _clipped, _clippedBlock); + } +#endif +} + +void Blitter::advance(int x, int y) { + if(y != 0) { + _cur.set(_src.left() + x, _cur.getY() + y); + } else { + _cur.getX() += x; + if(_cur.getX() >= _src.right()) { + _cur.set(_src.left(), _cur.getY()+1); + } + } + _offset = _ptr + _clip.getX() * _cur.getY() + _cur.getX(); + _outside = ! _src.isInside(_cur); +} + +void Blitter::advanceBlock(int x, int y) { + advance(x*4, y*4); +} + +void Blitter::put(char data) { + if(!_outside) { + *_offset = data; + advance(); + } +#ifdef DEBUG_CLIPPER + else _clipped ++; +#endif +} + +void Blitter::put(char data, unsigned int len) { + while(len) { + if(_outside) { +#ifdef DEBUG_CLIPPER + _clipped += len; +#endif + break; + } + int l = min((int)len, min(_clip.getX() - _cur.getX(), _src.right() - _cur.getX())); + len -= l; + memset(_offset, data, l); + advance(l); + } +} + +void Blitter::blit(char * ptr, unsigned int len) { + while(len) { + if(_outside) { +#ifdef DEBUG_CLIPPER + _clipped += len; +#endif + break; + } + int l = min((int)len, min(_clip.getX() - _cur.getX(), _src.right() - _cur.getX())); + len -= l; + memcpy(_offset, ptr, l); + ptr += l; + advance(l); + } +} + +void Blitter::blit(Chunck & src, unsigned int len) { + while(len) { + if(_outside) { +#ifdef DEBUG_CLIPPER + _clipped += len; +#endif + break; + } + int l = min((int)len, min(_clip.getX() -_cur.getX(), _src.right() - _cur.getX())); + len -= l; + src.read(_offset, l); + advance(l); + } +} + +void Blitter::putBlock(unsigned int data) { + if(_cur.getX() + 3 < _src.right() && _cur.getY() + 3 < _src.bottom()) { // This is clipping + assert((_clip.getX() & 3) == 0); + unsigned int * dst = (unsigned int *)_offset; + int line_size = _clip.getX() >> 2; + + *dst = data; dst += line_size; + *dst = data; dst += line_size; + *dst = data; dst += line_size; + *dst = data; + +#ifdef DEBUG_CLIPPER + } else { + _clippedBlock ++; +#endif + } + advanceBlock(); +} + +void Blitter::putBlock(unsigned int d1, unsigned int d2, unsigned int d3, unsigned int d4) { + if(_cur.getX() + 3 < _src.right() && _cur.getY() + 3 < _src.bottom()) { // This is clipping + assert((_clip.getX() & 3) == 0); + unsigned int * dst = (unsigned int *)_offset; + int line_size = _clip.getX() >> 2; + + *dst = d4; dst += line_size; + *dst = d3; dst += line_size; + *dst = d2; dst += line_size; + *dst = d1; + +#ifdef DEBUG_CLIPPER + } else { + _clippedBlock ++; +#endif + } + advanceBlock(); +} + +void Blitter::putBlock(unsigned char * data) { + if(_cur.getX() + 3 < _src.right() && _cur.getY() + 3 < _src.bottom()) { // This is clipping + assert((_clip.getX() & 3) == 0); + unsigned int * dst = (unsigned int *)_offset; + int line_size = _clip.getX() >> 2; + unsigned int * src = (unsigned int *)data; + *dst = *src++; dst += line_size; + *dst = *src++; dst += line_size; + *dst = *src++; dst += line_size; + *dst = *src++; +#ifdef DEBUG_CLIPPER + } else { + _clippedBlock ++; +#endif + } + advanceBlock(); +} + +void Blitter::putBlock(Chunck & src) { + if(_cur.getX() + 3 < _src.right() && _cur.getY() + 3 < _src.bottom()) { // This is clipping + assert((_clip.getX() & 3) == 0); + unsigned int * dst = (unsigned int *)_offset; + int line_size = _clip.getX() >> 2; + *dst = src.getDword(); dst += line_size; + *dst = src.getDword(); dst += line_size; + *dst = src.getDword(); dst += line_size; + *dst = src.getDword(); +#ifdef DEBUG_CLIPPER + } else { + _clippedBlock ++; +#endif + } + advanceBlock(); +} + +void Blitter::blockCopy(int offset) { + if(_cur.getX() + 3 < _src.right() && _cur.getY() + 3 < _src.bottom()) {// This is clipping + char * dst = _offset; + *((unsigned int *)dst) = *((unsigned int *)(dst + offset)); + dst += _clip.getX(); + *((unsigned int *)dst) = *((unsigned int *)(dst + offset)); + dst += _clip.getX(); + *((unsigned int *)dst) = *((unsigned int *)(dst + offset)); + dst += _clip.getX(); + *((unsigned int *)dst) = *((unsigned int *)(dst + offset)); +#ifdef DEBUG_CLIPPER + } else { + _clippedBlock ++; +#endif + } + advanceBlock(); +} diff --git a/scumm/smush/blitter.h b/scumm/smush/blitter.h new file mode 100644 index 0000000000..3515ba0a20 --- /dev/null +++ b/scumm/smush/blitter.h @@ -0,0 +1,79 @@ +/* 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$ + * + */ + +#ifndef __BLITTER_H_ +#define __BLITTER_H_ + +#include "config.h" + +#ifdef DEBUG +# ifndef NO_DEBUG_CLIPPER +# define DEBUG_CLIPPER +# endif +#else +# ifdef DEBUG_CLIPPER +# error DEBUG_CLIPPER defined without DEBUG +# endif +#endif + +#include "rect.h" + +class Chunck; +/*! @brief class for handling blitting on a frame buffer + + This class allows to perform secure blitting to a frame buffer in several ways. + This means that clipping is performed, so that only the part that you want to modify can be modified. +*/ +class Blitter { +private: + char * _ptr; //!< This is the pointer to the start of the frame buffer + char * _offset; //!< This is the current pointer in the frame buffer + Point _clip; //!< This is the size of the frame buffer (width/height) + Rect _src; //!< This is the size and position of the destination rectangle + Point _cur; //!< This is the current position in the destination rectangle + bool _outside; //!< flag that is set to \c true when the blitter reach the end of the destination rectangle +#ifdef DEBUG_CLIPPER + int _clipped; + int _clippedBlock; +#endif +public: + /*! @brief constructor + + @param buffer the frame buffer to blit to + @param dstsize the size of the frame buffer + @param src the rectangle to blit to + */ + Blitter(char * buffer, const Point & dstsize, const Rect & src); + virtual ~Blitter(); + void blit(char *, unsigned int); //!< This method allows to blit directly some data from a buffer + void blit(Chunck &, unsigned int); //!< This method allows to blit directly some data from a chunck + void put(char); //!< This method allows to blit one byte + void put(char, unsigned int); //!< This method allows to blit one byte several times + void advance(int = 1, int = 0); //!< This method allows to advance the current position in the blitter + void advanceBlock(int = 1, int = 0); //!< This method allows to advance the current position in the blitter in terms of blocks + void putBlock(unsigned int); //!< This method allows to blit one block from an int value repeated 4 time + void putBlock(Chunck &); //!< This method allows to blit one block directly read from a chunck + void putBlock(unsigned char *); //!< This method allows to blit one block directly from a buffer + void putBlock(unsigned int, unsigned int, unsigned int, unsigned int); //!< This method allows to blit one block from a 4 int value + void blockCopy(int); //!< This method allows to copy one block from another separated by the given offset +}; + +#endif diff --git a/scumm/smush/brenderer.cpp b/scumm/smush/brenderer.cpp new file mode 100644 index 0000000000..1efa8be5b0 --- /dev/null +++ b/scumm/smush/brenderer.cpp @@ -0,0 +1,75 @@ +/* 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 "brenderer.h" + +#include <assert.h> + +void BaseRenderer::clean() { + if(_data) { + delete[] _data; + _data = 0; + _width = _height = 0; + } +} + +BaseRenderer::BaseRenderer() : + _data(0), + _frame(0), + _nbframes(0), + _width(0), + _height(0) { +} + +BaseRenderer::~BaseRenderer() { + clean(); +} + +bool BaseRenderer::initFrame(const Point & p) { + clean(); + _width = p.getX(); + _height = p.getY(); + assert(_width && _height); + _data = new char[_width * _height]; + if(!_data) error("base_renderer unable to allocate frame buffer"); + return true; +} + +char * BaseRenderer::lockFrame(int frame) { + _frame = frame; + if(!_data) error("no allocated image buffer in lock_frame"); + return _data; +} + +bool BaseRenderer::unlockFrame() { + return true; +} + +bool BaseRenderer::flipFrame() { + save(); + return true; +} + +bool BaseRenderer::setPalette(const Palette & pal) { + _pal = pal; + return true; +} diff --git a/scumm/smush/brenderer.h b/scumm/smush/brenderer.h new file mode 100644 index 0000000000..792fd034c4 --- /dev/null +++ b/scumm/smush/brenderer.h @@ -0,0 +1,86 @@ +/* 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$ + * + */ + +#ifndef __BRENDERER_H_ +#define __BRENDERER_H_ + +#include "config.h" + +#include "renderer.h" +#include "palette.h" +#include "rect.h" + +/*! @brief base class for easily creating ::renderer instances + + This class implements some function available in the ::renderer abstract class, so that + creation of subclasses of ::renderer is easier. +*/ +class BaseRenderer : public Renderer { +private: + Palette _pal; //!< The current palette + char * _data; //!< The current frame buffer + int _frame; //!< The current frame number + int _nbframes; //!< The number of frames in the animation + int _width; //!< The current frame's width + int _height; //!< The current frame's height + const char * _fname; //!< The filename of the animation being played +protected: + virtual void save(int frame = -1) = 0; + +protected: + const char * getFilename() const { return _fname; }; //!< accessor for animation filename + int getNbframes() const { return _nbframes; }; //!< accessor for number of frames + int getWidth() const { return _width; }; //!< accessor for current width + int getHeight() const { return _height; }; //!< accessor for current height + const Palette & pal() const { return _pal; }; //!< accessor for current palette + const char * data() const { return _data; }; //!< accessor for current frame buffer + void clean(); //!< memory cleanup (deletes frame buffer) + void setFrame(int f) { _frame = f; }; //!< allows to change the frame number +public: + int getFrame() const { return _frame; }; //!< accessor for current frame number + BaseRenderer(); + virtual ~BaseRenderer(); + + virtual bool initFrame(const Point & size); + virtual char * lockFrame(int frame); + virtual bool unlockFrame(); + virtual bool flipFrame(); + virtual bool setPalette(const Palette & pal); + virtual bool startDecode(const char * fname, int version, int nbframes) { _fname = fname; _nbframes = nbframes; return true; } + virtual Mixer * getMixer() { return 0; }; + virtual bool prematureClose() { return false; }; +}; + +/*! @brief A null ::renderer + + This class completely implements ::renderer, without actually doing anything. + This class is useful for performance measurements. +*/ +class NullRenderer : public BaseRenderer { +protected: + void save(int frame = -1) {}; +public: + NullRenderer() {}; + virtual ~NullRenderer() {}; + bool wait(int ms) { return true; }; +}; + +#endif diff --git a/scumm/smush/channel.h b/scumm/smush/channel.h new file mode 100644 index 0000000000..5010c3f581 --- /dev/null +++ b/scumm/smush/channel.h @@ -0,0 +1,157 @@ +/* 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$ + * + */ + +#ifndef __CHANNEL_H_ +#define __CHANNEL_H_ + +#include "config.h" + +#ifdef DEBUG +# ifndef NO_DEBUG_CHANNEL +# define DEBUG_CHANNEL +# endif +#else +# ifdef DEBUG_CHANNEL +# error DEBUG_CHANNEL defined without DEBUG +# endif +#endif + +class Chunck; +class ContChunck; + +/*! @brief interface for a sound channel (a track) + + This is the interface for sound channels. +*/ +class _Channel { +public: + virtual ~_Channel() {}; + // called by the smush_player + virtual bool appendData(Chunck & b, int size) = 0; + virtual bool setParameters(int, int, int, int) = 0; + virtual bool checkParameters(int, int, int, int, int) = 0; + // called by the mixer + virtual bool isTerminated() const = 0; + virtual int availableSoundData() const = 0; + virtual void getSoundData(short * sound_buffer, int size) = 0; // size is in sample + virtual void getSoundData(char * sound_buffer, int size) = 0; + virtual bool getParameters(int &rate, bool &stereo, bool &is_16bit) = 0; + virtual int getTrackIdentifier() const = 0; +}; + +class SaudChannel : public _Channel { +private: + int _track; //!< The track identifier + int _nbframes; //!< number of frames of the track (unused) + int _dataSize; //!< the size of the sound buffer + int _frequency; //!< the frequency target of the track (always 22050) + bool _inData; //!< are we processing data ? + bool _markReached; //!< set to \c true when the SMRK tag is reached + int _flags; //!< current flags of the track (unused) + int _volume; //!< the current track volume + int _balance; //!< the current track balance + int _index; //!< the current PSAD index (for coherency checking) + short _voltable[2][256]; //!< the precalculated volume table (stereo 16 bits) + unsigned char * _tbuffer; //!< data temporary buffer + int _tbufferSize; //!< temporary buffer size + unsigned char * _sbuffer; //!< sound buffer + int _sbufferSize; //!< sound buffer size + +protected: + void handleStrk(Chunck & c); + void handleSmrk(Chunck & c); + void handleShdr(Chunck & c); + bool handleSubTags(int & offset); + bool processBuffer(); + void recalcVolumeTable(); + +public: + SaudChannel(int track, int freq); + virtual ~SaudChannel(); + bool isTerminated() const; + bool setParameters(int duration, int flags, int vol1, int vol2); + bool checkParameters(int index, int duration, int flags, int vol1, int vol2); + bool appendData(Chunck & b, int size); + int availableSoundData() const; + void getSoundData(short * sound_buffer, int size); + void getSoundData(char * sound_buffer, int size) { error("16bit request for SAUD channel should never happen"); }; + bool getParameters(int &rate, bool &stereo, bool &is_16bit) { + rate = _frequency; + stereo = true; + is_16bit = true; + return true; + }; + virtual int getTrackIdentifier() const { return _track; }; +}; + +/*! @brief class for a IACT sound ::channel (a The Dig track) + + This class implements a channel specifically for The Dig. + + \bug for unknown reason, some sound have a too long duration, or repeat themselves. +*/ +class ImuseChannel : public _Channel { +private: + int _track; //!< the track number + unsigned char * _tbuffer; //!< data temporary buffer + int _tbufferSize; //!< temporary buffer size + unsigned char * _sbuffer; //!< sound buffer + int _sbufferSize; //!< sound buffer size + int _srbufferSize; + int _frequency; //!< the target frequency of the ::mixer + int _dataSize; //!< remaining size of sound data in the iMUS buffer + bool _inData; + + int _bitsize; //!< the bitsize of the original data + int _rate; //!< the sampling rate of the original data + int _channels; //!< the number of channels of the original data + +protected: + int decode(int size, int &ret); + void decode(); + bool processBuffer(); + bool handleMap(Chunck &); + bool handleFormat(Chunck &); + bool handleText(Chunck &); + bool handleRegion(Chunck &); + bool handleStop(Chunck &); + bool handleSubTags(int & offset); + +public: + ImuseChannel(int track, int freq); + virtual ~ImuseChannel(); + bool isTerminated() const; + bool setParameters(int nbframes, int size, int unk1, int unk2); + bool checkParameters(int index, int nbframes, int size, int unk1, int unk2); + bool appendData(Chunck & b, int size); + int availableSoundData() const; + void getSoundData(short * sound_buffer, int size); + void getSoundData(char * sound_buffer, int size); + bool getParameters(int &rate, bool &stereo, bool &is_16bit) { + rate = _frequency; + stereo = (_channels == 2); + is_16bit = (_bitsize > 8); + return true; + }; + virtual int getTrackIdentifier() const { return _track; }; +}; + +#endif diff --git a/scumm/smush/chunck.cpp b/scumm/smush/chunck.cpp new file mode 100644 index 0000000000..b4bf99fcbc --- /dev/null +++ b/scumm/smush/chunck.cpp @@ -0,0 +1,288 @@ +/* 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 "chunck.h" + +#include <stdio.h> // for FILE, fopen, fclose, fseek and ftell +#include <string.h> // for memcpy + +/*! @brief very small and fast wrapper for a ifstream. + + implements reference counting, so that ::file_chunck does not leak memory ! +*/ +class FilePtr { + char * _filename; + FILE * _ifs; + int _refcount; + int _curPos; +public: + FilePtr(const char * fname) : _refcount(1), _curPos(0) { + debug(9, "FilePtr created for %s", fname); + _filename = strdup(fname); + _ifs = fopen(fname, "rb"); + if(_ifs == NULL) error("FilePtr unable to read file \"%s\"", fname); + } + ~FilePtr() { + debug(9, "FilePtr destroyed for %s", _filename); + free(_filename); + fclose(_ifs); + } + int tell() { + return _curPos; + } + bool seek(int pos) { + if(pos != _curPos) { + fseek(_ifs, pos, SEEK_SET); + _curPos = pos; + } + return true; + } + bool read(void * ptr, int size) { + fread(ptr, size, 1, _ifs); + _curPos += size; + return true; + } + void incRef() { + _refcount++; + } + void decRef() { + if(--_refcount == 0) + delete this; + } +}; + +const char * Chunck::ChunckString(Chunck::type t) { + static char data[5]; + data[0] = (char)((t >> 24) & 0xFF); + data[1] = (char)((t >> 16) & 0xFF); + data[2] = (char)((t >> 8) & 0xFF); + data[3] = (char)((t >> 0) & 0xFF); + data[4] = 0; + return data; +} + +FileChunck::FileChunck() : _data(0), _type(0), _size(0), _curPos(0) { +} + +FileChunck::~FileChunck() { + if(_data) _data->decRef(); +} + +FileChunck::FileChunck(const char * fname) { + _data = new FilePtr(fname); + _data->read(&_type, 4); + _type = TO_BE_32(_type); + _data->read(&_size, 4); + _size = TO_BE_32(_size); + _offset = _data->tell(); + _curPos = 0; +} + +Chunck::type FileChunck::getType() const { + return _type; +} + +unsigned int FileChunck::getSize() const { + return _size; +} + +Chunck * FileChunck::subBlock() { + FileChunck * ptr = new FileChunck; + ptr->_data = _data; + _data->incRef(); + _data->seek(_offset + _curPos); + unsigned int temp; + _data->read(&temp, 4); + ptr->_type = TO_BE_32(temp); + _data->read(&temp, 4); + ptr->_size = TO_BE_32(temp); + ptr->_offset = _offset + _curPos + 8; + ptr->_curPos = 0; + seek(8 + ptr->getSize()); + return ptr; +} + +bool FileChunck::eof() const { + return _curPos >= _size; +} + +unsigned int FileChunck::tell() const { + return _curPos; +} + +bool FileChunck::seek(int delta, seek_type dir) { + switch(dir) { + case seek_cur: + _curPos += delta; + break; + case seek_start: + if(delta < 0) error("invalid seek request"); + _curPos = (unsigned int)delta; + break; + case seek_end: + if(delta > 0 || (_size + delta) < 0) error("invalid seek request"); + _curPos = (unsigned int)(_size + delta); + break; + } + if(_curPos > _size) { + error("invalid seek request : %d > %d (delta == %d)", _curPos, _size, delta); + } + return true; +} + +bool FileChunck::read(void * buffer, unsigned int size) { + if(size <= 0 || (_curPos + size) > _size) error("invalid buffer read request"); + _data->seek(_offset + _curPos); + _data->read(buffer, size); + _curPos += size; + return true; +} + +char FileChunck::getChar() { + if(_curPos >= _size) error("invalid char read request"); + _data->seek(_offset + _curPos); + char buffer; + _data->read(&buffer, sizeof(buffer)); + _curPos+= sizeof(buffer); + return buffer; +} + +unsigned char FileChunck::getByte() { + if(_curPos >= _size) error("invalid byte read request"); + _data->seek(_offset + _curPos); + unsigned char buffer; + _data->read(&buffer, sizeof(buffer)); + _curPos+= sizeof(buffer); + return buffer; +} + +short FileChunck::getShort() { + unsigned short buffer = getWord(); + return *((short*)&buffer); +} + +unsigned short FileChunck::getWord() { + if(_curPos >= _size - 1) error("invalid word read request"); + _data->seek(_offset + _curPos); + unsigned short buffer; + _data->read(&buffer, sizeof(buffer)); + _curPos+= sizeof(buffer); + return TO_LE_16(buffer); +} + +unsigned int FileChunck::getDword() { + if(_curPos >= _size - 3) error("invalid dword read request"); + _data->seek(_offset + _curPos); + unsigned int buffer; + _data->read(&buffer, sizeof(buffer)); + _curPos+= sizeof(buffer); + return TO_LE_32(buffer); +} + +ContChunck::ContChunck(char * data) { + if(data == 0) error("Chunck() called with NULL pointer"); + _type = (Chunck::type)READ_BE_UINT32(data); + _size = READ_BE_UINT32(data+4); + _data = data + sizeof(Chunck::type) + sizeof(unsigned int); + _curPos = 0; +} + +Chunck::type ContChunck::getType() const { + return _type; +} + +unsigned int ContChunck::getSize() const { + return _size; +} + +Chunck * ContChunck::subBlock() { + ContChunck * ptr = new ContChunck(_data + _curPos); + seek(sizeof(Chunck::type) + sizeof(unsigned int) + ptr->getSize()); + return ptr; +} + +bool ContChunck::eof() const { + return _curPos >= _size; +} + +unsigned int ContChunck::tell() const { + return _curPos; +} + +bool ContChunck::seek(int delta, seek_type dir) { + switch(dir) { + case seek_cur: + _curPos += delta; + break; + case seek_start: + if(delta < 0) error("invalid seek request"); + _curPos = (unsigned int)delta; + break; + case seek_end: + if(delta > 0 || (_size + delta) < 0) error("invalid seek request"); + _curPos = (unsigned int)(_size + delta); + break; + } + if(_curPos > _size) { + error("invalid seek request : %d > %d (delta == %d)", _curPos, _size, delta); + } + return true; +} + +bool ContChunck::read(void * buffer, unsigned int size) { + if(size <= 0 || (_curPos + size) > _size) error("invalid buffer read request"); + memcpy(buffer, _data + _curPos, size); + _curPos += size; + return true; +} + +char ContChunck::getChar() { + if(_curPos >= _size) error("invalid char read request"); + return _data[_curPos++]; +} + +unsigned char ContChunck::getByte() { + if(_curPos >= _size) error("invalid byte read request"); + unsigned char * ptr = (unsigned char *)(_data + _curPos); + _curPos += 1; + return *ptr; +} + +short ContChunck::getShort() { + if(_curPos >= _size - 1) error("invalid short read request"); + unsigned short buffer = getWord(); + return *((short*)&buffer); +} + +unsigned short ContChunck::getWord() { + if(_curPos >= _size - 1) error("invalid word read request"); + unsigned short * ptr = (unsigned short *)(_data + _curPos); + _curPos += 2; + return READ_LE_UINT16(ptr); +} + +unsigned int ContChunck::getDword() { + if(_curPos >= _size - 3) error("invalid dword read request"); + unsigned int * ptr = (unsigned int *)(_data + _curPos); + _curPos += 4; + return READ_LE_UINT32(ptr); +} diff --git a/scumm/smush/chunck.h b/scumm/smush/chunck.h new file mode 100644 index 0000000000..3a1ec6adda --- /dev/null +++ b/scumm/smush/chunck.h @@ -0,0 +1,121 @@ +/* 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$ + * + */ + +#ifndef __CHUNCK_H_ +#define __CHUNCK_H_ + +#include "config.h" + +/*! @brief Interface for chunck handling + + This class is an interface for reading from a chunck. + + \todo handle big endian system. +*/ +class Chunck { +public: + enum seek_type { seek_start, seek_end, seek_cur }; + virtual ~Chunck() {}; + typedef unsigned int type; //!< type of a chunck (i.e. The first 4byte field of the chunck structure). + /*! @brief convert a type to a string + + Utility function that convert a type to a string. + + @param t the type to convert to a string + + @return the converted string + */ + static const char * ChunckString(type t); + + virtual type getType() const = 0; //!< return the type of the chunck + virtual unsigned int getSize() const = 0; //!< return the size of the chunck + virtual Chunck * subBlock() = 0; //!< extract a subchunck from the current read position + virtual bool eof() const = 0; //!< is the chunck completely read ? + virtual unsigned int tell() const = 0; //!< get the chunck current read position + virtual bool seek(int delta, seek_type dir = seek_cur) = 0; //!< move the current read position inside the chunck + virtual bool read(void * buffer, unsigned int size) = 0; //!< read some data for the current read position + virtual char getChar() = 0; //!< extract the character at the current read position + virtual unsigned char getByte() = 0; //!< extract the byte at the current read position + virtual short getShort() = 0; //!< extract the short at the current read position + virtual unsigned short getWord() = 0; //!< extract the word at the current read position + virtual unsigned int getDword()= 0; //!< extract the dword at the current read position +}; + +class FilePtr; + +/*! @brief file based ::chunck + + This class is an implementation of ::chunck that handles file. + +*/ +class FileChunck : public Chunck { +private: + FilePtr * _data; + type _type; + unsigned int _size; + unsigned int _offset; + unsigned int _curPos; +protected: + FileChunck(); +public: + FileChunck(const char * fname); + virtual ~FileChunck(); + type getType() const; + unsigned int getSize() const; + Chunck * subBlock(); + bool eof() const; + unsigned int tell() const; + bool seek(int delta, seek_type dir = seek_cur); + bool read(void * buffer, unsigned int size); + char getChar(); + unsigned char getByte(); + short getShort(); + unsigned short getWord(); + unsigned int getDword(); +}; + +/*! @brief memory based ::chunck + + This class is an implementation of ::chunck that handles a memory buffer. +*/ +class ContChunck : public Chunck { +private: + char * _data; + Chunck::type _type; + unsigned int _size; + unsigned int _curPos; +public: + ContChunck(char * data); + Chunck::type getType() const; + unsigned int getSize() const; + Chunck * subBlock(); + bool eof() const; + unsigned int tell() const; + bool seek(int delta, seek_type dir = seek_cur); + bool read(void * buffer, unsigned int size); + char getChar(); + unsigned char getByte(); + short getShort(); + unsigned short getWord(); + unsigned int getDword(); +}; + +#endif diff --git a/scumm/smush/chunck_type.h b/scumm/smush/chunck_type.h new file mode 100644 index 0000000000..e3e2596175 --- /dev/null +++ b/scumm/smush/chunck_type.h @@ -0,0 +1,57 @@ +/* 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$ + * + */ + +#ifndef __CHUNCK_TYPE_H +#define __CHUNCK_TYPE_H + +#include "chunck.h" + +#define MAKE_TYPE(a,b,c,d) (Chunck::type)( ((a) << 24) | ((b) << 16) | ((c) << 8) | (d) ) + +static const Chunck::type TYPE_ANIM = MAKE_TYPE('A', 'N', 'I', 'M'); +static const Chunck::type TYPE_AHDR = MAKE_TYPE('A', 'H', 'D', 'R'); +static const Chunck::type TYPE_FRME = MAKE_TYPE('F', 'R', 'M', 'E'); +static const Chunck::type TYPE_NPAL = MAKE_TYPE('N', 'P', 'A', 'L'); +static const Chunck::type TYPE_FOBJ = MAKE_TYPE('F', 'O', 'B', 'J'); +static const Chunck::type TYPE_PSAD = MAKE_TYPE('P', 'S', 'A', 'D'); +static const Chunck::type TYPE_TRES = MAKE_TYPE('T', 'R', 'E', 'S'); +static const Chunck::type TYPE_XPAL = MAKE_TYPE('X', 'P', 'A', 'L'); +static const Chunck::type TYPE_IACT = MAKE_TYPE('I', 'A', 'C', 'T'); +static const Chunck::type TYPE_STOR = MAKE_TYPE('S', 'T', 'O', 'R'); +static const Chunck::type TYPE_FTCH = MAKE_TYPE('F', 'T', 'C', 'H'); +static const Chunck::type TYPE_SKIP = MAKE_TYPE('S', 'K', 'I', 'P'); +static const Chunck::type TYPE_STRK = MAKE_TYPE('S', 'T', 'R', 'K'); +static const Chunck::type TYPE_SMRK = MAKE_TYPE('S', 'M', 'R', 'K'); +static const Chunck::type TYPE_SHDR = MAKE_TYPE('S', 'H', 'D', 'R'); +static const Chunck::type TYPE_SDAT = MAKE_TYPE('S', 'D', 'A', 'T'); +static const Chunck::type TYPE_SAUD = MAKE_TYPE('S', 'A', 'U', 'D'); +static const Chunck::type TYPE_iMUS = MAKE_TYPE('i', 'M', 'U', 'S'); +static const Chunck::type TYPE_FRMT = MAKE_TYPE('F', 'R', 'M', 'T'); +static const Chunck::type TYPE_TEXT = MAKE_TYPE('T', 'E', 'X', 'T'); +static const Chunck::type TYPE_REGN = MAKE_TYPE('R', 'E', 'G', 'N'); +static const Chunck::type TYPE_STOP = MAKE_TYPE('S', 'T', 'O', 'P'); +static const Chunck::type TYPE_MAP_ = MAKE_TYPE('M', 'A', 'P', ' '); +static const Chunck::type TYPE_DATA = MAKE_TYPE('D', 'A', 'T', 'A'); +static const Chunck::type TYPE_ETRS = MAKE_TYPE('E', 'T', 'R', 'S'); + +#undef MAKE_TYPE + +#endif diff --git a/scumm/smush/codec1.cpp b/scumm/smush/codec1.cpp new file mode 100644 index 0000000000..495b50404f --- /dev/null +++ b/scumm/smush/codec1.cpp @@ -0,0 +1,76 @@ +/* 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 "codec1.h" +#include "chunck.h" +#include "blitter.h" + +Codec1Decoder::~Codec1Decoder() { +} + +bool Codec1Decoder::decode(Blitter & dst, Chunck & src) { + int val; + int size_line; + int code, length; + int h, height = getRect().height(); + + for(h = 0; h < height; h++) { + size_line = src.getWord(); // size of compressed line ! +#ifdef DEBUG_CODEC1 + debug(7, "codec1 : h == %d, size_line == %d", h, size_line); +#endif + while(size_line > 0) { + code = src.getByte(); + size_line --; + length = (code >> 1) + 1; +#ifdef DEBUG_CODEC1 + debug(7, "codec1 : length == %d", length); +#endif + if(code & 1) { + val = src.getByte(); + size_line --; + if(val) dst.put(val, length); + else dst.advance(length); +#ifdef DEBUG_CODEC1 + debug(7, "codec1 : blitting %d times %d", length, val); +#endif + } else { + size_line -= length; +#ifdef DEBUG_CODEC1 + debug(7, "codec1 : blitting %d entries", length); +#endif + while(length--) { + val = src.getByte(); + if(val) dst.put(val); + else dst.advance(); + } + } + } + } +#ifdef DEBUG_CODEC1 + if(!src.eof()) { + int len = src.getSize() - src.tell(); + debug(7, "codec1: remaining length after decode == %d", len); + } +#endif + return true; +} diff --git a/scumm/smush/codec1.h b/scumm/smush/codec1.h new file mode 100644 index 0000000000..ae60518d6a --- /dev/null +++ b/scumm/smush/codec1.h @@ -0,0 +1,48 @@ +/* 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$ + * + */ + +#ifndef __CODEC1_H_ +#define __CODEC1_H_ + +#include "config.h" + +#ifdef DEBUG +# ifndef NO_DEBUG_CODEC1 +# define DEBUG_CODEC1 +# endif +#else +# ifdef DEBUG_CODEC1 +# error DEBUG_CODEC1 defined without DEBUG +# endif +#endif + +#include "decoder.h" + +/*! @brief ::decoder for codec 1 and 3. + +*/ +class Codec1Decoder : public Decoder { +public: + virtual ~Codec1Decoder(); + bool decode(Blitter &, Chunck &); +}; + +#endif diff --git a/scumm/smush/codec37.cpp b/scumm/smush/codec37.cpp new file mode 100644 index 0000000000..5adaa56d54 --- /dev/null +++ b/scumm/smush/codec37.cpp @@ -0,0 +1,436 @@ +/* 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 "codec37.h" +#include "chunck.h" +#include "blitter.h" + +#include <assert.h> +#include <string.h> // for memset + +bool Codec37Decoder::initSize(const Point & p, const Rect & r) { + if(r.width() != getRect().width() && r.height() != getRect().height()) { + if( + (r.width() != 320 || r.height() != 200) && + (r.width() != 384 || r.height() != 242) && + (r.width() != 640 || r.height() != 480) + ) + return false; + Decoder::initSize(p, r); + clean(); + int frame_size = getRect().width() * getRect().height(); + _deltaSize = frame_size * 2 + DELTA_ADD * 4; + _deltaBuf = new unsigned char[_deltaSize]; + if(_deltaBuf == 0) error("unable to allocate decoder buffer"); + _deltaBufs[0] = _deltaBuf + DELTA_ADD; + _deltaBufs[1] = _deltaBuf + frame_size + DELTA_ADD * 3; + _offsetTable = new short[255]; + if(_offsetTable == 0) error("unable to allocate decoder offset table"); + _tableLastPitch = -1; + _tableLastIndex = -1; + return true; + } + return false; +} + +Codec37Decoder::Codec37Decoder() { + _deltaSize = 0; + _deltaBuf = 0; + _deltaBufs[0] = 0; + _deltaBufs[1] = 0; + _curtable = 0; + _offsetTable = 0; + _tableLastPitch = -1; + _tableLastIndex = -1; + _prevSeqNb = 32768; // Some invalid number +} + +void Codec37Decoder::clean() { + if(_offsetTable) { + delete []_offsetTable; + _offsetTable = 0; + _tableLastPitch = -1; + _tableLastIndex = -1; + } + if(_deltaBuf) { + delete []_deltaBuf; + _deltaSize = 0; + _deltaBuf = 0; + _deltaBufs[0] = 0; + _deltaBufs[1] = 0; + } +} + +Codec37Decoder::~Codec37Decoder() { + clean(); +} + +void Codec37Decoder::maketable(int pitch, int index) { + static const char maketable_bytes[] = { + 0, 0, 1, 0, 2, 0, 3, 0, 5, 0, 8, 0, 13, 0, 21, 0, + -1, 0, -2, 0, -3, 0, -5, 0, -8, 0, -13, 0, -17, 0, -21, 0, + 0, 1, 1, 1, 2, 1, 3, 1, 5, 1, 8, 1, 13, 1, 21, 1, + -1, 1, -2, 1, -3, 1, -5, 1, -8, 1, -13, 1, -17, 1, -21, 1, + 0, 2, 1, 2, 2, 2, 3, 2, 5, 2, 8, 2, 13, 2, 21, 2, + -1, 2, -2, 2, -3, 2, -5, 2, -8, 2, -13, 2, -17, 2, -21, 2, + 0, 3, 1, 3, 2, 3, 3, 3, 5, 3, 8, 3, 13, 3, 21, 3, + -1, 3, -2, 3, -3, 3, -5, 3, -8, 3, -13, 3, -17, 3, -21, 3, + 0, 5, 1, 5, 2, 5, 3, 5, 5, 5, 8, 5, 13, 5, 21, 5, + -1, 5, -2, 5, -3, 5, -5, 5, -8, 5, -13, 5, -17, 5, -21, 5, + 0, 8, 1, 8, 2, 8, 3, 8, 5, 8, 8, 8, 13, 8, 21, 8, + -1, 8, -2, 8, -3, 8, -5, 8, -8, 8, -13, 8, -17, 8, -21, 8, + 0, 13, 1, 13, 2, 13, 3, 13, 5, 13, 8, 13, 13, 13, 21, 13, + -1, 13, -2, 13, -3, 13, -5, 13, -8, 13, -13, 13, -17, 13, -21, 13, + 0, 21, 1, 21, 2, 21, 3, 21, 5, 21, 8, 21, 13, 21, 21, 21, + -1, 21, -2, 21, -3, 21, -5, 21, -8, 21, -13, 21, -17, 21, -21, 21, + 0, -1, 1, -1, 2, -1, 3, -1, 5, -1, 8, -1, 13, -1, 21, -1, + -1, -1, -2, -1, -3, -1, -5, -1, -8, -1, -13, -1, -17, -1, -21, -1, + 0, -2, 1, -2, 2, -2, 3, -2, 5, -2, 8, -2, 13, -2, 21, -2, + -1, -2, -2, -2, -3, -2, -5, -2, -8, -2, -13, -2, -17, -2, -21, -2, + 0, -3, 1, -3, 2, -3, 3, -3, 5, -3, 8, -3, 13, -3, 21, -3, + -1, -3, -2, -3, -3, -3, -5, -3, -8, -3, -13, -3, -17, -3, -21, -3, + 0, -5, 1, -5, 2, -5, 3, -5, 5, -5, 8, -5, 13, -5, 21, -5, + -1, -5, -2, -5, -3, -5, -5, -5, -8, -5, -13, -5, -17, -5, -21, -5, + 0, -8, 1, -8, 2, -8, 3, -8, 5, -8, 8, -8, 13, -8, 21, -8, + -1, -8, -2, -8, -3, -8, -5, -8, -8, -8, -13, -8, -17, -8, -21, -8, + 0, -13, 1, -13, 2, -13, 3, -13, 5, -13, 8, -13, 13, -13, 21, -13, + -1, -13, -2, -13, -3, -13, -5, -13, -8, -13, -13, -13, -17, -13, -21, -13, + 0, -17, 1, -17, 2, -17, 3, -17, 5, -17, 8, -17, 13, -17, 21, -17, + -1, -17, -2, -17, -3, -17, -5, -17, -8, -17, -13, -17, -17, -17, -21, -17, + 0, -21, 1, -21, 2, -21, 3, -21, 5, -21, 8, -21, 13, -21, 21, -21, + -1, -21, -2, -21, -3, -21, -5, -21, -8, -21, -13, -21, -17, -21, 0, 0, + -8, -29, 8, -29, -18, -25, 17, -25, 0, -23, -6, -22, 6, -22, -13, -19, + 12, -19, 0, -18, 25, -18, -25, -17, -5, -17, 5, -17, -10, -15, 10, -15, + 0, -14, -4, -13, 4, -13, 19, -13, -19, -12, -8, -11, -2, -11, 0, -11, + 2, -11, 8, -11, -15, -10, -4, -10, 4, -10, 15, -10, -6, -9, -1, -9, + 1, -9, 6, -9, -29, -8, -11, -8, -8, -8, -3, -8, 3, -8, 8, -8, + 11, -8, 29, -8, -5, -7, -2, -7, 0, -7, 2, -7, 5, -7, -22, -6, + -9, -6, -6, -6, -3, -6, -1, -6, 1, -6, 3, -6, 6, -6, 9, -6, + 22, -6, -17, -5, -7, -5, -4, -5, -2, -5, 0, -5, 2, -5, 4, -5, + 7, -5, 17, -5, -13, -4, -10, -4, -5, -4, -3, -4, -1, -4, 0, -4, + 1, -4, 3, -4, 5, -4, 10, -4, 13, -4, -8, -3, -6, -3, -4, -3, + -3, -3, -2, -3, -1, -3, 0, -3, 1, -3, 2, -3, 4, -3, 6, -3, + 8, -3, -11, -2, -7, -2, -5, -2, -3, -2, -2, -2, -1, -2, 0, -2, + 1, -2, 2, -2, 3, -2, 5, -2, 7, -2, 11, -2, -9, -1, -6, -1, + -4, -1, -3, -1, -2, -1, -1, -1, 0, -1, 1, -1, 2, -1, 3, -1, + 4, -1, 6, -1, 9, -1, -31, 0, -23, 0, -18, 0, -14, 0, -11, 0, + -7, 0, -5, 0, -4, 0, -3, 0, -2, 0, -1, 0, 0, -31, 1, 0, + 2, 0, 3, 0, 4, 0, 5, 0, 7, 0, 11, 0, 14, 0, 18, 0, + 23, 0, 31, 0, -9, 1, -6, 1, -4, 1, -3, 1, -2, 1, -1, 1, + 0, 1, 1, 1, 2, 1, 3, 1, 4, 1, 6, 1, 9, 1, -11, 2, + -7, 2, -5, 2, -3, 2, -2, 2, -1, 2, 0, 2, 1, 2, 2, 2, + 3, 2, 5, 2, 7, 2, 11, 2, -8, 3, -6, 3, -4, 3, -2, 3, + -1, 3, 0, 3, 1, 3, 2, 3, 3, 3, 4, 3, 6, 3, 8, 3, + -13, 4, -10, 4, -5, 4, -3, 4, -1, 4, 0, 4, 1, 4, 3, 4, + 5, 4, 10, 4, 13, 4, -17, 5, -7, 5, -4, 5, -2, 5, 0, 5, + 2, 5, 4, 5, 7, 5, 17, 5, -22, 6, -9, 6, -6, 6, -3, 6, + -1, 6, 1, 6, 3, 6, 6, 6, 9, 6, 22, 6, -5, 7, -2, 7, + 0, 7, 2, 7, 5, 7, -29, 8, -11, 8, -8, 8, -3, 8, 3, 8, + 8, 8, 11, 8, 29, 8, -6, 9, -1, 9, 1, 9, 6, 9, -15, 10, + -4, 10, 4, 10, 15, 10, -8, 11, -2, 11, 0, 11, 2, 11, 8, 11, + 19, 12, -19, 13, -4, 13, 4, 13, 0, 14, -10, 15, 10, 15, -5, 17, + 5, 17, 25, 17, -25, 18, 0, 18, -12, 19, 13, 19, -6, 22, 6, 22, + 0, 23, -17, 25, 18, 25, -8, 29, 8, 29, 0, 31, 0, 0, -6, -22, + 6, -22, -13, -19, 12, -19, 0, -18, -5, -17, 5, -17, -10, -15, 10, -15, + 0, -14, -4, -13, 4, -13, 19, -13, -19, -12, -8, -11, -2, -11, 0, -11, + 2, -11, 8, -11, -15, -10, -4, -10, 4, -10, 15, -10, -6, -9, -1, -9, + 1, -9, 6, -9, -11, -8, -8, -8, -3, -8, 0, -8, 3, -8, 8, -8, + 11, -8, -5, -7, -2, -7, 0, -7, 2, -7, 5, -7, -22, -6, -9, -6, + -6, -6, -3, -6, -1, -6, 1, -6, 3, -6, 6, -6, 9, -6, 22, -6, + -17, -5, -7, -5, -4, -5, -2, -5, -1, -5, 0, -5, 1, -5, 2, -5, + 4, -5, 7, -5, 17, -5, -13, -4, -10, -4, -5, -4, -3, -4, -2, -4, + -1, -4, 0, -4, 1, -4, 2, -4, 3, -4, 5, -4, 10, -4, 13, -4, + -8, -3, -6, -3, -4, -3, -3, -3, -2, -3, -1, -3, 0, -3, 1, -3, + 2, -3, 3, -3, 4, -3, 6, -3, 8, -3, -11, -2, -7, -2, -5, -2, + -4, -2, -3, -2, -2, -2, -1, -2, 0, -2, 1, -2, 2, -2, 3, -2, + 4, -2, 5, -2, 7, -2, 11, -2, -9, -1, -6, -1, -5, -1, -4, -1, + -3, -1, -2, -1, -1, -1, 0, -1, 1, -1, 2, -1, 3, -1, 4, -1, + 5, -1, 6, -1, 9, -1, -23, 0, -18, 0, -14, 0, -11, 0, -7, 0, + -5, 0, -4, 0, -3, 0, -2, 0, -1, 0, 0, -23, 1, 0, 2, 0, + 3, 0, 4, 0, 5, 0, 7, 0, 11, 0, 14, 0, 18, 0, 23, 0, + -9, 1, -6, 1, -5, 1, -4, 1, -3, 1, -2, 1, -1, 1, 0, 1, + 1, 1, 2, 1, 3, 1, 4, 1, 5, 1, 6, 1, 9, 1, -11, 2, + -7, 2, -5, 2, -4, 2, -3, 2, -2, 2, -1, 2, 0, 2, 1, 2, + 2, 2, 3, 2, 4, 2, 5, 2, 7, 2, 11, 2, -8, 3, -6, 3, + -4, 3, -3, 3, -2, 3, -1, 3, 0, 3, 1, 3, 2, 3, 3, 3, + 4, 3, 6, 3, 8, 3, -13, 4, -10, 4, -5, 4, -3, 4, -2, 4, + -1, 4, 0, 4, 1, 4, 2, 4, 3, 4, 5, 4, 10, 4, 13, 4, + -17, 5, -7, 5, -4, 5, -2, 5, -1, 5, 0, 5, 1, 5, 2, 5, + 4, 5, 7, 5, 17, 5, -22, 6, -9, 6, -6, 6, -3, 6, -1, 6, + 1, 6, 3, 6, 6, 6, 9, 6, 22, 6, -5, 7, -2, 7, 0, 7, + 2, 7, 5, 7, -11, 8, -8, 8, -3, 8, 0, 8, 3, 8, 8, 8, + 11, 8, -6, 9, -1, 9, 1, 9, 6, 9, -15, 10, -4, 10, 4, 10, + 15, 10, -8, 11, -2, 11, 0, 11, 2, 11, 8, 11, 19, 12, -19, 13, + -4, 13, 4, 13, 0, 14, -10, 15, 10, 15, -5, 17, 5, 17, 0, 18, + -12, 19, 13, 19, -6, 22, 6, 22, 0, 23, + }; + + if (_tableLastPitch == pitch && _tableLastIndex == index) + return; +#ifdef DEBUG_CODEC37 + debug(7, "codec37::maketable(%d, %d) called", pitch, index); +#endif + _tableLastPitch = pitch; + _tableLastIndex = index; + index *= 255; + assert(index + 254 < (int)(sizeof(maketable_bytes) / 2)); + + for (int i = 0; i < 255; i++) { + int j = (i + index) << 1; // * 2 + _offsetTable[i] = maketable_bytes[j + 1] * pitch + maketable_bytes[j]; + } +} + +void Codec37Decoder::proc1(Blitter & dst, Chunck & src, int next_offs, int bw, int bh, int size) { + unsigned char * decoded = new unsigned char[size]; + int w = 0; + while(!src.eof()) { + int code = src.getByte(); + int length = (code >> 1) + 1; + if (code & 1) { + unsigned char val = src.getByte(); + while(length--) + decoded[w++] = val; + } else { + while(length--) { + decoded[w++] = src.getByte(); + } + } + } + assert(w == size); + w = 0; + // Now we have our stream ready... + for(int i = 0; i < size; i++) { + if(decoded[i] == 0xFF) { + dst.putBlock(decoded + i + 1); + i += 16; + } else { + dst.blockCopy(_offsetTable[decoded[i]] + next_offs); + } + if(++w == bw) { + w = 0; + dst.advance(0, 3); + } + } + delete []decoded; +} + +void Codec37Decoder::proc2(Blitter & dst, Chunck & src, int size) { // This is codec1 like... +#ifdef DEBUG_CODEC37_PROC2 + int decoded_size = 0; + int coded_size = 0; +#endif + do { + int code = src.getByte(); + int length = (code >> 1) + 1; + size -= length; +#ifdef DEBUG_CODEC37_PROC2 + decoded_size += length; + coded_size += 1 + ((code & 1) ? 1 : length); + + debug(7, "proc2() : code == %d : length == %d : decoded_size == %d : coded_size == %d : seek - header == %d : size == %d", + code, length, decoded_size, coded_size, src.tell() - 31, size + decoded_size); +#endif + if (code & 1) + dst.put(src.getChar(), length); + else + // while(length--) dst.put(src.get_char()); + dst.blit(src, length); + } while (size); +} + +void Codec37Decoder::proc3WithFDFE(Blitter & dst, Chunck & src, int next_offs, int bw, int bh) { + do { + int i = bw; + do { + int code = src.getByte(); + if (code == 0xFD) { +#ifdef USE_COLOR_CODE_FOR_BLOCK + dst.putBlock(expand(1)); +#else + dst.putBlock(expand(src.getByte())); +#endif + } else if (code == 0xFE) { +#ifdef USE_COLOR_CODE_FOR_BLOCK + dst.putBlock(expand(2)); +#else + dst.putBlock(expand(src.getByte()), expand(src.getByte()), expand(src.getByte()), expand(src.getByte())); +#endif + } else if (code == 0xFF) { +#ifdef USE_COLOR_CODE_FOR_BLOCK + dst.putBlock(expand(3)); +#else + dst.putBlock(src); +#endif + } else { +#ifdef USE_COLOR_CODE_FOR_BLOCK + dst.putBlock(expand(4)); +#else + dst.blockCopy(_offsetTable[code] + next_offs); // copy from an offset ! +#endif + } + } while (--i); + dst.advance(0, 3); // advance 3 lines + } while (--bh); +} + +void Codec37Decoder::proc3WithoutFDFE(Blitter & dst, Chunck & src, int next_offs, int bw, int bh) { + do { + int i = bw; + do { + int code = src.getByte(); + if (code == 0xFF) { +#ifdef USE_COLOR_CODE_FOR_BLOCK + dst.putBlock(expand(5)); +#else + dst.putBlock(src); +#endif + } else { +#ifdef USE_COLOR_CODE_FOR_BLOCK + dst.putBlock(expand(6)); +#else + dst.blockCopy(_offsetTable[code] + next_offs); // copy from an offset ! +#endif + } + } while (--i); + dst.advance(0, 3); // advance 3 lines + } while (--bh); +} + +void Codec37Decoder::proc4(Blitter & dst, Chunck & src, int next_offs, int bw, int bh) { +#ifdef DEBUG_CODEC37_PROC4 + int b_nb = 0; +#endif + do { + int i = bw; + do { + int code = src.getByte(); + if (code == 0xFD) { +#ifdef USE_COLOR_CODE_FOR_BLOCK + dst.putBlock(expand(7)); +#else + dst.putBlock(expand(src.getByte())); +#endif + } else if (code == 0xFE) { +#ifdef USE_COLOR_CODE_FOR_BLOCK + dst.putBlock(expand(8)); +#else + dst.putBlock(expand(src.getByte()), expand(src.getByte()), expand(src.getByte()), expand(src.getByte())); +#endif + } else if (code == 0xFF) { +#ifdef USE_COLOR_CODE_FOR_BLOCK + dst.putBlock(expand(9)); +#else + dst.putBlock(src); +#endif + } else if (code == 0x00) { + int length = src.getByte() + 1; + for (int l = 0; l < length; l++) { +#ifdef USE_COLOR_CODE_FOR_BLOCK + dst.putBlock(expand(10)); +#else + dst.blockCopy(next_offs); +#endif + i--; + if (i == 0) { + dst.advance(0, 3); // advance 3 lines + bh--; + i = bw; + } + } + if(bh == 0) return; + i++; + } else { +#ifdef USE_COLOR_CODE_FOR_BLOCK + dst.putBlock(expand(11)); +#else + dst.blockCopy(_offsetTable[code] + next_offs); // copy from an offset ! +#endif + } + } while (--i); + dst.advance(0, 3); // advance 3 lines + } while (--bh); +} + +bool Codec37Decoder::decode(Blitter & dst, Chunck & src) { + int width = getRect().width(); + int height = getRect().height(); + int bw = (width + 3) >> 2, bh = (height + 3) >> 2; + int pitch = bw << 2; +#ifdef DEBUG_CODEC37 + debug(7, "codec37::decode() : width == %d : height == %d : pitch == %d : _prevSeqNb == %d", + width, height, pitch, _prevSeqNb); +#endif + int code = src.getByte(); // 0 -> 1 (1) + int index = src.getByte(); // 1 -> 2 (1) + unsigned short seq_nb = src.getWord(); // 2 -> 4 (2) + unsigned int decoded_size = src.getDword(); // 4 -> 8 (4) +#ifdef DEBUG_CODEC37 + unsigned int coded_size = src.getDword(); // 8 -> 12 (4) +#else + src.seek(4); +#endif + unsigned int mask_flag = src.getDword(); // 12 -> 16 (4) +#ifdef DEBUG_CODEC37 + debug(7, "codec37::decode() : code == %d : index == %d : seq_nb == %d : decoded_size == %d : coded_size == %d : mask_flag == %d", + code, index, seq_nb, decoded_size, coded_size, mask_flag); +#endif + maketable(pitch, index); + if(code == 3 || code == 4 || code == 1) { + assert(seq_nb && _prevSeqNb + 1 == seq_nb); + if (seq_nb & 1 || !(mask_flag & 1)) _curtable ^= 1; + } + Blitter blit((char *)_deltaBufs[_curtable], Point(width, height), Rect(0, 0, width, height)); + switch(code) { + case 0: + memset(_deltaBuf, 0, _deltaBufs[_curtable] - _deltaBuf); + memset(_deltaBufs[_curtable] + decoded_size, 0, _deltaBuf + _deltaSize - _deltaBufs[_curtable] - decoded_size); + blit.blit(src, decoded_size); + break; + case 1: + proc1(blit, src, _deltaBufs[_curtable ^ 1] - _deltaBufs[_curtable], bw, bh, decoded_size); + break; + case 2: + memset(_deltaBuf, 0, _deltaBufs[_curtable] - _deltaBuf); + memset(_deltaBufs[_curtable] + decoded_size, 0, _deltaBuf + _deltaSize - _deltaBufs[_curtable] - decoded_size); + proc2(blit, src, decoded_size); + break; + case 3: + if(mask_flag & 1) + proc3WithFDFE(blit, src, _deltaBufs[_curtable ^ 1] - _deltaBufs[_curtable], bw, bh); + else + proc3WithoutFDFE(blit, src, _deltaBufs[_curtable ^ 1] - _deltaBufs[_curtable], bw, bh); + break; + case 4: + proc4(blit, src, _deltaBufs[_curtable ^ 1] - _deltaBufs[_curtable], bw, bh); + break; + default: +#ifdef DEBUG_CODEC37 + error("codec37::decode() received an invalid code : %d", code); +#endif + break; + } + dst.blit((char*)_deltaBufs[_curtable], width * height); + _prevSeqNb = seq_nb; + return true; +} + diff --git a/scumm/smush/codec37.h b/scumm/smush/codec37.h new file mode 100644 index 0000000000..65fd7c1195 --- /dev/null +++ b/scumm/smush/codec37.h @@ -0,0 +1,86 @@ +/* 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$ + * + */ + +#ifndef __CODEC37_H_ +#define __CODEC37_H_ + +#include "config.h" + +#ifdef DEBUG +# ifndef NO_DEBUG_CODEC37 +# define DEBUG_CODEC37 +# endif +#else +# ifdef DEBUG_CODEC37 +# error DEBUG_CODEC37 defined without DEBUG +# endif +#endif + +#ifdef DEBUG_CODEC37 +# ifndef NO_DEBUG_CODEC37_PROCS +# define DEBUG_CODEC37_PROC1 +# define DEBUG_CODEC37_PROC2 +# define DEBUG_CODEC37_PROC3 +# define DEBUG_CODEC37_PROC4 +# endif +#endif + +#include "decoder.h" + +/*! @brief ::decoder for codec 37. + +*/ + +#define DELTA_ADD 0x3E00 // what is this 0x3E00 ?? == 320*200/4 - 128 + // It looks like it is a safe-guarding protection from bugs., but maybe not... + +class Codec37Decoder : public Decoder { +private: + int _deltaSize; + unsigned char * _deltaBufs[2]; + unsigned char * _deltaBuf; + short * _offsetTable; + int _curtable; + unsigned short _prevSeqNb; + int _tableLastPitch; + int _tableLastIndex; + +public: + bool initSize(const Point &, const Rect &); + Codec37Decoder(); + void clean(); + virtual ~Codec37Decoder(); +protected: + static inline unsigned int expand(unsigned char b) { + unsigned int r = b | (b << 8); + return r | (r << 16); + } + void maketable(int, int); + void proc1(Blitter &, Chunck &, int, int, int, int); + void proc2(Blitter &, Chunck &, int); + void proc3WithFDFE(Blitter &, Chunck &, int, int, int); + void proc3WithoutFDFE(Blitter &, Chunck &, int, int, int); + void proc4(Blitter &, Chunck &, int, int, int); +public: + bool decode(Blitter &, Chunck &); +}; + +#endif diff --git a/scumm/smush/codec44.cpp b/scumm/smush/codec44.cpp new file mode 100644 index 0000000000..9795842e76 --- /dev/null +++ b/scumm/smush/codec44.cpp @@ -0,0 +1,71 @@ +/* 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 "codec44.h" +#include "chunck.h" +#include "blitter.h" + +bool Codec44Decoder::decode(Blitter & dst, Chunck & src) { + int size_line; + int num; + int w, width = getRect().width() + 1; + int h, height = getRect().height() + 1; + bool zero; +#ifdef DEBUG_CODEC44 + debug(7, "codec44 : %dx%d", width, height); +#endif + + for(h = 0; h < height - 1; h++) { + w = width; + size_line = src.getWord(); // size of compressed line ! +#ifdef DEBUG_CODEC44 + debug(7, "codec44 : h == %d, size_line == %d", h, size_line); +#endif + zero = true; + while(size_line > 1) { + num = src.getWord(); + size_line -= 2; + if(zero) { +#ifdef DEBUG_CODEC44 + debug(7, "codec44 : zeroing %d, entries", num); +#endif + if(w == num) + num--; + w -= num; + if(num) + dst.put(0, num); + } else { + num += 1; +#ifdef DEBUG_CODEC44 + debug(7, "codec44 : blitting %d, entries", num); +#endif + if(w == num) + num--; + w -= num; + dst.blit(src, num); + size_line -= num; + } + zero = !zero; + } + } + return true; +} diff --git a/scumm/smush/codec44.h b/scumm/smush/codec44.h new file mode 100644 index 0000000000..a7c39b02fb --- /dev/null +++ b/scumm/smush/codec44.h @@ -0,0 +1,47 @@ +/* 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$ + * + */ + +#ifndef __CODEC44_H_ +#define __CODEC44_H_ + +#include "config.h" + +#ifdef DEBUG +# ifndef NO_DEBUG_CODEC44 +# define DEBUG_CODEC44 +# endif +#else +# ifdef DEBUG_CODEC44 +# error DEBUG_CODEC44 defined without DEBUG +# endif +#endif + +#include "decoder.h" + +/*! @brief ::decoder for codec 21 and 44. + +*/ +class Codec44Decoder : public Decoder { +public: + bool decode(Blitter & dst, Chunck & src); +}; + +#endif diff --git a/scumm/smush/codec47.cpp b/scumm/smush/codec47.cpp new file mode 100644 index 0000000000..88038bb94d --- /dev/null +++ b/scumm/smush/codec47.cpp @@ -0,0 +1,51 @@ +/* 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 "codec47.h" +#include "chunck.h" +#include "blitter.h" + +DumpDecoder::~DumpDecoder() { +} + +bool DumpDecoder::decode(Blitter & dst, Chunck & src) { + int n = 0, i = 0; + int seq = src.getWord(); + int codec = src.getByte(); + int flags = src.getByte(); + int unknown[22]; + for(i = 0; i < 0; i++) { + unknown[i] = src.getByte(); + } + if(codec == 5 || codec == 1) { + do { + int code = src.getByte(); + int length = (code >> 1) + 1; + if (code & 1) + dst.put(src.getChar(), length); + else + dst.blit(src, length); + } while (!src.eof()); + } + + return true; +} diff --git a/scumm/smush/codec47.h b/scumm/smush/codec47.h new file mode 100644 index 0000000000..ecf34f7e70 --- /dev/null +++ b/scumm/smush/codec47.h @@ -0,0 +1,38 @@ +/* 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$ + * + */ + +#ifndef __CODEC_47_H_ +#define __CODEC_47_H_ + +#include "config.h" + +#include "decoder.h" + +/*! @brief ::decoder for debugging purpose. + +*/ +class DumpDecoder : public Decoder { +public: + virtual ~DumpDecoder(); + bool decode(Blitter &, Chunck &); +}; + +#endif diff --git a/scumm/smush/color.cpp b/scumm/smush/color.cpp new file mode 100644 index 0000000000..e88785ccbd --- /dev/null +++ b/scumm/smush/color.cpp @@ -0,0 +1,69 @@ +/* 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 "color.h" + +Color::Color() : _r(0), _g(0), _b(0) { +} + +Color::Color(value_type r, value_type g, value_type b) : _r(r), _g(g), _b(b) { +} + +Color::Color(const Color & c) : _r(c._r), _g(c._g), _b(c._b) { +} + +Color & Color::operator=(const Color & c) { + _r = c._r; + _g = c._g; + _b = c._b; + return *this; +} + +Color::~Color() { +} + +Color::value_type Color::red() const { + return _r; +} + +Color::value_type Color::green() const { + return _g; +} + +Color::value_type Color::blue() const { + return _b; +} + +void Color::delta(short * ptr) { + // This is a very specific method for XPALs. + int t; +#define UPDATE_COLOR(c, inc) (((int)((c)) << 7) + (c) + (inc)) >> 7 +#define CHECK_BOUNDS(c) (((c) > 255) ? 255 : (((c) < 0) ? 0 : (c))) + t = UPDATE_COLOR(_r, ptr[0]); + _r = CHECK_BOUNDS(t); + t = UPDATE_COLOR(_g, ptr[1]); + _g = CHECK_BOUNDS(t); + t = UPDATE_COLOR(_b, ptr[2]); + _b = CHECK_BOUNDS(t); +#undef UPDATE_COLOR +#undef CHECK_BOUNDS +} diff --git a/scumm/smush/color.h b/scumm/smush/color.h new file mode 100644 index 0000000000..00c8978162 --- /dev/null +++ b/scumm/smush/color.h @@ -0,0 +1,57 @@ +/* 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$ + * + */ + +#ifndef __COLOR_H_ +#define __COLOR_H_ + +#include "config.h" + +/*! @brief simple class for handling a color. + + This small class is an helper for colors. +*/ +class Color { +public: + typedef unsigned char value_type; //!< The type of the color components. +private: + value_type _r; //!< The red component. + value_type _g; //!< The green component. + value_type _b; //!< The blue component. +public: + Color(); + Color(value_type, value_type, value_type); + Color(const Color &); + Color & operator=(const Color &); + virtual ~Color(); + value_type red() const; + value_type green() const; + value_type blue() const; + /*! @brief handle delta palette modification + + This method is used specifically by player::handleDeltaPalette(). + It updates the color component using delta values given as short. + + @param ptr pointer to a table of 3 shorts that contain delta values to use. + */ + void delta(short * ptr); +}; + +#endif diff --git a/scumm/smush/config.h b/scumm/smush/config.h new file mode 100644 index 0000000000..4734413f2e --- /dev/null +++ b/scumm/smush/config.h @@ -0,0 +1,42 @@ +/* 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$ + * + */ + +#ifndef __CONFIG_H_ +#define __CONFIG_H_ + +#include <stdafx.h> +#include <scumm.h> + +#ifndef NDEBUG +#define DEBUG +#endif + +//~ #define NO_DEBUG_MIXER +//~ #define NO_DEBUG_CHANNEL +//~ #define NO_DEBUG_CLIPPER +#define NO_DEBUG_CODEC1 +#define NO_DEBUG_CODEC37 +#define NO_DEBUG_CODEC44 +//~ #define NO_DEBUG_WIN32 +//~ #define NO_DEBUG_FONT_RENDERER + + +#endif diff --git a/scumm/smush/decoder.h b/scumm/smush/decoder.h new file mode 100644 index 0000000000..a216d2b6b1 --- /dev/null +++ b/scumm/smush/decoder.h @@ -0,0 +1,51 @@ +/* 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$ + * + */ + +#ifndef __DECODER_H_ +#define __DECODER_H_ + +#include "config.h" + +#include "rect.h" + +class Blitter; +class Chunck; + +/*! @brief base class for codec decompression. + + This class provides an interface for codec decompression. + +*/ +class Decoder { +private: + Rect _r; //!< current size of the frame object to decode + Point _p; //!< position of the frame object to decode +protected: + const Rect & getRect() const{ return _r; } + const Point & getSize() const { return _p; } +public: + Decoder() {}; + virtual ~Decoder() {}; + virtual bool initSize(const Point & p, const Rect & r) { _p = p; _r = r; return true; }; + virtual bool decode(Blitter &, Chunck &) = 0; +}; + +#endif diff --git a/scumm/smush/frenderer.cpp b/scumm/smush/frenderer.cpp new file mode 100644 index 0000000000..7f3f08cae8 --- /dev/null +++ b/scumm/smush/frenderer.cpp @@ -0,0 +1,232 @@ +/* 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 "frenderer.h" +#include "rect.h" + +#include <assert.h> +#include <string.h> // for memcpy, strcat, strdup + +#ifndef max +#define max(x, y) ((x) > (y) ? (x) : (y)) +#endif + +FontRenderer::FontRenderer(bool use_original_colors) : _nbChars(0), _color(-1), _original(use_original_colors) { +} + +FontRenderer::~FontRenderer() { + for(int i = 0; i < _nbChars; i++) { + if(_chars[i].chr) delete []_chars[i].chr; + } +} + +void FontRenderer::save(int frame) { + _chars[_nbChars].width = getWidth(); + _chars[_nbChars].height = getHeight(); + int size = getWidth() * getHeight(); + _chars[_nbChars].chr = new char[size]; + memcpy(_chars[_nbChars].chr, data(), size); + _nbChars++; +} + +int FontRenderer::charWidth(int v) const { + if(v < 0) v = 256 + v; + if(v < 0 || v >= _nbChars) error("invalid character in FontRenderer::charWidth : %d (%d)", v, _nbChars); + return _chars[v].width; +} + +int FontRenderer::charHeight(int v) const { + if(v < 0) v = 256 + v; + if(v < 0 || v >= _nbChars) error("invalid character in FontRenderer::charHeight : %d (%d)", v, _nbChars); + return _chars[v].height; +} + +int FontRenderer::stringWidth(const char * str) const { + int ret = 0; + + while(*str) { + ret += charWidth(*str++); + } + + return ret; +} + +int FontRenderer::stringHeight(const char * str) const { + int ret = 0; + + for(int i = 0; str[i] != 0; i++) { + int h = charHeight(str[i]); + ret = max(ret, h); + } + + return ret; +} + +int FontRenderer::drawChar(char * buffer, const Point & size, int x, int y, int chr) const { + int w = _chars[chr].width; + int h = _chars[chr].height; + char * src = _chars[chr].chr; + char * dst = buffer + size.getX() * y + x; + + if(_original) { + for(int j = 0; j < h; j++) { + for(int i = 0; i < w; i++) { + int value = *src++; + if(value) dst[i] = value; + } + dst += size.getX(); + } + } else { + int color = (_color != -1) ? _color : 1; + for(int j = 0; j < h; j++) { + for(int i = 0; i < w; i++) { + int value = *src++; + if(value == 1) { + dst[i] = color; + } else if(value) { + dst[i] = 0; + } + } + dst += size.getX(); + } + } + return w; +} + +static char * * split(const char * str, char sep) { + char * * ret = new char *[32]; + int n = 0; + const char * i = str, * j = strchr(i, sep); + + while(j != NULL) { + assert(n < 30); + ret[n] = new char[j - i + 1]; + memcpy(ret[n], i, j - i); + ret[n++][j - i] = 0; + i = j+1; + j = strchr(i, sep); + } + + ret[n] = new char[strlen(i) + 1]; + memcpy(ret[n], i, strlen(i)); + ret[n++][strlen(i)] = 0; + ret[n] = 0; + return ret; +} + +void FontRenderer::drawSubstring(const unsigned char * str, char * buffer, const Point & size, int x, int y) const { + for(int i = 0; str[i] != 0; i++) + x += drawChar(buffer, size, x, y, str[i]); +} + +bool FontRenderer::drawStringAbsolute(const char * str, char * buffer, const Point & size, int x, int y) const { + debug(9, "FontRenderer::drawStringAbsolute(%s, %d, %d)", str, x, y); + while(str) { + char line[256]; + char * pos = strchr(str, '\n'); + if(pos) { + memcpy(line, str, pos - str - 1); + line[pos - str - 1] = 0; + str = pos + 1; + } else { + strcpy(line, str); + str = 0; + } + drawSubstring((const unsigned char *)line, buffer, size, x, y); + y += stringHeight(line); + } + return true; +} + +bool FontRenderer::drawStringCentered(const char * str, char * buffer, const Point & size, int y, int xmin, int width, int offset) const { + debug(9, "FontRenderer::drawStringCentered(%s, %d, %d)", str, xmin, y); + assert(strchr(str, '\n') == 0); + char * * words = split(str, ' '); + int nb_sub = 0; + + while(words[nb_sub]) nb_sub++; + + int * sizes = new int[nb_sub]; + int i = 0, max_width = 0, height = 0, nb_subs = 0; + + for(i = 0; i < nb_sub; i++) + sizes[i] = stringWidth(words[i]); + + char * * substrings = new char *[nb_sub]; + int * substr_widths = new int[nb_sub]; + int space_width = charWidth(' '); + + while(i < nb_sub) { + int substr_width = sizes[i]; + char * substr = new char[1000]; + strcpy(substr, words[i]); + int j = i + 1; + + while(j < nb_sub && (substr_width + space_width + sizes[j]) < width) { + substr_width += sizes[j++] + space_width; + } + + for(int k = i + 1; k < j; k++) { + strcat(substr, " "); + strcat(substr, words[k]); + } + + substrings[nb_subs] = substr; + substr_widths[nb_subs++] = substr_width; + if(substr_width > max_width) + max_width = substr_width; + i = j; + height += stringHeight(substr); + } + + delete []sizes; + for(i = 0; i < nb_sub; i++) { + delete []words[i]; + } + delete []words; + + max_width = (max_width + 1) >> 1; + // we have a box from 0 -> max_width + // we want a box from (xmin + offset) - max_width / 2, (xmin + offset) + max_width / 2 + int x = xmin + width / 2; + x += offset - size.getX() / 2; + + if(x < max_width) x = max_width; + if(x + max_width > size.getX()) { + x = size.getX() - max_width; + } + + if(y + height > size.getY()) { + y = size.getY() - height; + } + + for(i = 0; i < nb_subs; i++) { + int substr_width = substr_widths[i]; + drawSubstring((const unsigned char *)substrings[i], buffer, size, x - substr_width / 2, y); + y += stringHeight(substrings[i]); + delete []substrings[i]; + } + + delete []substr_widths; + delete []substrings; + return true; +} diff --git a/scumm/smush/frenderer.h b/scumm/smush/frenderer.h new file mode 100644 index 0000000000..2ed9cd98cc --- /dev/null +++ b/scumm/smush/frenderer.h @@ -0,0 +1,164 @@ +/* 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$ + * + */ + +#ifndef __FRENDERER_H_ +#define __FRENDERER_H_ + +#include "config.h" + +#ifdef DEBUG +# ifndef NO_DEBUG_FONT_RENDERER +# define DEBUG_FONT_RENDERER +# endif +#else +# ifdef DEBUG_FONT_RENDERER +# error DEBUG_FONT_RENDERER defined without DEBUG +# endif +#endif + +#include "brenderer.h" +#include "rect.h" +#include "blitter.h" + +/*! @brief ::renderer implementation specifically designed for font files. + + This class is a valid ::renderer implementation. The frames are kept in memory, as bitmap representing characters, so that + they can be rendered again in another frame as strings. + + This class also contains some functions useful for printing strings. This is used to show subtitles and more generally texts + in animations. + + @todo update the mehod to use the ::blitter class, instead of direct pointers. +*/ +class FontRenderer : public BaseRenderer { +private: + int _nbChars; //!< The number of frames in the font + int _color; //!< A color parameter used for font printing. + bool _original; //!< flag for color selection + struct { + int width; + int height; + char * chr; + } _chars[256]; //!< array that contains the size of the different frames (i.e. characters) of the font. +public: + /*! @brief font_renderer constructor + + @param use_original_colors flag to indicate if the font use it's own color, or if the base color are set at runtime. + */ + FontRenderer(bool use_original_colors = false); + virtual ~FontRenderer(); + virtual bool wait(int ms) { return true; }; +protected: + virtual void save(int frame = -1); + /*! @brief get the width of a character. + + @param c the character we want the width from. + + @return the width of the character + */ + int charWidth(int c) const; + /*! @brief get the width of a string. + + @param str the string we want the width from. + + @return the complete width of the string + */ + int stringWidth(const char * str) const; + /*! @brief get the height of a character. + + @param c the character we want the height from. + + @return the height of the character + */ + int charHeight(int c) const; + /*! @brief get the height of a string. + + @param str the string we want the height from. + + @return the complete height of the string + */ + int stringHeight(const char * str) const; + /*! @brief draw a character in the given frame buffer. + + @param buffer the frame buffer to draw into. + @param size the size of the frame buffer. + @param x the horizontal position of the topleft corner of the character. + @param y the vertical position of the topleft corner of the character. + @param c the character to draw. + + @bug This method does not clip. This is not really a bug, as it should always be correctly called, but some asserts would be welcome. + + @return the width of the character + */ + int drawChar(char * buffer, const Point & size, int x, int y, int c) const; + /*! @brief draw a string in the given frame buffer. + + @param str the string to draw. + @param buffer the frame buffer to draw into. + @param size the size of the frame buffer. + @param x the horizontal position of the topleft corner of the string. + @param y the vertical position of the topleft corner of the string. + + @bug This method does not clip. This is not really a bug, as it should always be correctly called, but some asserts would be welcome. + */ + void drawSubstring(const unsigned char * str, char * buffer, const Point & size, int x, int y) const; +public: + /*! @brief change the programmable color of the font. + + @param c the new color to use. + + @return \c true if everything went fine, \c false otherwise + */ + bool setColor(int c) { _color = c; return true; } + /*! @brief draw a centered and possibly using multiple lines string. + + This method performs calculation of the string size before choosing where to draw it. + As I still not have figured out exactly what is the meaning of the fields in the TRES chunck, + the real meaning of the parameters can be quite difficult to understand. + + @remark The current implementation is incorrect in the sense that it does not conform to the original game. + @todo rewrite and rethink this to better match the original implementation. + + @param str the string to draw. + @param buffer the frame buffer to draw into. + @param size the size of the frame buffer. + @param y the vertical position of the topleft corner of the string. This position may be changed if it is too low to be correctly drawn. + @param xmin the minimum horizontal position of the topleft corner of the string. + @param width the maximum width of the string. If the string is too long, it will wrap. + @param offset offset to give to the horizontal position. + + @return \c true if everything went fine, \c false otherwise + */ + bool drawStringCentered(const char * str, char * buffer, const Point & size, int y, int xmin, int width, int offset) const; + /*! @brief draw a string at an absolute position. + + @param str the string to draw. + @param buffer the frame buffer to draw into. + @param size the size of the frame buffer. + @param x the horizontal position of the topleft corner of the string. + @param y the vertical position of the topleft corner of the string. This position may be changed if it is too low to be correctly drawn. + + @return \c true if everything went fine, \c false otherwise + */ + bool drawStringAbsolute(const char * str, char * buffer, const Point & size, int x, int y) const; +}; + +#endif diff --git a/scumm/smush/imuse_channel.cpp b/scumm/smush/imuse_channel.cpp new file mode 100644 index 0000000000..f082407abd --- /dev/null +++ b/scumm/smush/imuse_channel.cpp @@ -0,0 +1,329 @@ +/* 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 "channel.h" +#include "chunck.h" +#include "chunck_type.h" + +#include <assert.h> +#include <string.h> // for memcpy.h +#ifndef min +#define min(x, y) ((x) > (y) ? (y) : (x)) +#endif + +ImuseChannel::ImuseChannel(int track, int freq) : + _track(track), + _tbuffer(0), + _tbufferSize(0), + _sbuffer(0), + _sbufferSize(0), + _frequency(freq), + _dataSize(-1), + _inData(false) { +} + +ImuseChannel::~ImuseChannel() { + if(_tbuffer) { + delete []_tbuffer; + } + if(_sbuffer) { + warning("_sbuffer should be 0 !!!"); + delete []_sbuffer; + } +} + +bool ImuseChannel::isTerminated() const { + return (_dataSize <= 0 && _sbuffer == 0); +} + +bool ImuseChannel::setParameters(int nbframes, int size, int unk1, int unk2) { + return true; +} + +bool ImuseChannel::checkParameters(int index, int nbframes, int size, int unk1, int unk2) { + return true; +} + +bool ImuseChannel::appendData(Chunck & b, int size) { + if(_dataSize == -1) { // First call + assert(size > 8); + Chunck::type imus_type = b.getDword(); imus_type = TO_BE_32(imus_type); + unsigned int imus_size = b.getDword(); imus_size = TO_BE_32(imus_size); + if(imus_type != TYPE_iMUS) error("Invalid CHUNCK for imuse_channel"); + size -= 8; + _tbufferSize = size; + assert(_tbufferSize); + _tbuffer = new unsigned char[_tbufferSize]; + if(!_tbuffer) error("imuse_channel failed to allocate memory"); + b.read(_tbuffer, size); + _dataSize = -2; // even if _in_data does not get set, this won't be called again + } else { + if(_tbuffer) { // remaining from last call + unsigned char * old = _tbuffer; + int new_size = size + _tbufferSize; + _tbuffer = new unsigned char[new_size]; + if(!_tbuffer) error("imuse_channel failed to allocate memory"); + memcpy(_tbuffer, old, _tbufferSize); + delete []old; + b.read(_tbuffer + _tbufferSize, size); + _tbufferSize += size; + } else { + _tbufferSize = size; + _tbuffer = new unsigned char[_tbufferSize]; + if(!_tbuffer) error("imuse_channel failed to allocate memory"); + b.read(_tbuffer, size); + } + } + return processBuffer(); +} + +bool ImuseChannel::handleFormat(Chunck & src) { + if(src.getSize() != 20) error("invalid size for FRMT chunck"); + unsigned imuse_start = src.getDword(); + imuse_start = TO_BE_32(imuse_start); + src.seek(4); + _bitsize = src.getDword(); + _bitsize = TO_BE_32(_bitsize); + _rate = src.getDword(); + _rate = TO_BE_32(_rate); + _channels = src.getDword(); + _channels = TO_BE_32(_channels); + assert(_channels == 1 || _channels == 2); + return true; +} + +bool ImuseChannel::handleText(Chunck & src) { + return true; +} + +bool ImuseChannel::handleRegion(Chunck & src) { + if(src.getSize() != 8) error("invalid size for REGN chunck"); + return true; +} + +bool ImuseChannel::handleStop(Chunck & src) { + if(src.getSize() != 4) error("invalid size for STOP chunck"); + return true; +} + +bool ImuseChannel::handleMap(Chunck & map) { + while(!map.eof()) { + Chunck * sub = map.subBlock(); + switch(sub->getType()) { + case TYPE_FRMT: + handleFormat(*sub); + break; + case TYPE_TEXT: + handleText(*sub); + break; + case TYPE_REGN: + handleRegion(*sub); + break; + case TYPE_STOP: + handleStop(*sub); + break; + default: + error("Unknown iMUS subchunck found : %s, %d", Chunck::ChunckString(sub->getType()), sub->getSize()); + } + delete sub; + } + return true; +} + +void ImuseChannel::decode() { + int remaining_size = _sbufferSize % 3; + if(remaining_size) { + _srbufferSize -= remaining_size; + assert(_inData); + if(_tbuffer == 0) { + _tbuffer = new unsigned char[remaining_size]; + memcpy(_tbuffer, _sbuffer + _sbufferSize - remaining_size, remaining_size); + _tbufferSize = remaining_size; + _sbufferSize -= remaining_size; + } else { + warning("impossible ! : %p, %d, %d, %p(%d), %p(%d, %d)", + this, _dataSize, _inData, _tbuffer, _tbufferSize, _sbuffer, _sbufferSize, _srbufferSize); + unsigned char * old = _tbuffer; + int new_size = remaining_size + _tbufferSize; + _tbuffer = new unsigned char[new_size]; + if(!_tbuffer) error("imuse_channel failed to allocate memory"); + memcpy(_tbuffer, old, _tbufferSize); + delete []old; + memcpy(_tbuffer + _tbufferSize, _sbuffer + _sbufferSize - remaining_size, remaining_size); + _tbufferSize += remaining_size; + } + } + int loop_size = _sbufferSize / 3; + int new_size = loop_size * 2; + short * keep, * decoded; + keep = decoded = new short[new_size]; + assert(keep); + unsigned char * source = _sbuffer; + while(loop_size--) { + int v1 = *source++; + int v2 = *source++; + int v3 = *source++; + int value = (((v2 & 0x0f) << 12) | (v1 << 4)) - 0x8000; + *decoded++ = (short)value; + value = (((v2 & 0xf0) << 8) | (v3 << 4)) - 0x8000; + *decoded++ = (short)value; + } + delete []_sbuffer; + _sbuffer = (unsigned char*)keep; + _sbufferSize = new_size * sizeof(short); +} + +bool ImuseChannel::handleSubTags(int & offset) { + int available_size = _tbufferSize - offset; + if(available_size >= 8) { + Chunck::type type = READ_BE_UINT32(_tbuffer + offset); + unsigned int size = READ_BE_UINT32(_tbuffer + offset + 4); + switch(type) { + case TYPE_MAP_: + _inData = false; + if(available_size >= (size + 8)) { + ContChunck c((char*)_tbuffer + offset); + handleMap(c); + } + break; + case TYPE_DATA: // Sound data !!! + _inData = true; + _dataSize = size; + offset += 8; + { + int reqsize = 1; + if(_channels == 2) reqsize *= 2; + if(_bitsize == 16) reqsize *= 2; + else if(_bitsize == 12) { + if(reqsize > 1) + reqsize = reqsize * 3 / 2; + else reqsize = 3; + } + if((size % reqsize) != 0) { + warning("Invalid iMUS sound data size : (%d %% %d) != 0, correcting...", size, reqsize); + size += 3 - (size % reqsize); + } + } + return false; + default: + error("unknown chunck in iMUS track : %s ", Chunck::ChunckString(type)); + } + offset += size + 8; + return true; + } + return false; +} + +bool ImuseChannel::processBuffer() { + // see comments in saud_channel::processBuffer for an explanation + assert(_tbuffer != 0); + assert(_tbufferSize != 0); + assert(_sbuffer == 0); + assert(_sbufferSize == 0); + + if(_inData) { + if(_dataSize < _tbufferSize) { + int offset= _dataSize; + while(handleSubTags(offset)); + _sbufferSize = _dataSize; + _sbuffer = _tbuffer; + if(offset < _tbufferSize) { // there is still some unprocessed data + int new_size = _tbufferSize - offset; + _tbuffer = new unsigned char[new_size]; + if(!_tbuffer) error("imuse_channel failed to allocate memory"); + memcpy(_tbuffer, _sbuffer + offset, new_size); + _tbufferSize = new_size; + } else { + _tbuffer = 0; + _tbufferSize = 0; + } + if(_sbufferSize == 0) { + // this never happened yet, but who knows + delete []_sbuffer; + _sbuffer = 0; + } + } else { + // easy, swap the buffer + _sbufferSize = _tbufferSize; + _sbuffer = _tbuffer; + _tbufferSize = 0; + _tbuffer = 0; + } + } else { + int offset = 0; + while(handleSubTags(offset)); + if(_inData) { + //~ unsigned char * old = _tbuffer; + _sbufferSize = _tbufferSize - offset; + assert(_sbufferSize); + _sbuffer = new unsigned char[_sbufferSize]; + if(!_sbuffer) error("imuse_channel failed to allocate memory"); + memcpy(_sbuffer, _tbuffer + offset, _sbufferSize); + delete []_tbuffer; + _tbuffer = 0; + _tbufferSize = 0; + } else { + if(offset) { // maybe I should assert() this to avoid a lock... + unsigned char * old = _tbuffer; + int new_size = _tbufferSize - offset; + _tbuffer = new unsigned char[new_size]; + if(!_tbuffer) error("imuse_channel failed to allocate memory"); + memcpy(_tbuffer, old + offset, new_size); + _tbufferSize = new_size; + delete []old; + } + } + } + _srbufferSize = _sbufferSize; + if(_sbuffer && _bitsize == 12) decode(); + return true; +} + +int ImuseChannel::availableSoundData(void) const { + int ret = _sbufferSize; + if(_channels == 2) ret /= 2; + if(_bitsize > 8) ret /= 2; + return ret; +} + +void ImuseChannel::getSoundData(short * snd, int size) { + if(_dataSize <= 0 || _bitsize <= 8) error("invalid call to imuse_channel::read_sound_data()"); + if(_channels == 2) size *= 2; + for(int i = 0; i < size; i++) + snd[i] = READ_BE_UINT16(_sbuffer + 2 * i); + delete []_sbuffer; + assert(_sbufferSize == 2 * size); + _sbuffer = 0; + _sbufferSize = 0; + _dataSize -= _srbufferSize; +} + +void ImuseChannel::getSoundData(char * snd, int size) { + if(_dataSize <= 0 || _bitsize > 8) error("invalid call to imuse_channel::read_sound_data()"); + if(_channels == 2) size *= 2; + for(int i = 0; i < size; i++) + snd[i] = _sbuffer[i]; + delete []_sbuffer; + _sbuffer = 0; + _sbufferSize = 0; + _dataSize -= _srbufferSize; +} diff --git a/scumm/smush/mixer.h b/scumm/smush/mixer.h new file mode 100644 index 0000000000..30858b6c29 --- /dev/null +++ b/scumm/smush/mixer.h @@ -0,0 +1,58 @@ +/* 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$ + * + */ + +#ifndef __MIXER_H_ +#define __MIXER_H_ + +#include "config.h" + +#ifdef DEBUG +# ifndef NO_DEBUG_MIXER +# define DEBUG_MIXER +# endif +#else +# ifdef DEBUG_MIXER +# error DEBUG_MIXER defined without DEBUG +# endif +#endif + +class _Channel; + +class SoundRenderer; + +/*! @brief The class for the player's sound mixer + + This class is used for sound mixing. + It contains a list of current track and request them to mix. + It then sends the mixed sound samples to the sound renderer. + +*/ +class Mixer { +public: + virtual ~Mixer() {}; + virtual bool init() = 0; + virtual _Channel * findChannel(int track) = 0; + virtual bool addChannel(_Channel * c) = 0; + virtual bool handleFrame() = 0; + virtual bool stop() = 0; +}; + +#endif diff --git a/scumm/smush/palette.cpp b/scumm/smush/palette.cpp new file mode 100644 index 0000000000..033c6b2563 --- /dev/null +++ b/scumm/smush/palette.cpp @@ -0,0 +1,56 @@ +/* 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 "palette.h" +#include "assert.h" + +Palette::Palette() { +} + +Palette::Palette(unsigned char * ptr) { + for(int i = 0; i < 256; i++) { + _colors[i] = Color(ptr[3 * i + 0], ptr[3 * i + 1], ptr[3 * i + 2]); + } +} + +Palette::Palette(const Palette & p) { + for(int i = 0; i < 256; i++) { + _colors[i] = p._colors[i]; + } +} + +const Color & Palette::operator[](int a) const { + assert(a >= 0 && a < 256); + return _colors[a]; +} + +Color & Palette::operator[](int a) { + assert(a >= 0 && a < 256); + return _colors[a]; +} + +Palette & Palette::operator=(const Palette & p) { + for(int i = 0; i < 256; i++) { + _colors[i] = p._colors[i]; + } + return *this; +} diff --git a/scumm/smush/palette.h b/scumm/smush/palette.h new file mode 100644 index 0000000000..4cc8491219 --- /dev/null +++ b/scumm/smush/palette.h @@ -0,0 +1,45 @@ +/* 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$ + * + */ + +#ifndef __PALETTE_H_ +#define __PALETTE_H_ + +#include "config.h" + +#include "color.h" + +/*! @brief simple class for handling a palette. + + This small class is an helper for palettes. +*/ +class Palette { +private: + Color _colors[256]; +public: + Palette(); + Palette(unsigned char *); + Palette(const Palette &); + const Color & operator[](int) const; + Color & operator[](int); + Palette & operator=(const Palette &); +}; + +#endif diff --git a/scumm/smush/player.cpp b/scumm/smush/player.cpp new file mode 100644 index 0000000000..5362f5be9a --- /dev/null +++ b/scumm/smush/player.cpp @@ -0,0 +1,760 @@ +/* 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 "player.h" + +#include "renderer.h" +#include "channel.h" +#include "chunck_type.h" +#include "rect.h" +#include "blitter.h" + +#include <assert.h> +#include <stdlib.h> // for atoi +#include <stdio.h> // for FILE, fopen, fclose, fread, fseek, ftell +#include <string.h> // for strchr, strrchr +#include <ctype.h> // for isdigit + +#ifndef max +#define max(x, y) ((x) > (y) ? (x) : (y)) +#endif + +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 int MAX_STRINGS = 200; + +class StringResource { +private: + struct { + int id; + char * string; + } _strings[MAX_STRINGS]; + int _nbStrings; + int _lastId; + char * _lastString; +public: + StringResource() : _nbStrings(0), _lastId(-1) {}; + ~StringResource() { + for(int 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, int 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; + int id = atoi(idstring); + //~ assert(id != LONG_MIN && id != 0 && id != LONG_MAX); + 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; +#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(int 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 { + int 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 Chunck & b, Chunck::type type_expected, unsigned int min_size) { + if(type_expected != b.getType()) { + error("chunck type is different from expected : %d != %d", b.getType(), type_expected); + } + if(min_size > b.getSize()) { + error( "chunck size is inferior than minimum required size : %d < %d", b.getSize(), min_size); + } +} + +void SmushPlayer::handleSoundBuffer(int track_id, int index, int max_frames, int flags, int vol, int bal, Chunck & b, int 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(Chunck & b) { + checkBlock(b, TYPE_PSAD); + debug(6, "SmushPlayer::handleSoundFrame()"); + if(!_outputSound) return; + int track_id = b.getWord(); + int index = b.getWord(); + int max_frames = b.getWord(); + int flags = b.getWord(); + int vol = b.getByte(); + int 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 + int size = b.getSize() - 10; + handleSoundBuffer(track_id, index, max_frames, flags, vol, bal, b, size); +} + +void SmushPlayer::handleSkip(Chunck & b) { + checkBlock(b, TYPE_SKIP, 4); + int code = b.getDword(); + debug(6, "SmushPlayer::handleSkip(%d)", code); + if(code >= 0 && code < 37) + _skipNext =_skips[code]; + else + _skipNext =true; +} + +void SmushPlayer::handleStore(Chunck & b) { + checkBlock(b, TYPE_STOR, 4); + debug(6, "SmushPlayer::handleStore()"); +} + +void SmushPlayer::handleFetch(Chunck & b) { + checkBlock(b, TYPE_FTCH, 6); + debug(6, "SmushPlayer::handleFetch()"); +} + +void SmushPlayer::handleImuseBuffer(int track_id, int index, int nbframes, int size, int unk1, int unk2, Chunck & b, int 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(Chunck & b, int flags, int unknown, int track_id) { + assert(flags == 46 && unknown == 0); + int unknown2 = b.getWord(); + track_id |= unknown2 << 16; + int index = b.getWord(); + int nbframes = b.getWord(); + int size = b.getDword(); + int bsize = b.getSize() - 18; + handleImuseBuffer(track_id, index, nbframes, size, unknown, unknown2, b, bsize); +} + +void SmushPlayer::handleImuseAction(Chunck & b) { + checkBlock(b, TYPE_IACT, 8); + debug(6, "SmushPlayer::handleImuseAction()"); + if(!_outputSound) return; + int code = b.getWord(); + int flags = b.getWord(); + int unknown = b.getShort(); + int 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(Chunck & b) { + checkBlock(b, TYPE_TRES, 18); + int pos_x = b.getShort(); + int pos_y = b.getShort(); + int flags = b.getShort(); + int left = b.getShort(); + int top = b.getShort(); + int width = b.getShort(); + int height = b.getShort(); + int unk2 = b.getWord(); + int 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]; + int color = 15; + while(*str == '/') str++; // For Full Throttle text resources + while(str[0] == '^') { + switch(str[1]) { + case 'f': + { +#if 0 + // This cause trouble if the next character is a digit. + int id = atoi(str+2); +#else + // assume ASCII like character set... + int id = str[3] - '0'; +#endif + str += 4; + fr = _fr[id]; + } break; + case 'c': + { + //~ int id = atoi(str+2); + 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, Chunck & in) { + unsigned char buffer[768]; + in.read(buffer, 768); + out = Palette(buffer); +} + +void SmushPlayer::handleDeltaPalette(Chunck & b) { + checkBlock(b, TYPE_XPAL); + debug(6, "SmushPlayer::handleDeltaPalette()"); + if(b.getSize() == 768 * 3 + 4) { + int unk1, num; + unk1 = b.getWord(); + num = b.getWord(); + for(int i = 0; i < 768; i++) { + _deltaPal[i] = b.getWord(); + } + readPalette(_pal, b); + updatePalette(); + } else if(b.getSize() == 6) { + int unk1, num, unk2; + unk1 = b.getWord(); + num = b.getWord(); + unk2 = b.getWord(); + for(int i = 0; i < 256; i++) { + _pal[i].delta(_deltaPal + 3 * i); + } + updatePalette(); + } else { + error("wrong size for DeltaPalette"); + } +} + +void SmushPlayer::handleNewPalette(Chunck & b) { + checkBlock(b, TYPE_NPAL, 768); + debug(6, "SmushPlayer::handleNewPalette()"); + readPalette(_pal, b); + updatePalette(); +} + +void SmushPlayer::decodeCodec(Chunck & b, const Rect & r, Decoder & codec) { + assert(_curBuffer); + Blitter blit(_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(Chunck & b) { + checkBlock(b, TYPE_FOBJ, 14); + if(_skipNext) { + _skipNext = false; + return; + } + int codec = b.getWord(); + debug(6, "SmushPlayer::handleFrameObject(%d)", codec); + unsigned short left = b.getWord(); + unsigned short top = b.getWord(); + unsigned short width = b.getWord(); + unsigned short height = b.getWord(); + Rect r(left, top, left + width, top + height); + unsigned short 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", (int)codec); + } +} + +void SmushPlayer::handleFrame(Chunck & b) { + checkBlock(b, TYPE_FRME); + debug(6, "SmushPlayer::handleFrame(%d)", _frame); + _alreadyInit = false; + _skipNext = false; + + while(!b.eof()) { + Chunck * 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 subchunck found : %s, %d", Chunck::ChunckString(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(Chunck & b) { + checkBlock(b, TYPE_AHDR, 774); + debug(6, "SmushPlayer::handleAnimHeader()"); + _version = b.getWord(); + _nbframes = b.getWord(); + int 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(); + int unknown2 = b.getDword(); + _soundFrequency = b.getDword(); +#ifdef DEBUG + debug(5, "SMUSH HEADER : secondary version == %d, unknown2 == %d, sound frequency == %d", _secondaryVersion, unknown2, _soundFrequency); + int 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); + int 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 int ETRS_HEADER_LENGTH = 16; + assert(length > ETRS_HEADER_LENGTH); + Chunck::type type = READ_BE_UINT32(filebuffer); + if(type != TYPE_ETRS) error("invalid type for file"); // mem leak !!! + char * old = filebuffer; + filebuffer = new char[length - ETRS_HEADER_LENGTH]; + for(int 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(int i = 0; i < 4; i++) { + char file[260]; + sprintf(file, "%s/font%d.nut",directory, i); + _fr[i] = loadFont(file, i != 0); + } + } + } + } + FileChunck base = FileChunck(file); + + checkBlock(base, TYPE_ANIM); + + while(!base.eof()) { + Chunck * sub = base.subBlock(); + switch(sub->getType()) { + case TYPE_AHDR: + handleAnimHeader(*sub); + break; + case TYPE_FRME: + handleFrame(*sub); + break; + default: + error("Unknown chunck 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; +} diff --git a/scumm/smush/player.h b/scumm/smush/player.h new file mode 100644 index 0000000000..ceb58eaee2 --- /dev/null +++ b/scumm/smush/player.h @@ -0,0 +1,104 @@ +/* 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$ + * + */ + +#ifndef __PLAYER_H_ +#define __PLAYER_H_ + +#include "config.h" + +#include "rect.h" +#include "mixer.h" +#include "chunck.h" +#include "palette.h" +#include "codec1.h" +#include "codec37.h" +#include "codec44.h" +#include "codec47.h" +#include "frenderer.h" + +class Renderer; + +class StringResource; + +/*! @brief the SMUSH player class + + This class is the player itself. +*/ +class SmushPlayer { +private: + char * _fname; //!< the name of the animation file being played + int _version; //!< the version of the animation file being played + int _secondaryVersion; //!< the secondary version number of the animation file being played + int _soundFrequency; //!< the sound frequency of the animation file being played + int _nbframes; //!< the number of frames in the animation file + Mixer * _mixer; //!< the sound mixer + Palette _pal; //!< the current palette + short _deltaPal[768]; //!< the delta palette information set by an xpal + Renderer * _renderer; //!< pointer to the ::renderer + StringResource * _strings; //!< pointer to the string resources associated with the animation + FontRenderer * _fr[4]; //!< pointers to the fonts for the animation + Codec1Decoder _codec1; //!< the ::decoder for codec 1 and 3 + Codec37Decoder _codec37; //!< the ::decoder for codec 37 + Codec44Decoder _codec44; //!< the ::decoder for codec 21 and 44 + DumpDecoder _codecd; //!< the ::decoder for codec 21 and 44 + Point _frameSize; //!< the current frame size of the animation + int _frame; //!< the current frame number of the animation + bool _outputSound; //!< should we handle sound ? + bool _wait; //!< should we synchronise the player ? + bool _alreadyInit; //!< has the player already been initialized for the current frame + bool _codec37Called; //!< has the codec 37 already been called once for this animation + bool _skipNext; //!< should the player skip the next frame object ? + bool _subtitles; //!< should the player handle subtitles ? + bool _bgmusic; //!< should the player output the background music ? + bool _voices; //!< should the player output the voice ? + bool _skips[37]; //!< mapping of frame object identifier to show or hide + char * _curBuffer; //!< pointer to the current frame +public: + SmushPlayer(Renderer *, bool wait = true, bool output_sound = true); + virtual ~SmushPlayer(); + bool play(const char *); + void updatePalette(void); + void show(const char *); + void hide(const char *); +protected: + bool readString(const char * file, bool &); + void clean(); + void checkBlock(const Chunck &, Chunck::type, unsigned int = 0); + void handleAnimHeader(Chunck &); + void handleFrame(Chunck &); + void handleNewPalette(Chunck &); + void handleFrameObject(Chunck &); + void handleSoundBuffer(int, int, int, int, int, int, Chunck &, int); + void handleImuseBuffer(int, int, int, int, int, int, Chunck &, int); + void handleSoundFrame(Chunck &); + void handleSkip(Chunck &); + void handleStore(Chunck &); + void handleFetch(Chunck &); + void handleImuseAction8(Chunck &, int flags, int unknown, int track_id); + void handleImuseAction(Chunck &); + void handleTextResource(Chunck &); + void handleDeltaPalette(Chunck &); + void decodeCodec(Chunck &, const Rect &, Decoder &); + void readPalette(Palette &, Chunck &); + void initSize(const Rect &, bool, bool); +}; + +#endif diff --git a/scumm/smush/rect.cpp b/scumm/smush/rect.cpp new file mode 100644 index 0000000000..deb1d5b35f --- /dev/null +++ b/scumm/smush/rect.cpp @@ -0,0 +1,62 @@ +/* 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 "rect.h" + +Rect::Rect() : _topLeft(0, 0), _bottomRight(0,0) { +} + +Rect::Rect(int x, int y) : _topLeft(0, 0), _bottomRight(x, y) { + check(); +} + +Rect::Rect(int x1, int y1, int x2, int y2) : _topLeft(x1, y1), _bottomRight(x2, y2) { + check(); +} + +Rect::Rect(const Rect & r) : _topLeft(r._topLeft), _bottomRight(r._bottomRight) { +} + +Rect & Rect::operator=(const Rect & r) { + _topLeft = r._topLeft; + _bottomRight = r._bottomRight; + return *this; +} + +bool Rect::operator==(const Rect & r) const { + return _topLeft == r._topLeft && _bottomRight == r._bottomRight; +} + +void Rect::check() { + if ((_topLeft.getX() < 0) || (_bottomRight.getX() < _topLeft.getX()) || (_topLeft.getY() < 0) || (_bottomRight.getY() < _topLeft.getY())) { + error("Invalid rect"); + } +} + +bool Rect::isInside(int x, int y) const { + return _topLeft.getX() >= x && _bottomRight.getX() < x && _topLeft.getY() >= y && _bottomRight.getY() < y; +} + +bool Rect::isInside(const Point & p) const { + return (left() <= p.getX()) && (right() > p.getX()) && (top() <= p.getY()) && (bottom() > p.getY()); +} + diff --git a/scumm/smush/rect.h b/scumm/smush/rect.h new file mode 100644 index 0000000000..c7483f54ef --- /dev/null +++ b/scumm/smush/rect.h @@ -0,0 +1,101 @@ +/* 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$ + * + */ + +#ifndef __RECT_H_ +#define __RECT_H_ + +#include "config.h" + +/*! @brief simple class for handling both 2D position and size + + This small class is an helper for position and size values. +*/ +class Point { +private: + int _x; //!< The horizontal part of the point + int _y; //!< The vertical part of the point +public: + Point() : _x(0), _y(0) {}; + Point(const Point & p) : _x(p.getX()), _y(p.getY()) {}; + explicit Point(int x, int y) : _x(x), _y(y) {}; + Point & operator=(const Point & p) { _x = p.getX(); _y = p.getY(); return *this; }; + bool operator==(const Point & p) const { return _x == p.getX() && _y == p.getY(); }; + const int & getX() const { return _x; }; + const int & getY() const { return _y; }; + int & getX() { return _x; }; + int & getY() { return _y; }; + Point operator+(const Point & p) const { return Point(_x + p.getX(), _y+p.getY()); }; + Point operator-(const Point & p) const { return Point(_x - p.getX(), _y-p.getY()); }; + Point & operator+=(const Point & p) { _x += p.getX(); _y += p.getY(); return *this; }; + Point & operator-=(const Point & p) { _x -= p.getX(); _y -= p.getY(); return *this; }; + bool isOrigin() const { return *this == Point(0, 0); }; + void set(int x, int y) { _x = x; _y = y; } +}; + +/*! @brief simple class for handling a rectangular zone. + + This small class is an helper for rectangles. + It is mostly used by the blitter class. +*/ +class Rect { +private: + Point _topLeft; //!< The point at the top left of the rectangle + Point _bottomRight; //!< The point at the bottom right of the rectangle +protected: + void check(); +public: + Rect(); + Rect(int x, int y); + explicit Rect(const Point & size); + Rect(int x1, int y1, int x2, int y2); + Rect(const Point & topleft, const Point & bottomright); + Rect(const Rect & r); + Rect & operator=(const Rect & r); + bool operator==(const Rect & r) const; + Point size() const { return (_bottomRight - _topLeft); }; + int width() const { return size().getX(); } + int height() const { return size().getY(); } + int left() const { return _topLeft.getX(); } + int right() const { return _bottomRight.getX(); } + int top() const { return _topLeft.getY(); } + int bottom() const { return _bottomRight.getY(); } + const Point & topLeft() const { return _topLeft; } + const Point & bottomRight() const { return _bottomRight; } + + /*! @brief check if given position is inside the rectangle + + @param x the horizontal position to check + @param y the vertical position to check + + @return true if the given position is inside the rectangle, false otherwise + */ + bool isInside(int x, int y) const; + /*! @brief check if given point is inside the rectangle + + @param p the point to check + + @return true if the given point is inside the rectangle, false otherwise + */ + bool isInside(const Point & p) const; + bool clip(Rect & r) const; +}; + +#endif diff --git a/scumm/smush/renderer.h b/scumm/smush/renderer.h new file mode 100644 index 0000000000..dde44a3afe --- /dev/null +++ b/scumm/smush/renderer.h @@ -0,0 +1,123 @@ +/* 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$ + * + */ + +#ifndef __RENDERER_H_ +#define __RENDERER_H_ + +#include "config.h" + +#include "rect.h" + +class Palette; +class Mixer; + +/*! @brief interface for general output (rendering) + + This is the interface for frame output. + Several implementations of these interface exist, each having a particular + application. +*/ +class Renderer { +public: + virtual ~Renderer() {}; + /*! @brief start of animation output + + This is called by the animation player when output is going to start. + + @param fname name of the animation being played. + @param version version number of the animation + @param nbframes total number of frames of the animation. + + @return true if initialisation was ok, false otherwise + */ + virtual bool startDecode(const char * fname, int version, int nbframes) = 0; + /*! @brief start of animation output + + This is called by the animation player when the frame size is changing. + + @param size new size of the frames. + + @return true if everything went fine, false otherwise + */ + virtual bool initFrame(const Point & size) = 0; + /*! @brief set a new palette + + This is called by the animation player when the palette is changing. + + @param pal new palette. + + @return true if everything went fine, false otherwise + */ + virtual bool setPalette(const Palette & pal) = 0; + /*! @brief lock a frame buffer + + This is called by the animation player when a frame is going to be decoded. + + @param frame the frame number. + + @return a pointer to the frame buffer to output data to. + */ + virtual char * lockFrame(int frame) = 0; + /*! @brief unlock a frame buffer + + This is called by the animation player when a frame has been decoded. + + @return true if everything went fine, false otherwise + */ + virtual bool unlockFrame() = 0; + /*! @brief flip a frame buffer + + This is called by the animation player when the current frame should be shown. + + @return true if everything went fine, false otherwise + */ + virtual bool flipFrame() = 0; + /*! @brief wait for some time + + This is called by the animation player when the animation should stay idle. + + @param ms number of millisecond to wait. + + @return true if everything went fine, false otherwise + */ + virtual bool wait(int ms) = 0; + /*! @brief does the renderer want a premature end of the animation ? + + This is called by the animation player after each frame. + + @return true if playing should be stopped, false otherwise. + */ + virtual bool prematureClose() = 0; + /*! @brief request for a mixer + + This is called by the animation player when sound output is required by the animation. + + @return a valid pointer to an uninitialized mixer instance, or null if none is available. + */ + virtual Mixer * getMixer() = 0; + /*! @brief debugging function : do not use + + @return true if everything went fine, false otherwise + */ + virtual bool saveCurrent() { return false; }; +}; + +#endif diff --git a/scumm/smush/saud_channel.cpp b/scumm/smush/saud_channel.cpp new file mode 100644 index 0000000000..8fd584b659 --- /dev/null +++ b/scumm/smush/saud_channel.cpp @@ -0,0 +1,274 @@ +/* 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 "channel.h" +#include "chunck.h" +#include "chunck_type.h" + +#include <assert.h> +#include <string.h> // for memcpy.h +#ifndef min +#define min(x, y) ((x) > (y) ? (y) : (x)) +#endif + +void SaudChannel::handleStrk(Chunck & b) { + int size = b.getSize(); + if(size != 14 && size != 10) { + error("STRK has a invalid size : %d", size); + } +} + +void SaudChannel::handleSmrk(Chunck & b) { + _markReached = true; +} + +void SaudChannel::handleShdr(Chunck & b) { + int size = b.getSize(); + if(size != 4) warning("SMRK has a invalid size : %d", size); +} + +bool SaudChannel::handleSubTags(int & offset) { + int available_size = _tbufferSize - offset; + if(available_size >= 8) { + Chunck::type type = READ_BE_UINT32(_tbuffer + offset); + unsigned int size = READ_BE_UINT32(_tbuffer + offset + 4); + + switch(type) { + case TYPE_STRK: + _inData = false; + if(available_size >= (size + 8)) { + ContChunck c((char*)_tbuffer + offset); + handleStrk(c); + } + else + return false; + break; + case TYPE_SMRK: + _inData = false; + if(available_size >= (size + 8)) { + ContChunck c((char*)_tbuffer + offset); + handleSmrk(c); + } + else + return false; + break; + case TYPE_SHDR: + _inData = false; + if(available_size >= (size + 8)) { + ContChunck c((char*)_tbuffer + offset); + handleShdr(c); + } + else + return false; + break; + case TYPE_SDAT: + _inData = true; + _dataSize = size; + offset += 8; + return false; + default: + error("unknown chunck in SAUD track : %s ", Chunck::ChunckString(type)); + } + offset += size + 8; + return true; + } + return false; +} + +bool SaudChannel::processBuffer() { + // At the start of this function, we have _tbuffer[0.._tbuffersize] containing possible data... + // and _sbuffer is 0 + // At the end we have : + // if(sound data) _sbuffer[0.._sbuffer_size] contains the sound data + // the unprocessed data is kept in _tbuffer[0.._tbuffersize] (which may have changed) + // if no unprocessed data, then _tbuffer is 0 + assert(_tbuffer != 0); + assert(_tbufferSize != 0); + assert(_sbuffer == 0); + assert(_sbufferSize == 0); + + if(_inData) { + if(_dataSize < _tbufferSize) { + // I can't assume that the channel is finished after data is received... (this assumption failed in realride.san) + int offset= _dataSize; + while(handleSubTags(offset)); + _sbufferSize = _dataSize; + _sbuffer = _tbuffer; + if(offset < _tbufferSize) { // there is still some unprocessed data + int new_size = _tbufferSize - offset; + _tbuffer = new unsigned char[new_size]; + if(!_tbuffer) error("SaudChannel failed to allocate memory"); + memcpy(_tbuffer, _sbuffer + offset, new_size); + _tbufferSize = new_size; + } else { + _tbuffer = 0; + _tbufferSize = 0; + } + if(_sbufferSize == 0) { + // this never happened yet, but who knows + delete []_sbuffer; + _sbuffer = 0; + } + } else { + // easy, swap the buffer + _sbufferSize = _tbufferSize; + _sbuffer = _tbuffer; + _tbufferSize = 0; + _tbuffer = 0; + } + } else { + int offset = 0; + while(handleSubTags(offset)); + if(_inData) { + _sbufferSize = _tbufferSize - offset; + assert(_sbufferSize); + _sbuffer = new unsigned char[_sbufferSize]; + if(!_sbuffer) error("saud_channel failed to allocate memory"); + memcpy(_sbuffer, _tbuffer + offset, _sbufferSize); + delete []_tbuffer; + _tbuffer = 0; + _tbufferSize = 0; + } else { + if(offset) { // maybe I should assert() this to avoid a lock... + unsigned char * old = _tbuffer; + int new_size = _tbufferSize - offset; + _tbuffer = new unsigned char[new_size]; + if(!_tbuffer) error("SaudChannel failed to allocate memory"); + memcpy(_tbuffer, old + offset, new_size); + _tbufferSize = new_size; + delete []old; + } + } + } + return true; +} + +SaudChannel::SaudChannel(int track, int freq) : + _track(track), + _nbframes(0), + _dataSize(-1), + _tbuffer(0), + _sbuffer(0), + _frequency(freq), + _tbufferSize(0), + _sbufferSize(0), + _inData(false), + _markReached(false) + { +} + +SaudChannel::~SaudChannel() { + if(_tbuffer) delete []_tbuffer; + if(_sbuffer) { + warning("this should never happen !!!! (_sbuffer not NULL here)"); + delete []_sbuffer; + } +} + +bool SaudChannel::isTerminated() const { + return (_markReached && _dataSize == 0 && _sbuffer == 0); +} + +void SaudChannel::recalcVolumeTable() { + const int MAX_BALANCE = 100; + int volume_left, volume_right; + if(_balance < -MAX_BALANCE || _balance > MAX_BALANCE) { + error("balance is out of range ! : %d", _balance); + } + int left_multiplier = MAX_BALANCE - _balance; + int right_multiplier = MAX_BALANCE + _balance; + volume_left = _volume * left_multiplier / (MAX_BALANCE * 2); + volume_right = _volume * right_multiplier / (MAX_BALANCE * 2); + if(volume_left < 0) volume_left = 0; + if(volume_left > 128) volume_left = 128; + if(volume_right < 0) volume_right = 0; + if(volume_right > 128) volume_right = 128; + for(int i = 0; i < 256; i++) { + int value = volume_left * (signed char)i; + _voltable[0][i] = TO_BE_16(value); + value = volume_right * (signed char)i; + _voltable[1][i] = TO_BE_16(value); + } +} + +bool SaudChannel::setParameters(int nb, int flags, int volume, int balance) { + _nbframes = nb; + _flags = flags; // bit 7 == IS_VOICE, bit 6 == IS_BACKGROUND_MUSIC, other ?? + _volume = volume; + _balance = balance; + _index = 0; + recalcVolumeTable(); + return true; +} + +bool SaudChannel::checkParameters(int index, int nb, int flags, int volume, int balance) { + if(++_index != index) error("invalid index in SaudChannel::checkParameters()"); + if(_nbframes != nb) error("invalid duration in SaudChannel::checkParameters()"); + if(_flags != flags) error("invalid flags in SaudChannel::checkParameters()"); + if(_volume != volume || _balance != balance) { + _volume = volume; + _balance = balance; + recalcVolumeTable(); + } + return true; +} + +bool SaudChannel::appendData(Chunck & b, int size) { + if(_dataSize == -1) { // First call + assert(size > 8); + Chunck::type saud_type = b.getDword(); saud_type = TO_BE_32(saud_type); + unsigned int saud_size = b.getDword(); saud_size = TO_BE_32(saud_size); + if(saud_type != TYPE_SAUD) error("Invalid CHUNCK for SaudChannel : %X", saud_type); + size -= 8; + _dataSize = -2; // We don't get here again... + } + if(_tbuffer) { + unsigned char * old = _tbuffer; + _tbuffer = new unsigned char[_tbufferSize + size]; + if(!_tbuffer) error("saud_channel failed to allocate memory"); + memcpy(_tbuffer, old, _tbufferSize); + delete []old; + b.read(_tbuffer + _tbufferSize, size); + _tbufferSize += size; + } else { + _tbufferSize = size; + _tbuffer = new unsigned char[_tbufferSize]; + if(!_tbuffer) error("saud_channel failed to allocate memory"); + b.read(_tbuffer, _tbufferSize); + } + return processBuffer(); +} + +int SaudChannel::availableSoundData(void) const { + return _sbufferSize; +} + +void SaudChannel::getSoundData(short * snd, int size) { + for(int i = 0; i < size; i++) { + snd[2 * i] = _voltable[0][_sbuffer[i] ^ 0x80]; + snd[2 * i + 1] = _voltable[1][_sbuffer[i] ^ 0x80]; + } + _dataSize -= size; + delete []_sbuffer; + _sbuffer = 0; + _sbufferSize = 0; +} diff --git a/scumm/smush/scumm_renderer.cpp b/scumm/smush/scumm_renderer.cpp new file mode 100644 index 0000000000..bfce102ae5 --- /dev/null +++ b/scumm/smush/scumm_renderer.cpp @@ -0,0 +1,266 @@ +/* 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 "scumm_renderer.h" +#include "channel.h" + +class scumm_mixer : public Mixer { +private: + SoundMixer * _mixer; //!< pointer to the SoundMixer instance + struct { + int id; + _Channel * chan; + bool first; + int mixer_index; + } _channels[SoundMixer::NUM_CHANNELS]; //!< The map of track and channels + int _nextIndex; +public: + scumm_mixer(SoundMixer *); + virtual ~scumm_mixer(); + bool init(); + _Channel * findChannel(int track); + bool addChannel(_Channel * c); + bool handleFrame(); + bool stop(); + bool update(); +}; + +scumm_mixer::scumm_mixer(SoundMixer * m) : _mixer(m), _nextIndex(0) { + for(int i = 0; i < SoundMixer::NUM_CHANNELS; i++) { + _channels[i].id = -1; + _channels[i].chan = 0; + _channels[i].first = true; + } +} + +scumm_mixer::~scumm_mixer() { +} + +bool scumm_mixer::init() { + debug(9, "scumm_mixer::init()"); + return true; +} + +_Channel * scumm_mixer::findChannel(int track) { + debug(9, "scumm_mixer::findChannel(%d)", track); + for(int i = 0; i < SoundMixer::NUM_CHANNELS; i++) { + if(_channels[i].id == track) + return _channels[i].chan; + } + return 0; +} + +bool scumm_mixer::addChannel(_Channel * c) { + int track = c->getTrackIdentifier(); + int i; + + debug(9, "scumm_mixer::addChannel(%d)", track); + + for(i = 0; i < SoundMixer::NUM_CHANNELS; i++) { + if(_channels[i].id == track) + warning("mixer::addChannel(%d) : channel already exist !", track); + } + if(_nextIndex >= SoundMixer::NUM_CHANNELS) _nextIndex = 0; + + for(i = _nextIndex; i < SoundMixer::NUM_CHANNELS; i++) { + if(_channels[i].chan == 0 || _channels[i].id == -1) { + _channels[i].chan = c; + _channels[i].id = track; + _channels[i].first = true; + _nextIndex = i + 1; + return true; + } + } + + for(i = 0; i < _nextIndex; i++) { + if(_channels[i].chan == 0 || _channels[i].id == -1) { + _channels[i].chan = c; + _channels[i].id = track; + _channels[i].first = true; + _nextIndex = i + 1; + return true; + } + } + + fprintf(stderr, "_nextIndex == %d\n", _nextIndex); + + for(i = 0; i < SoundMixer::NUM_CHANNELS; i++) { + fprintf(stderr, "channel %d : %p(%d, %d) %d %d\n", i, _channels[i].chan, + _channels[i].chan ? _channels[i].chan->getTrackIdentifier() : -1, + _channels[i].chan ? _channels[i].chan->isTerminated() : 1, + _channels[i].first, _channels[i].mixer_index); + } + + error("mixer::add_channel() : no more channel available"); + return false; +} + +bool scumm_mixer::handleFrame() { + debug(9, "scumm_mixer::handleFrame()"); + for(int i = 0; i < SoundMixer::NUM_CHANNELS; i++) { + if(_channels[i].id != -1) { + debug(9, "updating channel %d (%p)", _channels[i].id, _channels[i].chan); + if(_channels[i].chan->isTerminated()) { + debug(9, "channel %d has terminated (%p)", _channels[i].id, _channels[i].chan); + delete _channels[i].chan; + _channels[i].id = -1; + _channels[i].chan = 0; + } else { + int rate; + bool stereo, is_short; + + _channels[i].chan->getParameters(rate, stereo, is_short); + int size = _channels[i].chan->availableSoundData(); + debug(9, "channel %d : %d, %s, %d bits, %d", _channels[i].id, rate, stereo ? "stereo" : "mono", is_short ? 16 : 8, size); + int flags = stereo ? SoundMixer::FLAG_STEREO : 0; + + if(is_short) { + // FIXME this is one more data copy... we could get rid of it... + short * data = new short[size * (stereo ? 2 : 1)]; + _channels[i].chan->getSoundData(data, size); + size *= stereo ? 4 : 2; + + // append to _sound + if(_channels[i].first) { + _channels[i].mixer_index = _mixer->playStream(NULL, -1, data, size, rate, flags | SoundMixer::FLAG_16BITS); + debug(5, "channel %d bound to mixer_index %d", _channels[i].id, _channels[i].mixer_index); + _channels[i].first = false; + } else { + _mixer->append(_channels[i].mixer_index, data, size, rate, flags | SoundMixer::FLAG_16BITS); + } + + delete []data; + } else { + char * data = new char[size*(stereo ? 2 : 1)]; + _channels[i].chan->getSoundData(data, size); + size *= stereo ? 2 : 1; + + // append to _sound + if(_channels[i].first) { + _channels[i].mixer_index = _mixer->playStream(NULL, -1, data, size, rate, flags | SoundMixer::FLAG_UNSIGNED); + _channels[i].first = false; + } else { + _mixer->append(_channels[i].mixer_index, data, size, rate, flags | SoundMixer::FLAG_UNSIGNED); + } + + delete []data; + } + } + } + } + return true; +} + +bool scumm_mixer::stop() { + debug(9, "scumm_mixer::stop()"); + for(int i = 0; i < SoundMixer::NUM_CHANNELS; i++) { + if(_channels[i].id != -1) { + delete _channels[i].chan; + _channels[i].id = -1; + _channels[i].chan = 0; + } + } + //~ _mixer->stopAll(); + return true; +} + +ScummRenderer::ScummRenderer(Scumm * scumm) : _scumm(scumm), _smixer(0) { +} + +static ScummRenderer * s_renderer; + +static void smush_handler(Scumm * scumm) { + s_renderer->update(); +} + +Mixer * ScummRenderer::getMixer() { + if(_smixer == 0) { + _scumm->_sound->pauseBundleMusic(true); + _smixer = new scumm_mixer(_scumm->_mixer); + if(!_smixer) error("unable to allocate a smush mixer"); + s_renderer = this; + _scumm->_timer->installProcedure(&smush_handler, 75); + } + return _smixer; +} + +ScummRenderer::~ScummRenderer() { + _scumm->_insaneState = 0; + _scumm->exitCutscene(); + if(_smixer) { + _scumm->_timer->releaseProcedure(&smush_handler); + delete _smixer; + _smixer = 0; + } + _scumm->_sound->pauseBundleMusic(false); +} + +bool ScummRenderer::wait(int ms) { + while(_wait) { + _scumm->waitForTimer(1); + } + return true; +} + +bool ScummRenderer::startDecode(const char * fname, int version, int nbframes) { + _scumm->_sound->pauseBundleMusic(true); + _scumm->videoFinished = 0; + _scumm->_insaneState = 1; + return true; +} + +bool ScummRenderer::setPalette(const Palette & pal) { + int i; + byte palette_colors[1024]; + byte *p = palette_colors; + + for (i = 0; i < 256; i++, p += 4) { + p[0] = pal[i].red(); + p[1] = pal[i].green(); + p[2] = pal[i].blue(); + p[3] = 0; + } + + _scumm->_system->set_palette(palette_colors, 0, 256); + _scumm->setDirtyColors(0, 255); + return BaseRenderer::setPalette(pal); // For compatibility with possible subclass... +} + +void ScummRenderer::save(int frame) { + int width = min(getWidth(), _scumm->_realWidth); + int height = min(getHeight(), _scumm->_realHeight); + + _scumm->_system->copy_rect((const byte *)data(), getWidth(), 0, 0, width, height); + _scumm->_system->update_screen(); + _scumm->processKbd(); + _wait = true; +} + +bool ScummRenderer::prematureClose() { + return _scumm->videoFinished; +} + +bool ScummRenderer::update() { + _wait = false; + return true; +} + diff --git a/scumm/smush/scumm_renderer.h b/scumm/smush/scumm_renderer.h new file mode 100644 index 0000000000..097c657c17 --- /dev/null +++ b/scumm/smush/scumm_renderer.h @@ -0,0 +1,66 @@ +/* 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$ + * + */ + +#ifndef __SCUMM_RENDERER_H_ +#define __SCUMM_RENDERER_H_ + +#include "config.h" + +#ifdef DEBUG +# ifndef NO_DEBUG_SCUMM_RENDERER +# define DEBUG_SCUMM_RENDERER +# endif +#else +# ifdef DEBUG_SCUMM_RENDERER +# error DEBUG_SCUMM_RENDERER defined without DEBUG +# endif +#endif + +#include "brenderer.h" +#include "mixer.h" +#include "rect.h" +#include "blitter.h" + +#ifndef min +#define min(x, y) ((x) > (y) ? (y) : (x)) +#endif + +class scumm_mixer; + +class ScummRenderer : public BaseRenderer { +private: + Scumm * _scumm; + scumm_mixer * _smixer; + volatile bool _wait; +public: + ScummRenderer(Scumm * scumm); + virtual ~ScummRenderer(); + virtual bool wait(int ms); + bool update(); +protected: + virtual bool startDecode(const char * fname, int version, int nbframes); + virtual bool setPalette(const Palette & pal); + virtual void save(int frame = -1); + virtual Mixer * getMixer(); + virtual bool prematureClose(); +}; + +#endif |