2010-01-06 21:56:31 +00:00
|
|
|
/* ScummVM - Graphic Adventure Engine
|
|
|
|
*
|
|
|
|
* ScummVM is the legal property of its developers, whose names
|
|
|
|
* are too numerous to list here. Please refer to the COPYRIGHT
|
|
|
|
* file distributed with this source distribution.
|
|
|
|
*
|
|
|
|
* This program is free software; you can redistribute it and/or
|
|
|
|
* modify it under the terms of the GNU General Public License
|
|
|
|
* as published by the Free Software Foundation; either version 2
|
|
|
|
* of the License, or (at your option) any later version.
|
2014-02-18 02:34:24 +01:00
|
|
|
*
|
2010-01-06 21:56:31 +00:00
|
|
|
* 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.
|
2014-02-18 02:34:24 +01:00
|
|
|
*
|
2010-01-06 21:56:31 +00:00
|
|
|
* You should have received a copy of the GNU General Public License
|
|
|
|
* along with this program; if not, write to the Free Software
|
|
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
2016-07-03 17:57:58 -05:00
|
|
|
#ifndef SCI_SOUND_DECODERS_ROBOT_H
|
|
|
|
#define SCI_SOUND_DECODERS_ROBOT_H
|
2010-01-06 21:56:31 +00:00
|
|
|
|
2016-07-03 17:57:58 -05:00
|
|
|
#include "audio/audiostream.h" // for AudioStream
|
|
|
|
#include "audio/rate.h" // for st_sample_t
|
|
|
|
#include "common/array.h" // for Array
|
|
|
|
#include "common/mutex.h" // for StackLock, Mutex
|
|
|
|
#include "common/rect.h" // for Point, Rect (ptr only)
|
|
|
|
#include "common/scummsys.h" // for int16, int32, byte, uint16
|
|
|
|
#include "sci/engine/vm_types.h" // for NULL_REG, reg_t
|
|
|
|
#include "sci/graphics/helpers.h" // for GuiResourceId
|
|
|
|
#include "sci/graphics/screen_item32.h" // for ScaleInfo, ScreenItem (ptr o...
|
2011-02-07 01:56:30 +00:00
|
|
|
|
2016-07-03 17:57:58 -05:00
|
|
|
namespace Common { class SeekableSubReadStreamEndian; }
|
|
|
|
namespace Sci {
|
|
|
|
class Plane;
|
|
|
|
class SegManager;
|
2010-01-28 19:28:56 +00:00
|
|
|
|
2016-07-03 17:57:58 -05:00
|
|
|
// Notes on Robot v5/v6 format:
|
|
|
|
//
|
|
|
|
// Robot is a packetized streaming AV format that encodes multiple bitmaps +
|
|
|
|
// positioning data, plus synchronised audio, for rendering in the SCI graphics
|
|
|
|
// system.
|
|
|
|
//
|
|
|
|
// Unlike traditional AV formats, Robot videos almost always require playback
|
|
|
|
// within the game engine because certain information (like the resolution of
|
|
|
|
// the Robot coordinates and the background for the video) is dependent on data
|
|
|
|
// that does not exist within the Robot file itself.
|
|
|
|
//
|
|
|
|
// The Robot container consists of a file header, an optional primer audio
|
|
|
|
// section, an optional colour palette, a frame seek index, a set of cuepoints,
|
|
|
|
// and variable-sized packets of compressed video+audio data.
|
|
|
|
//
|
|
|
|
// Integers in Robot files are coded using native endianness (LSB for x86
|
|
|
|
// versions, MSB for 68k/PPC versions).
|
|
|
|
//
|
|
|
|
// Robot video coding is a relatively simple variable-length compression with no
|
|
|
|
// interframe compression. Each cel in a frame is constructed from multiple
|
|
|
|
// contiguous data blocks, each of which can be independently compressed with
|
|
|
|
// LZS or left uncompressed. An entire cel can also be line decimated, where
|
|
|
|
// lines are deleted from the source bitmap at compression time and are
|
|
|
|
// reconstructed by decompression using line doubling. Each cel also includes
|
|
|
|
// coordinates where it should be placed within the video frame, relative to the
|
|
|
|
// top-left corner of the frame.
|
|
|
|
//
|
|
|
|
// Audio coding is fixed-length, and all audio blocks except for the primer
|
|
|
|
// audio are the same size. Audio is encoded with Sierra SOL DPCM16 compression,
|
|
|
|
// and is split into two channels ('even' and 'odd'), each at a 11025Hz sample
|
|
|
|
// rate. The original signal is restored by interleaving samples from the two
|
|
|
|
// channels together. Channel packets are 'even' if they have an ''absolute
|
|
|
|
// position of audio'' that is evenly divisible by 2; otherwise, they are 'odd'.
|
|
|
|
// Because the channels use DPCM compression, there is an 8-byte runway at the
|
|
|
|
// start of every audio block that is never written to the output stream, which
|
|
|
|
// is used to move the signal to the correct location by the 9th sample.
|
|
|
|
//
|
|
|
|
// File header (v5/v6):
|
|
|
|
//
|
|
|
|
// byte | description
|
|
|
|
// 0 | signature 0x16
|
|
|
|
// 1 | unused
|
|
|
|
// 2-5 | signature 'SOL\0'
|
|
|
|
// 6-7 | version (4, 5, and 6 are the only known versions)
|
|
|
|
// 8-9 | size of audio blocks
|
|
|
|
// 10-11 | primer is compressed flag
|
|
|
|
// 12-13 | unused
|
|
|
|
// 14-15 | total number of video frames
|
|
|
|
// 16-17 | embedded palette size, in bytes
|
|
|
|
// 18-19 | primer reserved size
|
|
|
|
// 20-21 | coordinate X-resolution (if 0, uses game coordinates)
|
|
|
|
// 22-23 | coordinate Y-resolution (if 0, uses game coordinates)
|
|
|
|
// 24 | if non-zero, Robot includes a palette
|
|
|
|
// 25 | if non-zero, Robot includes audio
|
|
|
|
// 26-27 | unused
|
|
|
|
// 28-29 | the frame rate, in frames per second
|
|
|
|
// 30-31 | coordinate conversion flag; if true, screen item coordinates
|
|
|
|
// | from the robot should be used as-is with NO conversion when
|
|
|
|
// | explicitly displaying a specific frame
|
|
|
|
// 32-33 | the maximum number of packets that can be skipped without causing
|
|
|
|
// | audio drop-out
|
|
|
|
// 34-35 | the maximum possible number of cels that will be displayed in any
|
|
|
|
// | frame of the robot
|
|
|
|
// 36-39 | the maximum possible size, in bytes, of the first fixed cel
|
|
|
|
// 40-43 | the maximum possible size, in bytes, of the second fixed cel
|
|
|
|
// 44-47 | the maximum possible size, in bytes, of the third fixed cel
|
|
|
|
// 48-51 | the maximum possible size, in bytes, of the fourth fixed cel
|
|
|
|
// 52-59 | unused
|
|
|
|
//
|
|
|
|
// If the ''file includes audio'' flag is false, seek ''primer reserved size''
|
|
|
|
// bytes from the end of the file header to get past a padding zone.
|
|
|
|
//
|
|
|
|
// If the ''file includes audio'' flag is true, and the ''primer reserved size''
|
|
|
|
// is not zero, the data immediately after the file header consists of an audio
|
|
|
|
// primer header plus compressed audio data:
|
|
|
|
//
|
|
|
|
// Audio primer header:
|
|
|
|
//
|
|
|
|
// byte | description
|
|
|
|
// 0-3 | the size, in bytes, of the entire primer audio section
|
|
|
|
// 4-5 | the compression format of the primer audio (must be zero)
|
|
|
|
// 6-9 | the size, in bytes, of the "even" primer
|
|
|
|
// 10-13 | the size, in bytes, of the "odd" primer
|
|
|
|
//
|
|
|
|
// If the combined sizes of the even and odd primers do not match the ''primer
|
|
|
|
// reserved size'', the next header block can be found ''primer reserved size''
|
|
|
|
// bytes from the *start* of the audio primer header.
|
|
|
|
//
|
|
|
|
// Otherwise, if the Robot has audio, and the ''primer reserved size'' is zero,
|
|
|
|
// and the ''primer is compressed flag'' is set, the "even" primer size is
|
|
|
|
// 19922, the "odd" primer size is 21024, and the "even" and "odd" buffers
|
|
|
|
// should be zero-filled.
|
|
|
|
//
|
|
|
|
// Any other combination of these flags is an error.
|
|
|
|
//
|
|
|
|
// If the Robot has a palette, the next ''palette size'' bytes should be read
|
|
|
|
// as a SCI HunkPalette. Otherwise, seek ''palette size'' bytes from the current
|
|
|
|
// position to get to the frame index.
|
|
|
|
//
|
|
|
|
// The next section of the Robot is the video frame size index. In version 5
|
|
|
|
// robots, read ''total number of frames'' 16-bit integers to get the size of
|
|
|
|
// the compressed video for each frame. For version 6 robots, use 32-bit
|
|
|
|
// integers.
|
|
|
|
//
|
|
|
|
// The next section of the Robot is the packet size index (combined compressed
|
|
|
|
// size of video + audio for each frame). In version 5 Robots, read ''total
|
|
|
|
// number of frames'' 16-bit integers. In version 6 robots, use 32-bit integers.
|
|
|
|
//
|
|
|
|
// The next section of the Robot is the cue times index. Read 256 32-bit
|
|
|
|
// integers, which represent the number of ticks from the start of playback that
|
|
|
|
// the given cue point falls on.
|
|
|
|
//
|
|
|
|
// The next section of the Robot is the cue values index. Read 256 16-bit
|
|
|
|
// integers, which represent the actual cue values that will be passed back to
|
|
|
|
// the game engine when a cue is requested.
|
|
|
|
//
|
|
|
|
// Finally, to get to the first frame packet, seek from the current position to
|
|
|
|
// the start of the next 2048-byte-aligned sector.
|
|
|
|
//
|
|
|
|
// Frame packet:
|
|
|
|
//
|
|
|
|
// byte | description
|
|
|
|
// 0..n | video data (size is in the ''video frame size index'')
|
|
|
|
// n+1.. | optional audio data (size is ''size of audio blocks'')
|
|
|
|
//
|
|
|
|
// Video data:
|
|
|
|
//
|
|
|
|
// byte | description
|
|
|
|
// 0-2 | number of cels in the frame (max 10)
|
|
|
|
// 3..n | cels
|
|
|
|
//
|
|
|
|
// Cel:
|
|
|
|
//
|
|
|
|
// 0-17 | cel header
|
|
|
|
// 18..n | data chunks
|
|
|
|
//
|
|
|
|
// Cel header:
|
|
|
|
//
|
|
|
|
// byte | description
|
|
|
|
// 0 | unused
|
|
|
|
// 1 | vertical scale factor, in percent decimation (100 = no decimation,
|
|
|
|
// | 50 = 50% of lines were removed)
|
|
|
|
// 2-3 | cel width
|
|
|
|
// 4-5 | cel height
|
|
|
|
// 6-9 | unused
|
|
|
|
// 10-11 | cel x-position, in Robot coordinates
|
|
|
|
// 12-13 | cel y-position, in Robot coordinates
|
|
|
|
// 14-15 | cel total data chunk size, in bytes
|
|
|
|
// 16-17 | number of data chunks
|
|
|
|
//
|
|
|
|
// Cel data chunk:
|
|
|
|
//
|
|
|
|
// 0-9 | cel data chunk header
|
|
|
|
// 10..n | cel data
|
|
|
|
//
|
|
|
|
// Cel data chunk header:
|
|
|
|
//
|
|
|
|
// byte | description
|
|
|
|
// 0-3 | compressed size
|
|
|
|
// 4-7 | decompressed size
|
|
|
|
// 8-9 | compression type (0 = LZS, 2 = uncompressed)
|
|
|
|
//
|
|
|
|
// Random frame seeking can be done by calculating the address of the frame
|
|
|
|
// packet by adding up the ''packet size index'' entries up to the current
|
|
|
|
// frame. This will normally disable audio playback, as audio data in a packet
|
|
|
|
// does not correspond to the video in the same packet.
|
|
|
|
//
|
|
|
|
// Audio data is placed immediately after the end of the video data in a packet,
|
|
|
|
// and consists of an audio header plus compressed audio data:
|
|
|
|
//
|
|
|
|
// Audio data:
|
|
|
|
//
|
|
|
|
// byte | description
|
|
|
|
// 0-7 | audio data header
|
|
|
|
// 8-15 | DPCM runway
|
|
|
|
// 16..n | compressed audio data
|
|
|
|
//
|
|
|
|
// Audio data header:
|
|
|
|
//
|
|
|
|
// byte | description
|
|
|
|
// 0-3 | absolute position of audio in the audio stream
|
|
|
|
// 4-7 | the size of the audio block, excluding the header
|
|
|
|
//
|
|
|
|
// When a block of audio is processed, first check to ensure that the
|
|
|
|
// decompressed audio block's `position * 2 + length * 4` runs past the end of
|
|
|
|
// the last packet of the same evenness/oddness. Discard the audio block
|
|
|
|
// entirely if data has already been written past the end of this block for this
|
|
|
|
// channel, or if the read head has already read past the end of this audio
|
|
|
|
// block.
|
|
|
|
//
|
|
|
|
// If the block is not discarded, apply DPCM decompression to the entire block,
|
|
|
|
// starting from beginning of the DPCM runway, using an initial sample value of
|
|
|
|
// 0. Then, copy every sample from the decompressed source outside of the DPCM
|
|
|
|
// runway into every *other* sample of the final audio buffer (1 -> 2, 2 -> 4,
|
|
|
|
// 3 -> 6, etc.).
|
|
|
|
//
|
|
|
|
// Finally, for any skipped samples where the opposing (even/odd) channel did
|
|
|
|
// not yet write, interpolate the skipped areas by adding together the
|
|
|
|
// neighbouring samples from this audio block and dividing by two. (This allows
|
|
|
|
// the audio quality to degrade to 11kHz in case it takes too long to decode all
|
|
|
|
// the frames in the stream). Interpolated samples must not be written on top of
|
|
|
|
// true data from the opposing channel. Audio from later packets must also not
|
|
|
|
// be written on top of data in the same channel that was already written by an
|
|
|
|
// earlier packet, in particular because the first 8 bytes of the next packet
|
|
|
|
// are garbage data used to move the waveform to the correct position (due to
|
|
|
|
// the use of DPCM compression).
|
2012-07-22 23:17:36 -04:00
|
|
|
|
2016-07-03 17:57:58 -05:00
|
|
|
#pragma mark -
|
|
|
|
#pragma mark RobotAudioStream
|
|
|
|
|
|
|
|
/**
|
|
|
|
* A Robot audio stream is a simple loop buffer
|
|
|
|
* that accepts audio blocks from the Robot engine.
|
|
|
|
*/
|
|
|
|
class RobotAudioStream : public Audio::AudioStream {
|
|
|
|
public:
|
|
|
|
enum {
|
|
|
|
/**
|
|
|
|
* The sample rate used for all robot audio.
|
|
|
|
*/
|
|
|
|
kRobotSampleRate = 22050,
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Multiplier for the size of a packet that
|
|
|
|
* is being expanded by writing to every other
|
|
|
|
* byte of the target buffer.
|
|
|
|
*/
|
|
|
|
kEOSExpansion = 2
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Playback state information. Used for framerate
|
|
|
|
* calculation.
|
|
|
|
*/
|
|
|
|
struct StreamState {
|
|
|
|
/**
|
|
|
|
* The current position of the read head of
|
|
|
|
* the audio stream.
|
|
|
|
*/
|
|
|
|
int bytesPlaying;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* The sample rate of the audio stream.
|
|
|
|
* Always 22050.
|
|
|
|
*/
|
|
|
|
uint16 rate;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* The bit depth of the audio stream.
|
|
|
|
* Always 16.
|
|
|
|
*/
|
|
|
|
uint8 bits;
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* A single packet of compressed audio from a
|
|
|
|
* Robot data stream.
|
|
|
|
*/
|
|
|
|
struct RobotAudioPacket {
|
|
|
|
/**
|
|
|
|
* Raw DPCM-compressed audio data.
|
|
|
|
*/
|
|
|
|
byte *data;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* The size of the compressed audio data,
|
|
|
|
* in bytes.
|
|
|
|
*/
|
|
|
|
int dataSize;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* The uncompressed, file-relative position
|
|
|
|
* of this audio packet.
|
|
|
|
*/
|
|
|
|
int position;
|
|
|
|
|
|
|
|
RobotAudioPacket(byte *data_, const int dataSize_, const int position_) :
|
|
|
|
data(data_), dataSize(dataSize_), position(position_) {}
|
|
|
|
};
|
|
|
|
|
|
|
|
RobotAudioStream(const int32 bufferSize);
|
|
|
|
virtual ~RobotAudioStream();
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Adds a new audio packet to the stream.
|
|
|
|
* @returns `true` if the audio packet was fully
|
|
|
|
* consumed, otherwise `false`.
|
|
|
|
*/
|
|
|
|
bool addPacket(const RobotAudioPacket &packet);
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Prevents any additional audio packets from
|
|
|
|
* being added to the audio stream.
|
|
|
|
*/
|
|
|
|
void finish();
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Returns the current status of the audio
|
|
|
|
* stream.
|
|
|
|
*/
|
|
|
|
StreamState getStatus() const;
|
|
|
|
|
|
|
|
private:
|
|
|
|
Common::Mutex _mutex;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Loop buffer for playback. Contains decompressed
|
|
|
|
* 16-bit PCM samples.
|
|
|
|
*/
|
|
|
|
byte *_loopBuffer;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* The size of the loop buffer, in bytes.
|
|
|
|
*/
|
|
|
|
int32 _loopBufferSize;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* The position of the read head within the loop
|
|
|
|
* buffer, in bytes.
|
|
|
|
*/
|
|
|
|
int32 _readHead;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* The lowest file position that can be buffered,
|
|
|
|
* in uncompressed bytes.
|
|
|
|
*/
|
|
|
|
int32 _readHeadAbs;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* The highest file position that can be buffered,
|
|
|
|
* in uncompressed bytes.
|
|
|
|
*/
|
|
|
|
int32 _maxWriteAbs;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* The highest file position, in uncompressed bytes,
|
|
|
|
* that has been written to the stream.
|
|
|
|
* Different from `_maxWriteAbs`, which is the highest
|
|
|
|
* uncompressed position which *can* be written right
|
|
|
|
* now.
|
|
|
|
*/
|
|
|
|
int32 _writeHeadAbs;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* The highest file position, in uncompressed bytes,
|
|
|
|
* that has been written to the even & odd sides of
|
|
|
|
* the stream.
|
|
|
|
*
|
|
|
|
* Index 0 corresponds to the 'even' side; index
|
|
|
|
* 1 correspond to the 'odd' side.
|
|
|
|
*/
|
|
|
|
int32 _jointMin[2];
|
|
|
|
|
|
|
|
/**
|
|
|
|
* When `true`, the stream is waiting for all primer
|
|
|
|
* blocks to be received before allowing playback to
|
|
|
|
* begin.
|
|
|
|
*/
|
|
|
|
bool _waiting;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* When `true`, the stream will accept no more audio
|
|
|
|
* blocks.
|
|
|
|
*/
|
|
|
|
bool _finished;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* The uncompressed position of the first packet of
|
|
|
|
* robot data. Used to decide whether all primer
|
|
|
|
* blocks have been received and the stream should
|
|
|
|
* be started.
|
|
|
|
*/
|
|
|
|
int32 _firstPacketPosition;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Decompression buffer, used to temporarily store
|
|
|
|
* an uncompressed block of audio data.
|
|
|
|
*/
|
|
|
|
byte *_decompressionBuffer;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* The size of the decompression buffer, in bytes.
|
|
|
|
*/
|
|
|
|
int32 _decompressionBufferSize;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* The position of the packet currently in the
|
|
|
|
* decompression buffer. Used to avoid
|
|
|
|
* re-decompressing audio data that has already
|
|
|
|
* been decompressed during a partial packet read.
|
|
|
|
*/
|
|
|
|
int32 _decompressionBufferPosition;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Calculates the absolute ranges for new fills
|
|
|
|
* into the loop buffer.
|
|
|
|
*/
|
|
|
|
void fillRobotBuffer(const RobotAudioPacket &packet, const int8 bufferIndex);
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Interpolates `numSamples` samples from the read
|
|
|
|
* head, if no true samples were written for one
|
|
|
|
* (or both) of the joint channels.
|
|
|
|
*/
|
|
|
|
void interpolateMissingSamples(const int32 numSamples);
|
|
|
|
|
|
|
|
#pragma mark -
|
|
|
|
#pragma mark RobotAudioStream - AudioStream implementation
|
|
|
|
public:
|
|
|
|
int readBuffer(Audio::st_sample_t *outBuffer, int numSamples) override;
|
|
|
|
virtual bool isStereo() const override { return false; };
|
|
|
|
virtual int getRate() const override { return 22050; };
|
|
|
|
virtual bool endOfData() const override {
|
|
|
|
Common::StackLock lock(_mutex);
|
|
|
|
return _readHeadAbs >= _writeHeadAbs;
|
|
|
|
};
|
|
|
|
virtual bool endOfStream() const override {
|
|
|
|
Common::StackLock lock(_mutex);
|
|
|
|
return _finished && endOfData();
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
#pragma mark -
|
|
|
|
#pragma mark RobotDecoder
|
|
|
|
|
|
|
|
/**
|
|
|
|
* RobotDecoder implements the logic required
|
|
|
|
* for Robot animations.
|
|
|
|
*
|
|
|
|
* @note A paused or finished RobotDecoder was
|
|
|
|
* classified as serializable in SCI3, but the
|
|
|
|
* save/load code would attempt to use uninitialised
|
|
|
|
* values, so it seems that robots were not ever
|
|
|
|
* actually able to be saved.
|
|
|
|
*/
|
|
|
|
class RobotDecoder {
|
|
|
|
public:
|
|
|
|
RobotDecoder(SegManager *segMan);
|
|
|
|
~RobotDecoder();
|
|
|
|
|
|
|
|
private:
|
|
|
|
SegManager *_segMan;
|
|
|
|
|
|
|
|
#pragma mark Constants
|
|
|
|
public:
|
|
|
|
/**
|
|
|
|
* The playback status of the robot.
|
|
|
|
*/
|
|
|
|
enum RobotStatus {
|
|
|
|
kRobotStatusUninitialized = 0,
|
|
|
|
kRobotStatusPlaying = 1,
|
|
|
|
kRobotStatusEnd = 2,
|
|
|
|
kRobotStatusPaused = 3
|
|
|
|
};
|
|
|
|
|
|
|
|
enum {
|
|
|
|
// Special high value used to represent
|
|
|
|
// parameters that should be left unchanged
|
|
|
|
// when calling `showFrame`
|
|
|
|
kUnspecified = 50000
|
|
|
|
};
|
|
|
|
|
|
|
|
private:
|
|
|
|
enum {
|
|
|
|
/**
|
|
|
|
* Maximum number of on-screen screen items.
|
|
|
|
*/
|
|
|
|
kScreenItemListSize = 10,
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Maximum number of queued audio blocks.
|
|
|
|
*/
|
|
|
|
kAudioListSize = 10,
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Maximum number of samples used for frame timing.
|
|
|
|
*/
|
|
|
|
kDelayListSize = 10,
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Maximum number of cues.
|
|
|
|
*/
|
|
|
|
kCueListSize = 256,
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Maximum number of 'fixed' cels that never
|
|
|
|
* change for the duration of a robot.
|
|
|
|
*/
|
|
|
|
kFixedCelListSize = 4,
|
|
|
|
|
|
|
|
/**
|
|
|
|
* The size of a hunk palette in the Robot stream.
|
|
|
|
*/
|
|
|
|
kRawPaletteSize = 1200,
|
|
|
|
|
|
|
|
/**
|
|
|
|
* The size of a frame of Robot data. This
|
|
|
|
* value was used to align the first block of
|
|
|
|
* data after the main Robot header to the next
|
|
|
|
* CD sector.
|
|
|
|
*/
|
|
|
|
kRobotFrameSize = 2048,
|
|
|
|
|
|
|
|
/**
|
|
|
|
* The size of a block of zero-compressed
|
|
|
|
* audio. Used to fill audio when the size of
|
|
|
|
* an audio packet does not match the expected
|
|
|
|
* packet size.
|
|
|
|
*/
|
|
|
|
kRobotZeroCompressSize = 2048,
|
2011-02-04 17:51:59 +00:00
|
|
|
|
2016-07-03 17:57:58 -05:00
|
|
|
/**
|
|
|
|
* The size of the audio block header, in bytes.
|
|
|
|
* The audio block header consists of the
|
|
|
|
* compressed size of the audio in the record,
|
|
|
|
* plus the position of the audio in the
|
|
|
|
* compressed data stream.
|
|
|
|
*/
|
|
|
|
kAudioBlockHeaderSize = 8,
|
|
|
|
|
|
|
|
/**
|
|
|
|
* The size of a Robot cel header, in bytes.
|
|
|
|
*/
|
|
|
|
kCelHeaderSize = 22,
|
|
|
|
|
|
|
|
/**
|
|
|
|
* The maximum amount that the frame rate is
|
|
|
|
* allowed to drift from the nominal frame rate
|
|
|
|
* in order to correct for AV drift or slow
|
|
|
|
* playback.
|
|
|
|
*/
|
|
|
|
kMaxFrameRateDrift = 1
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* The version number for the currently loaded
|
|
|
|
* robot.
|
|
|
|
*
|
|
|
|
* There are several known versions of robot:
|
|
|
|
*
|
|
|
|
* v2: before Nov 1994; no known examples
|
|
|
|
* v3: before Nov 1994; no known examples
|
|
|
|
* v4: Jan 1995; PQ:SWAT demo
|
|
|
|
* v5: Mar 1995; SCI2.1 and SCI3 games
|
|
|
|
* v6: SCI3 games
|
|
|
|
*/
|
|
|
|
uint16 _version;
|
|
|
|
|
|
|
|
#pragma mark -
|
|
|
|
#pragma mark Initialisation
|
|
|
|
private:
|
|
|
|
/**
|
|
|
|
* Sets up the read stream for the robot.
|
|
|
|
*/
|
|
|
|
void initStream(const GuiResourceId robotId);
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Sets up the initial values for playback control.
|
|
|
|
*/
|
|
|
|
void initPlayback();
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Sets up the initial values for audio decoding.
|
|
|
|
*/
|
|
|
|
void initAudio();
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Sets up the initial values for video rendering.
|
|
|
|
*/
|
|
|
|
void initVideo(const int16 x, const int16 y, const int16 scale, const reg_t plane, const bool hasPalette, const uint16 paletteSize);
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Sets up the robot's data record and cue positions.
|
|
|
|
*/
|
|
|
|
void initRecordAndCuePositions();
|
|
|
|
|
|
|
|
#pragma mark -
|
|
|
|
#pragma mark Playback
|
2010-01-06 21:56:31 +00:00
|
|
|
public:
|
2016-07-03 17:57:58 -05:00
|
|
|
/**
|
|
|
|
* Opens a robot file for playback.
|
|
|
|
* Newly opened robots are paused by default.
|
|
|
|
*/
|
|
|
|
void open(const GuiResourceId robotId, const reg_t plane, const int16 priority, const int16 x, const int16 y, const int16 scale);
|
2011-02-07 12:24:09 +00:00
|
|
|
|
2016-07-03 17:57:58 -05:00
|
|
|
/**
|
|
|
|
* Closes the currently open robot file.
|
|
|
|
*/
|
2011-02-07 12:24:09 +00:00
|
|
|
void close();
|
2012-09-26 04:17:31 +02:00
|
|
|
|
2016-07-03 17:57:58 -05:00
|
|
|
/**
|
|
|
|
* Pauses the robot. Once paused, the audio for a robot
|
|
|
|
* is disabled until the end of playback.
|
|
|
|
*/
|
|
|
|
void pause();
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Resumes a paused robot.
|
|
|
|
*/
|
|
|
|
void resume();
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Moves robot to the specified frame and pauses playback.
|
|
|
|
*
|
|
|
|
* @note Called DisplayFrame in SSCI.
|
|
|
|
*/
|
|
|
|
void showFrame(const uint16 frameNo, const uint16 newX, const uint16 newY, const uint16 newPriority);
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Retrieves the value associated with the
|
|
|
|
* current cue point.
|
|
|
|
*/
|
|
|
|
int16 getCue() const;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Gets the currently displayed frame.
|
|
|
|
*/
|
|
|
|
int16 getFrameNo() const;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Gets the playback status of the player.
|
|
|
|
*/
|
|
|
|
RobotStatus getStatus() const;
|
|
|
|
|
|
|
|
private:
|
|
|
|
/**
|
|
|
|
* The read stream containing raw robot data.
|
|
|
|
*/
|
|
|
|
Common::SeekableSubReadStreamEndian *_stream;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* The current status of the player.
|
|
|
|
*/
|
|
|
|
RobotStatus _status;
|
|
|
|
|
|
|
|
typedef Common::Array<int> PositionList;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* A map of frame numbers to byte offsets within `_stream`.
|
|
|
|
*/
|
|
|
|
PositionList _recordPositions;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* The offset of the Robot file within a
|
|
|
|
* resource bundle.
|
|
|
|
*/
|
|
|
|
int32 _fileOffset;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* A list of cue times that is updated to
|
|
|
|
* prevent earlier cue values from being
|
|
|
|
* given to the game more than once.
|
|
|
|
*/
|
|
|
|
mutable int32 _cueTimes[kCueListSize];
|
|
|
|
|
|
|
|
/**
|
|
|
|
* The original list of cue times from the
|
|
|
|
* raw Robot data.
|
|
|
|
*/
|
|
|
|
int32 _masterCueTimes[kCueListSize];
|
|
|
|
|
|
|
|
/**
|
|
|
|
* The list of values to provide to a game
|
|
|
|
* when a cue value is requested.
|
|
|
|
*/
|
|
|
|
int32 _cueValues[kCueListSize];
|
|
|
|
|
|
|
|
/**
|
|
|
|
* The current playback frame rate.
|
|
|
|
*/
|
|
|
|
int16 _frameRate;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* The nominal playback frame rate.
|
|
|
|
*/
|
|
|
|
int16 _normalFrameRate;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* The minimal playback frame rate. Used to
|
|
|
|
* correct for AV sync drift when the video
|
|
|
|
* is more than one frame ahead of the audio.
|
|
|
|
*/
|
|
|
|
int16 _minFrameRate;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* The maximum playback frame rate. Used to
|
|
|
|
* correct for AV sync drift when the video
|
|
|
|
* is more than one frame behind the audio.
|
|
|
|
*/
|
|
|
|
int16 _maxFrameRate;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* The maximum number of record blocks that
|
|
|
|
* can be skipped without causing audio to
|
|
|
|
* drop out.
|
|
|
|
*/
|
|
|
|
int16 _maxSkippablePackets;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* The currently displayed frame number.
|
|
|
|
*/
|
|
|
|
int _currentFrameNo;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* The last displayed frame number.
|
|
|
|
*/
|
|
|
|
int _previousFrameNo;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* The time, in ticks, when the robot was
|
|
|
|
* last started or resumed.
|
|
|
|
*/
|
|
|
|
int32 _startTime;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* The first frame displayed when the
|
|
|
|
* robot was resumed.
|
|
|
|
*/
|
|
|
|
int32 _startFrameNo;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* The last frame displayed when the robot
|
|
|
|
* was resumed.
|
|
|
|
*/
|
|
|
|
int32 _startingFrameNo;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Seeks the raw data stream to the record for
|
|
|
|
* the given frame number.
|
|
|
|
*/
|
|
|
|
bool seekToFrame(const int frameNo);
|
2011-02-07 12:24:09 +00:00
|
|
|
|
2016-07-03 17:57:58 -05:00
|
|
|
/**
|
|
|
|
* Sets the start time and frame of the robot
|
|
|
|
* when the robot is started or resumed.
|
|
|
|
*/
|
|
|
|
void setRobotTime(const int frameNo);
|
2012-09-26 04:17:31 +02:00
|
|
|
|
2016-07-03 17:57:58 -05:00
|
|
|
#pragma mark -
|
|
|
|
#pragma mark Timing
|
2010-01-06 21:56:31 +00:00
|
|
|
private:
|
2016-07-03 17:57:58 -05:00
|
|
|
/**
|
|
|
|
* This class tracks the amount of time it takes for
|
|
|
|
* a frame of robot animation to be rendered. This
|
|
|
|
* information is used by the player to speculatively
|
|
|
|
* skip rendering of future frames to keep the
|
|
|
|
* animation in sync with the robot audio.
|
|
|
|
*/
|
|
|
|
class DelayTime {
|
2012-07-22 23:17:36 -04:00
|
|
|
public:
|
2016-07-03 17:57:58 -05:00
|
|
|
DelayTime(RobotDecoder *decoder);
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Starts performance timing.
|
|
|
|
*/
|
|
|
|
void startTiming();
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Ends performance timing.
|
|
|
|
*/
|
|
|
|
void endTiming();
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Returns whether or not timing is currently in
|
|
|
|
* progress.
|
|
|
|
*/
|
|
|
|
bool timingInProgress() const;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Returns the median time, in ticks, of the
|
|
|
|
* currently stored timing samples.
|
|
|
|
*/
|
|
|
|
int predictedTicks() const;
|
2012-07-22 23:17:36 -04:00
|
|
|
|
|
|
|
private:
|
2016-07-03 17:57:58 -05:00
|
|
|
RobotDecoder *_decoder;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* The start time, in ticks, of the current timing
|
|
|
|
* loop. If no loop is in progress, the value is 0.
|
|
|
|
*
|
|
|
|
* @note This is slightly different than SSCI where
|
|
|
|
* the not-timing value was -1.
|
|
|
|
*/
|
|
|
|
uint32 _startTime;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* A sorted list containing the timing data for
|
|
|
|
* the last `kDelayListSize` frames, in ticks.
|
|
|
|
*/
|
|
|
|
int _delays[kDelayListSize];
|
|
|
|
|
|
|
|
/**
|
|
|
|
* A list of monotonically increasing identifiers
|
|
|
|
* used to identify and replace the oldest sample
|
|
|
|
* in the `_delays` array when finishing the
|
|
|
|
* next timing operation.
|
|
|
|
*/
|
|
|
|
uint _timestamps[kDelayListSize];
|
|
|
|
|
|
|
|
/**
|
|
|
|
* The identifier of the oldest timing.
|
|
|
|
*/
|
|
|
|
uint _oldestTimestamp;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* The identifier of the newest timing.
|
|
|
|
*/
|
|
|
|
uint _newestTimestamp;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Sorts the list of timings.
|
|
|
|
*/
|
|
|
|
void sortList();
|
2012-07-22 23:17:36 -04:00
|
|
|
};
|
|
|
|
|
2016-07-03 17:57:58 -05:00
|
|
|
/**
|
|
|
|
* Calculates the next frame number that needs
|
|
|
|
* to be rendered, using the timing data
|
|
|
|
* collected by DelayTime.
|
|
|
|
*/
|
|
|
|
uint16 calculateNextFrameNo(const uint32 extraTicks = 0) const;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Calculates and returns the number of frames
|
|
|
|
* that should be rendered in `ticks` time,
|
|
|
|
* according to the current target frame rate
|
|
|
|
* of the robot.
|
|
|
|
*/
|
|
|
|
uint32 ticksToFrames(const uint32 ticks) const;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Gets the current game time, in ticks.
|
|
|
|
*/
|
|
|
|
uint32 getTickCount() const;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* The performance timer for the robot.
|
|
|
|
*/
|
|
|
|
DelayTime _delayTime;
|
|
|
|
|
|
|
|
#pragma mark -
|
|
|
|
#pragma mark Audio
|
|
|
|
private:
|
|
|
|
enum {
|
|
|
|
/**
|
|
|
|
* The number of ticks that should elapse
|
|
|
|
* between each AV sync check.
|
|
|
|
*/
|
|
|
|
kAudioSyncCheckInterval = 5 * 60 /* 5 seconds */
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* The status of the audio track of a Robot
|
|
|
|
* animation.
|
|
|
|
*/
|
|
|
|
enum RobotAudioStatus {
|
|
|
|
kRobotAudioReady = 1,
|
|
|
|
kRobotAudioStopped = 2,
|
|
|
|
kRobotAudioPlaying = 3,
|
|
|
|
kRobotAudioPaused = 4,
|
|
|
|
kRobotAudioStopping = 5
|
|
|
|
};
|
|
|
|
|
|
|
|
#pragma mark -
|
|
|
|
#pragma mark Audio - AudioList
|
|
|
|
private:
|
|
|
|
/**
|
|
|
|
* This class manages packetized audio playback
|
|
|
|
* for robots.
|
|
|
|
*/
|
|
|
|
class AudioList {
|
2012-07-22 23:17:36 -04:00
|
|
|
public:
|
2016-07-03 17:57:58 -05:00
|
|
|
AudioList();
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Starts playback of robot audio.
|
|
|
|
*/
|
|
|
|
void startAudioNow();
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Stops playback of robot audio, allowing
|
|
|
|
* any queued audio to finish playing back.
|
|
|
|
*/
|
|
|
|
void stopAudio();
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Stops playback of robot audio immediately.
|
|
|
|
*/
|
|
|
|
void stopAudioNow();
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Submits as many blocks of audio as possible
|
|
|
|
* to the audio engine.
|
|
|
|
*/
|
|
|
|
void submitDriverMax();
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Adds a new AudioBlock to the queue.
|
|
|
|
*
|
|
|
|
* @param position The absolute position of the
|
|
|
|
* audio for the block, in compressed bytes.
|
|
|
|
* @param size The size of the buffer.
|
|
|
|
* @param buffer A pointer to compressed audio
|
|
|
|
* data that will be copied into the new
|
|
|
|
* AudioBlock.
|
|
|
|
*/
|
|
|
|
void addBlock(const int position, const int size, const byte *buffer);
|
2012-07-22 23:17:36 -04:00
|
|
|
|
2016-07-03 17:57:58 -05:00
|
|
|
/**
|
|
|
|
* Immediately stops any active playback and
|
|
|
|
* purges all audio data in the audio list.
|
|
|
|
*/
|
|
|
|
void reset();
|
2012-07-22 23:17:36 -04:00
|
|
|
|
2016-07-03 17:57:58 -05:00
|
|
|
/**
|
|
|
|
* Pauses the robot audio channel in
|
|
|
|
* preparation for the first block of audio
|
|
|
|
* data to be read.
|
|
|
|
*/
|
|
|
|
void prepareForPrimer();
|
2012-07-22 23:17:36 -04:00
|
|
|
|
2016-07-03 17:57:58 -05:00
|
|
|
/**
|
|
|
|
* Sets the audio offset which is used to
|
|
|
|
* offset the position of audio packets
|
|
|
|
* sent to the audio stream.
|
|
|
|
*/
|
|
|
|
void setAudioOffset(const int offset);
|
|
|
|
|
|
|
|
#pragma mark -
|
|
|
|
#pragma mark Audio - AudioList - AudioBlock
|
2012-07-22 23:17:36 -04:00
|
|
|
|
|
|
|
private:
|
2016-07-03 17:57:58 -05:00
|
|
|
/**
|
|
|
|
* AudioBlock represents a block of audio
|
|
|
|
* from the Robot's audio track.
|
|
|
|
*/
|
|
|
|
class AudioBlock {
|
|
|
|
public:
|
|
|
|
AudioBlock(const int position, const int size, const byte *const data);
|
|
|
|
~AudioBlock();
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Submits the block of audio to the
|
|
|
|
* audio manager.
|
|
|
|
* @returns true if the block was fully
|
|
|
|
* read, or false if the block was not
|
|
|
|
* read or only partially read.
|
|
|
|
*/
|
|
|
|
bool submit(const int startOffset);
|
|
|
|
|
|
|
|
private:
|
|
|
|
/**
|
|
|
|
* The absolute position, in compressed
|
|
|
|
* bytes, of this audio block's audio
|
|
|
|
* data in the audio stream.
|
|
|
|
*/
|
|
|
|
int _position;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* The compressed size, in bytes, of
|
|
|
|
* this audio block's audio data.
|
|
|
|
*/
|
|
|
|
int _size;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* A buffer containing raw
|
|
|
|
* SOL-compressed audio data.
|
|
|
|
*/
|
|
|
|
byte *_data;
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* The list of compressed audio blocks
|
|
|
|
* submitted for playback.
|
|
|
|
*/
|
|
|
|
AudioBlock *_blocks[kAudioListSize];
|
|
|
|
|
|
|
|
/**
|
|
|
|
* The number of blocks in `_blocks` that are
|
|
|
|
* ready to be submitted.
|
|
|
|
*/
|
|
|
|
uint8 _blocksSize;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* The index of the oldest submitted audio block.
|
|
|
|
*/
|
|
|
|
uint8 _oldestBlockIndex;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* The index of the newest submitted audio block.
|
|
|
|
*/
|
|
|
|
uint8 _newestBlockIndex;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* The offset used when sending packets to the
|
|
|
|
* audio stream.
|
|
|
|
*/
|
|
|
|
int _startOffset;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* The status of robot audio playback.
|
|
|
|
*/
|
|
|
|
RobotAudioStatus _status;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Frees all audio blocks in the `_blocks` list.
|
|
|
|
*/
|
|
|
|
void freeAudioBlocks();
|
2012-07-22 23:17:36 -04:00
|
|
|
};
|
|
|
|
|
2016-07-03 17:57:58 -05:00
|
|
|
/**
|
|
|
|
* Whether or not this robot animation has
|
|
|
|
* an audio track.
|
|
|
|
*/
|
|
|
|
bool _hasAudio;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* The audio list for the current robot.
|
|
|
|
*/
|
|
|
|
AudioList _audioList;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* The size, in bytes, of a block of audio data,
|
|
|
|
* excluding the audio block header.
|
|
|
|
*/
|
|
|
|
uint16 _audioBlockSize;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* The expected size of a block of audio data,
|
|
|
|
* in bytes, excluding the audio block header.
|
|
|
|
*/
|
|
|
|
int16 _expectedAudioBlockSize;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* The number of compressed audio bytes that are
|
|
|
|
* needed per frame to fill the audio buffer
|
|
|
|
* without causing audio to drop out.
|
|
|
|
*/
|
|
|
|
int16 _audioRecordInterval;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* If true, primer audio buffers should be filled
|
|
|
|
* with silence instead of trying to read buffers
|
|
|
|
* from the Robot data.
|
|
|
|
*/
|
|
|
|
uint16 _primerZeroCompressFlag;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* The size, in bytes, of the primer audio in the
|
|
|
|
* Robot, including any extra alignment padding.
|
|
|
|
*/
|
|
|
|
uint16 _primerReservedSize;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* The combined size, in bytes, of the even and odd
|
|
|
|
* primer channels.
|
|
|
|
*/
|
|
|
|
int32 _totalPrimerSize;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* The absolute offset of the primer audio data in
|
|
|
|
* the robot data stream.
|
|
|
|
*/
|
|
|
|
int32 _primerPosition;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* The size, in bytes, of the even primer.
|
|
|
|
*/
|
|
|
|
int32 _evenPrimerSize;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* The size, in bytes, of the odd primer.
|
|
|
|
*/
|
|
|
|
int32 _oddPrimerSize;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* The absolute position in the audio stream of
|
|
|
|
* the first audio packet.
|
|
|
|
*/
|
|
|
|
int32 _firstAudioRecordPosition;
|
2010-01-06 21:56:31 +00:00
|
|
|
|
2016-07-03 17:57:58 -05:00
|
|
|
/**
|
|
|
|
* A temporary buffer used to hold one frame of
|
|
|
|
* raw (DPCM-compressed) audio when reading audio
|
|
|
|
* records from the robot stream.
|
|
|
|
*/
|
|
|
|
byte *_audioBuffer;
|
2010-01-06 21:56:31 +00:00
|
|
|
|
2016-07-03 17:57:58 -05:00
|
|
|
/**
|
|
|
|
* The next tick count when AV sync should be
|
|
|
|
* checked and framerate adjustments made, if
|
|
|
|
* necessary.
|
|
|
|
*/
|
|
|
|
uint32 _checkAudioSyncTime;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Primes the audio buffer with the first frame
|
|
|
|
* of audio data.
|
|
|
|
*
|
|
|
|
* @note `primeAudio` was `InitAudio` in SSCI
|
|
|
|
*/
|
|
|
|
bool primeAudio(const uint32 startTick);
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Reads primer data from the robot data stream
|
|
|
|
* and puts it into the given buffers.
|
|
|
|
*/
|
|
|
|
bool readPrimerData(byte *outEvenBuffer, byte *outOddBuffer);
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Reads audio data for the given frame number
|
|
|
|
* into the given buffer.
|
|
|
|
*
|
|
|
|
* @param outAudioPosition The position of the
|
|
|
|
* audio, in compressed bytes, in the data stream.
|
|
|
|
* @param outAudioSize The size of the audio data,
|
|
|
|
* in compressed bytes.
|
|
|
|
*/
|
|
|
|
bool readAudioDataFromRecord(const int frameNo, byte *outBuffer, int &outAudioPosition, int &outAudioSize);
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Submits part of the audio packet of the given
|
|
|
|
* frame to the audio list, starting `startPosition`
|
|
|
|
* bytes into the audio.
|
|
|
|
*/
|
|
|
|
bool readPartialAudioRecordAndSubmit(const int startFrame, const int startPosition);
|
|
|
|
|
|
|
|
#pragma mark -
|
|
|
|
#pragma mark Rendering
|
|
|
|
public:
|
|
|
|
/**
|
|
|
|
* Puts the current dimensions of the robot, in game script
|
|
|
|
* coordinates, into the given rect, and returns the total
|
|
|
|
* number of frames in the robot animation.
|
|
|
|
*/
|
|
|
|
uint16 getFrameSize(Common::Rect &outRect) const;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Pumps the robot player for the next frame of video.
|
|
|
|
* This is the main rendering function.
|
|
|
|
*/
|
|
|
|
void doRobot();
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Submits any outstanding audio blocks that should
|
|
|
|
* be added to the queue before the robot frame
|
|
|
|
* becomes visible.
|
|
|
|
*/
|
|
|
|
void frameAlmostVisible();
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Evaluates frame drift and makes modifications to
|
|
|
|
* the player in order to ensure that future frames
|
|
|
|
* will arrive on time.
|
|
|
|
*/
|
|
|
|
void frameNowVisible();
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Scales a vertically compressed cel to its original
|
|
|
|
* uncompressed dimensions.
|
|
|
|
*/
|
|
|
|
void expandCel(byte *target, const byte* source, const int16 celWidth, const int16 celHeight) const;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Sets the visual priority of the robot.
|
|
|
|
* @see Plane::_priority
|
|
|
|
*/
|
|
|
|
void setPriority(const int16 newPriority);
|
|
|
|
|
|
|
|
private:
|
|
|
|
enum CompressionType {
|
|
|
|
kCompressionLZS = 0,
|
|
|
|
kCompressionNone = 2
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Describes the state of a Robot video cel.
|
|
|
|
*/
|
|
|
|
struct CelHandleInfo {
|
|
|
|
/**
|
|
|
|
* The persistence level of Robot cels.
|
|
|
|
*/
|
|
|
|
enum CelHandleLifetime {
|
|
|
|
kNoCel = 0,
|
|
|
|
kFrameLifetime = 1,
|
|
|
|
kRobotLifetime = 2
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* A reg_t pointer to an in-memory
|
|
|
|
* bitmap containing the cel.
|
|
|
|
*/
|
|
|
|
reg_t bitmapId;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* The lifetime of the cel, either just
|
|
|
|
* for this frame or for the entire
|
|
|
|
* duration of the robot playback.
|
|
|
|
*/
|
|
|
|
CelHandleLifetime status;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* The size, in pixels, of the decompressed
|
|
|
|
* cel.
|
|
|
|
*/
|
|
|
|
int area;
|
|
|
|
|
|
|
|
CelHandleInfo() : bitmapId(NULL_REG), status(kNoCel), area(0) {}
|
|
|
|
};
|
|
|
|
|
|
|
|
typedef Common::Array<ScreenItem *> RobotScreenItemList;
|
|
|
|
typedef Common::Array<CelHandleInfo> CelHandleList;
|
|
|
|
typedef Common::Array<int> VideoSizeList;
|
|
|
|
typedef Common::Array<uint> MaxCelAreaList;
|
|
|
|
typedef Common::Array<reg_t> FixedCelsList;
|
|
|
|
typedef Common::Array<Common::Point> CelPositionsList;
|
|
|
|
typedef Common::Array<byte> ScratchMemory;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Renders a version 5/6 robot frame.
|
|
|
|
*/
|
|
|
|
void doVersion5(const bool shouldSubmitAudio = true);
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Creates screen items for a version 5/6 robot.
|
|
|
|
*/
|
|
|
|
void createCels5(const byte *rawVideoData, const int16 numCels, const bool usePalette);
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Creates a single screen item for a cel in a
|
|
|
|
* version 5/6 robot.
|
|
|
|
*
|
|
|
|
* Returns the size, in bytes, of the raw cel data.
|
|
|
|
*/
|
|
|
|
uint32 createCel5(const byte *rawVideoData, const int16 screenItemIndex, const bool usePalette);
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Preallocates memory for the next `numCels` cels
|
|
|
|
* in the robot data stream.
|
|
|
|
*/
|
|
|
|
void preallocateCelMemory(const byte *rawVideoData, const int16 numCels);
|
|
|
|
|
|
|
|
/**
|
|
|
|
* The decompressor for LZS-compressed cels.
|
|
|
|
*/
|
|
|
|
DecompressorLZS _decompressor;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* The origin of the robot animation, in screen
|
|
|
|
* coordinates.
|
|
|
|
*/
|
|
|
|
Common::Point _position;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Global scaling applied to the robot.
|
|
|
|
*/
|
|
|
|
ScaleInfo _scaleInfo;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* The native resolution of the robot.
|
|
|
|
*/
|
|
|
|
int16 _xResolution, _yResolution;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Whether or not the coordinates read from robot
|
|
|
|
* data are high resolution.
|
|
|
|
*/
|
|
|
|
bool _isHiRes;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* The maximum number of cels that will be rendered
|
|
|
|
* on any given frame in this robot. Used for
|
|
|
|
* preallocation of cel memory.
|
|
|
|
*/
|
|
|
|
int16 _maxCelsPerFrame;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* The maximum areas, in pixels, for each of
|
|
|
|
* the fixed cels in the robot. Used for
|
|
|
|
* preallocation of cel memory.
|
|
|
|
*/
|
|
|
|
MaxCelAreaList _maxCelArea;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* The hunk palette to use when rendering the
|
|
|
|
* current frame, if the `usePalette` flag was set
|
|
|
|
* in the robot header.
|
|
|
|
*/
|
|
|
|
uint8 *_rawPalette;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* A list of the raw video data sizes, in bytes,
|
|
|
|
* for each frame of the robot.
|
|
|
|
*/
|
|
|
|
VideoSizeList _videoSizes;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* A list of cels that will be present for the
|
|
|
|
* entire duration of the robot animation.
|
|
|
|
*/
|
|
|
|
FixedCelsList _fixedCels;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* A list of handles for each cel in the current
|
|
|
|
* frame.
|
|
|
|
*/
|
|
|
|
CelHandleList _celHandles;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Scratch memory used to temporarily store
|
|
|
|
* decompressed cel data for vertically squashed
|
|
|
|
* cels.
|
|
|
|
*/
|
|
|
|
ScratchMemory _celDecompressionBuffer;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* The size, in bytes, of the squashed cel
|
|
|
|
* decompression buffer.
|
|
|
|
*/
|
|
|
|
int _celDecompressionArea;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* If true, the robot just started playing and
|
|
|
|
* is awaiting output for the first frame.
|
|
|
|
*/
|
|
|
|
bool _syncFrame;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Scratch memory used to store the compressed robot
|
|
|
|
* video data for the current frame.
|
|
|
|
*/
|
|
|
|
ScratchMemory _doVersion5Scratch;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* When set to a non-negative value, forces the next
|
|
|
|
* call to doRobot to render the given frame number
|
|
|
|
* instead of whatever frame would have normally been
|
|
|
|
* rendered.
|
|
|
|
*/
|
|
|
|
mutable int _cueForceShowFrame;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* The plane where the robot animation will be drawn.
|
|
|
|
*/
|
|
|
|
Plane *_plane;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* A list of pointers to ScreenItems used by the robot.
|
|
|
|
*/
|
|
|
|
RobotScreenItemList _screenItemList;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* The positions of the various screen items in this
|
|
|
|
* robot, in screen coordinates.
|
|
|
|
*/
|
|
|
|
Common::Array<int16> _screenItemX, _screenItemY;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* The raw position values from the cel header for
|
|
|
|
* each screen item currently on-screen.
|
|
|
|
*/
|
|
|
|
Common::Array<int16> _originalScreenItemX, _originalScreenItemY;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* The duration of the current robot, in frames.
|
|
|
|
*/
|
|
|
|
uint16 _numFramesTotal;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* The screen priority of the video.
|
|
|
|
* @see ScreenItem::_priority
|
|
|
|
*/
|
|
|
|
int16 _priority;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* The amount of visual vertical compression applied
|
|
|
|
* to the current cel. A value of 100 means no
|
|
|
|
* compression; a value above 100 indicates how much
|
|
|
|
* the cel needs to be scaled along the y-axis to
|
|
|
|
* return to its original dimensions.
|
|
|
|
*/
|
|
|
|
uint8 _verticalScaleFactor;
|
|
|
|
};
|
|
|
|
} // end of namespace Sci
|
2010-01-06 21:56:31 +00:00
|
|
|
#endif
|