/* 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. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * */ #ifndef SCI_SOUND_DECODERS_ROBOT_H #define SCI_SOUND_DECODERS_ROBOT_H #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... namespace Common { class SeekableSubReadStreamEndian; } namespace Sci { class Plane; class SegManager; // There were 3 different Robot video versions, used in the following games: // - v4: PQ:SWAT demo // - v5: KQ7 DOS, Phantasmagoria, PQ:SWAT, Lighthouse // - v6: RAMA // // 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). #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. */ class RobotDecoder { public: RobotDecoder(SegManager *segMan); ~RobotDecoder(); GuiResourceId getResourceId() const { return _robotId; } private: SegManager *_segMan; /** * The ID of the currently loaded robot. */ GuiResourceId _robotId; #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, /** * 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 public: /** * 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); /** * Closes the currently open robot file. */ void close(); /** * 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 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); /** * Sets the start time and frame of the robot * when the robot is started or resumed. */ void setRobotTime(const int frameNo); #pragma mark - #pragma mark Timing private: /** * 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 { public: 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; private: 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(); }; /** * 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 { public: 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); /** * Immediately stops any active playback and * purges all audio data in the audio list. */ void reset(); /** * Pauses the robot audio channel in * preparation for the first block of audio * data to be read. */ void prepareForPrimer(); /** * 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 private: /** * 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(); }; /** * 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; /** * A temporary buffer used to hold one frame of * raw (DPCM-compressed) audio when reading audio * records from the robot stream. */ byte *_audioBuffer; /** * 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: /** * Gets the plane used to render the robot. */ const reg_t getPlaneId() const { return _planeId; } /** * Gets the origin of the robot. */ Common::Point getPosition() const { return _position; } /** * Gets the scale of the robot. */ int16 getScale() const { return _scaleInfo.x; } /** * 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; int16 getPriority() 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 RobotScreenItemList; typedef Common::Array CelHandleList; typedef Common::Array VideoSizeList; typedef Common::Array MaxCelAreaList; typedef Common::Array FixedCelsList; typedef Common::Array CelPositionsList; typedef Common::Array 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 ID of the robot plane. */ reg_t _planeId; /** * 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 _screenItemX, _screenItemY; /** * The raw position values from the cel header for * each screen item currently on-screen. */ Common::Array _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 #endif