mirror of
https://github.com/libretro/scummvm.git
synced 2025-02-07 11:27:44 +00:00
![Torbjörn Andersson](/assets/img/avatar_default.png)
console. Some things are likely to have broken. For instance, I believe the old console was could be visible during gameplay, while ScummVM's is modal. So any place where we output something to the console during gameplay should probably be removed. Some things I've stubbed out. Basically any code that registers a variable. Most of the debugging commands are untested. Syntax may have changed because of different command-line parsing. (I never actually used the old console, so I don't know for sure. :-) Actually, I'm not that interested in reproducing the old console commands faithfully. What we should do now is to make the immediately useful console stuff work. Anything that remains unimplemented should probably be removed. svn-id: r15976
929 lines
20 KiB
C++
929 lines
20 KiB
C++
/* ScummVM - Scumm Interpreter
|
|
* Copyright (C) 2004 The ScummVM project
|
|
*
|
|
* The ReInherit Engine is (C)2000-2003 by Daniel Balsom.
|
|
*
|
|
* 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$
|
|
*
|
|
*/
|
|
|
|
// Background animation management module
|
|
#include "saga/saga.h"
|
|
#include "saga/gfx.h"
|
|
|
|
#include "saga/console.h"
|
|
#include "saga/game_mod.h"
|
|
#include "saga/events.h"
|
|
#include "saga/render.h"
|
|
|
|
#include "saga/animation.h"
|
|
|
|
namespace Saga {
|
|
|
|
Anim::Anim(SagaEngine *vm) : _vm(vm) {
|
|
int i;
|
|
|
|
_anim_limit = MAX_ANIMATIONS;
|
|
_anim_count = 0;
|
|
|
|
for (i = 0; i < MAX_ANIMATIONS; i++)
|
|
_anim_tbl[i] = NULL;
|
|
|
|
_initialized = true;
|
|
}
|
|
|
|
Anim::~Anim(void) {
|
|
uint16 i;
|
|
|
|
for (i = 0; i < MAX_ANIMATIONS; i++)
|
|
free(_anim_tbl[i]);
|
|
|
|
_initialized = false;
|
|
}
|
|
|
|
int Anim::load(const byte *anim_resdata, size_t anim_resdata_len, uint16 *anim_id_p) {
|
|
ANIMATION *new_anim;
|
|
|
|
uint16 anim_id = 0;
|
|
uint16 i;
|
|
|
|
if (!_initialized) {
|
|
warning("Anim::load not initialised");
|
|
return FAILURE;
|
|
}
|
|
|
|
// Find an unused animation slot
|
|
for (i = 0; i < MAX_ANIMATIONS; i++) {
|
|
if (_anim_tbl[i] == NULL) {
|
|
anim_id = i;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (i == MAX_ANIMATIONS) {
|
|
warning("Anim::load could not find unused animation slot");
|
|
return FAILURE;
|
|
}
|
|
|
|
new_anim = (ANIMATION *)malloc(sizeof *new_anim);
|
|
if (new_anim == NULL) {
|
|
warning("Anim::load Allocation failure");
|
|
return MEM;
|
|
}
|
|
|
|
new_anim->resdata = anim_resdata;
|
|
new_anim->resdata_len = anim_resdata_len;
|
|
|
|
if (GAME_GetGameType() == GID_ITE) {
|
|
if (getNumFrames(anim_resdata, anim_resdata_len, &new_anim->n_frames) != SUCCESS) {
|
|
warning("Anim::load Couldn't get animation frame count");
|
|
return FAILURE;
|
|
}
|
|
|
|
// Cache frame offsets
|
|
new_anim->frame_offsets = (size_t *)malloc(new_anim->n_frames * sizeof *new_anim->frame_offsets);
|
|
if (new_anim->frame_offsets == NULL) {
|
|
warning("Anim::load Allocation failure");
|
|
return MEM;
|
|
}
|
|
|
|
for (i = 0; i < new_anim->n_frames; i++) {
|
|
getFrameOffset(anim_resdata, anim_resdata_len, i + 1, &new_anim->frame_offsets[i]);
|
|
}
|
|
} else {
|
|
new_anim->cur_frame_p = anim_resdata + SAGA_FRAME_HEADER_LEN;
|
|
new_anim->cur_frame_len = anim_resdata_len - SAGA_FRAME_HEADER_LEN;
|
|
getNumFrames(anim_resdata, anim_resdata_len, &new_anim->n_frames);
|
|
}
|
|
|
|
// Set animation data
|
|
new_anim->current_frame = 1;
|
|
new_anim->end_frame = new_anim->n_frames;
|
|
new_anim->stop_frame = new_anim->end_frame;
|
|
|
|
new_anim->frame_time = DEFAULT_FRAME_TIME;
|
|
new_anim->flags = 0;
|
|
new_anim->play_flag = 0;
|
|
new_anim->link_flag = 0;
|
|
new_anim->link_id = 0;
|
|
|
|
_anim_tbl[anim_id] = new_anim;
|
|
|
|
*anim_id_p = anim_id;
|
|
|
|
_anim_count++;
|
|
|
|
return SUCCESS;
|
|
}
|
|
|
|
int Anim::link(uint16 anim_id1, uint16 anim_id2) {
|
|
ANIMATION *anim1;
|
|
ANIMATION *anim2;
|
|
|
|
if ((anim_id1 >= _anim_count) || (anim_id2 >= _anim_count)) {
|
|
return FAILURE;
|
|
}
|
|
|
|
anim1 = _anim_tbl[anim_id1];
|
|
anim2 = _anim_tbl[anim_id2];
|
|
|
|
if ((anim1 == NULL) || (anim2 == NULL)) {
|
|
return FAILURE;
|
|
}
|
|
|
|
anim1->link_id = anim_id2;
|
|
anim1->link_flag = 1;
|
|
|
|
anim2->frame_time = anim1->frame_time;
|
|
|
|
return SUCCESS;
|
|
}
|
|
|
|
int Anim::play(uint16 anim_id, int vector_time) {
|
|
EVENT event;
|
|
ANIMATION *anim;
|
|
ANIMATION *link_anim;
|
|
uint16 link_anim_id;
|
|
|
|
BUFFER_INFO buf_info;
|
|
|
|
byte *display_buf;
|
|
|
|
const byte *nextf_p;
|
|
size_t nextf_len;
|
|
|
|
uint16 frame;
|
|
int result;
|
|
|
|
GAME_DISPLAYINFO disp_info;
|
|
|
|
if (anim_id >= _anim_count) {
|
|
return FAILURE;
|
|
}
|
|
|
|
GAME_GetDisplayInfo(&disp_info);
|
|
|
|
_vm->_render->getBufferInfo(&buf_info);
|
|
display_buf = buf_info.bg_buf;
|
|
|
|
anim = _anim_tbl[anim_id];
|
|
if (anim == NULL) {
|
|
return FAILURE;
|
|
}
|
|
|
|
if (anim->flags & ANIM_PAUSE)
|
|
return SUCCESS;
|
|
|
|
if (anim->play_flag) {
|
|
frame = anim->current_frame;
|
|
if (GAME_GetGameType() == GID_ITE) {
|
|
result = ITE_DecodeFrame(anim->resdata, anim->resdata_len, anim->frame_offsets[frame - 1], display_buf,
|
|
disp_info.logical_w * disp_info.logical_h);
|
|
if (result != SUCCESS) {
|
|
warning("ANIM::play: Error decoding frame %u", anim->current_frame);
|
|
anim->play_flag = 0;
|
|
return FAILURE;
|
|
}
|
|
} else {
|
|
if (anim->cur_frame_p == NULL) {
|
|
warning("ANIM::play: Frames exhausted");
|
|
return FAILURE;
|
|
}
|
|
|
|
result = IHNM_DecodeFrame(display_buf, disp_info.logical_w * disp_info.logical_h,
|
|
anim->cur_frame_p, anim->cur_frame_len, &nextf_p, &nextf_len);
|
|
if (result != SUCCESS) {
|
|
warning("ANIM::play: Error decoding frame %u", anim->current_frame);
|
|
anim->play_flag = 0;
|
|
return FAILURE;
|
|
}
|
|
|
|
anim->cur_frame_p = nextf_p;
|
|
anim->cur_frame_len = nextf_len;
|
|
}
|
|
anim->current_frame++;
|
|
}
|
|
|
|
anim->play_flag = 1;
|
|
|
|
if (anim->current_frame > anim->n_frames) {
|
|
// Animation done playing
|
|
if (anim->link_flag) {
|
|
// If this animation has a link, follow it
|
|
anim->play_flag = 0;
|
|
anim->current_frame = 1;
|
|
|
|
link_anim_id = anim->link_id;
|
|
link_anim = _anim_tbl[link_anim_id];
|
|
|
|
if (link_anim != NULL) {
|
|
link_anim->current_frame = 1;
|
|
link_anim->play_flag = 1;
|
|
}
|
|
|
|
anim_id = link_anim_id;
|
|
} else if (anim->flags & ANIM_LOOP) {
|
|
// Loop animation
|
|
anim->current_frame = 1;
|
|
anim->cur_frame_p = anim->resdata + SAGA_FRAME_HEADER_LEN;
|
|
anim->cur_frame_len = anim->resdata_len - SAGA_FRAME_HEADER_LEN;
|
|
} else {
|
|
// No link, stop playing
|
|
anim->current_frame = anim->n_frames;
|
|
anim->play_flag = 0;
|
|
|
|
if (anim->flags & ANIM_ENDSCENE) {
|
|
// This animation ends the scene
|
|
event.type = ONESHOT_EVENT;
|
|
event.code = SCENE_EVENT;
|
|
event.op = EVENT_END;
|
|
event.time = anim->frame_time + vector_time;
|
|
_vm->_events->queue(&event);
|
|
}
|
|
return SUCCESS;
|
|
}
|
|
}
|
|
|
|
event.type = ONESHOT_EVENT;
|
|
event.code = ANIM_EVENT;
|
|
event.op = EVENT_FRAME;
|
|
event.param = anim_id;
|
|
event.time = anim->frame_time + vector_time;
|
|
|
|
_vm->_events->queue(&event);
|
|
|
|
return SUCCESS;
|
|
}
|
|
|
|
int Anim::reset() {
|
|
uint16 i;
|
|
|
|
for (i = 0; i < MAX_ANIMATIONS; i++) {
|
|
|
|
freeId(i);
|
|
}
|
|
|
|
_anim_count = 0;
|
|
|
|
return SUCCESS;
|
|
}
|
|
|
|
int Anim::setFlag(uint16 anim_id, uint16 flag) {
|
|
ANIMATION *anim;
|
|
|
|
if (anim_id > _anim_count) {
|
|
return FAILURE;
|
|
}
|
|
|
|
anim = _anim_tbl[anim_id];
|
|
if (anim == NULL) {
|
|
return FAILURE;
|
|
}
|
|
|
|
anim->flags |= flag;
|
|
|
|
return SUCCESS;
|
|
}
|
|
|
|
int Anim::clearFlag(uint16 anim_id, uint16 flag) {
|
|
ANIMATION *anim;
|
|
|
|
if (anim_id > _anim_count) {
|
|
return FAILURE;
|
|
}
|
|
|
|
anim = _anim_tbl[anim_id];
|
|
if (anim == NULL) {
|
|
return FAILURE;
|
|
}
|
|
|
|
anim->flags &= ~flag;
|
|
|
|
return SUCCESS;
|
|
}
|
|
|
|
int Anim::setFrameTime(uint16 anim_id, int time) {
|
|
ANIMATION *anim;
|
|
|
|
if (anim_id > _anim_count) {
|
|
return FAILURE;
|
|
}
|
|
|
|
anim = _anim_tbl[anim_id];
|
|
if (anim == NULL) {
|
|
return FAILURE;
|
|
}
|
|
|
|
anim->frame_time = time;
|
|
|
|
return SUCCESS;
|
|
}
|
|
|
|
int Anim::freeId(uint16 anim_id) {
|
|
ANIMATION *anim;
|
|
|
|
if (anim_id > _anim_count) {
|
|
return FAILURE;
|
|
}
|
|
|
|
anim = _anim_tbl[anim_id];
|
|
if (anim == NULL) {
|
|
return FAILURE;
|
|
}
|
|
|
|
if (GAME_GetGameType() == GID_ITE) {
|
|
free(anim->frame_offsets);
|
|
anim->frame_offsets = NULL;
|
|
}
|
|
|
|
free(anim);
|
|
_anim_tbl[anim_id] = NULL;
|
|
_anim_count--;
|
|
|
|
return SUCCESS;
|
|
}
|
|
|
|
// The actual number of frames present in an animation resource is
|
|
// sometimes less than number present in the .nframes member of the
|
|
// animation header. For this reason, the function attempts to find
|
|
// the last valid frame number, which it returns via 'n_frames'
|
|
int Anim::getNumFrames(const byte *anim_resource, size_t anim_resource_len, uint16 *n_frames) {
|
|
ANIMATION_HEADER ah;
|
|
|
|
size_t offset;
|
|
int magic;
|
|
|
|
int x;
|
|
|
|
if (!_initialized) {
|
|
return FAILURE;
|
|
}
|
|
|
|
MemoryReadStream readS(anim_resource, anim_resource_len);
|
|
|
|
ah.magic = readS.readUint16LE();
|
|
ah.screen_w = readS.readUint16LE();
|
|
ah.screen_h = readS.readUint16LE();
|
|
|
|
ah.unknown06 = readS.readByte();
|
|
ah.unknown07 = readS.readByte();
|
|
ah.nframes = readS.readByte();
|
|
|
|
if (GAME_GetGameType() == GID_IHNM) {
|
|
*n_frames = ah.nframes;
|
|
}
|
|
|
|
if (ah.magic == 68) {
|
|
for (x = ah.nframes; x > 0; x--) {
|
|
if (getFrameOffset(anim_resource, anim_resource_len, x, &offset) != SUCCESS) {
|
|
return FAILURE;
|
|
}
|
|
|
|
magic = *(anim_resource + offset);
|
|
if (magic == SAGA_FRAME_HEADER_MAGIC) {
|
|
*n_frames = x;
|
|
return SUCCESS;
|
|
}
|
|
}
|
|
|
|
return FAILURE;
|
|
}
|
|
|
|
return FAILURE;
|
|
}
|
|
|
|
int Anim::ITE_DecodeFrame(const byte *resdata, size_t resdata_len, size_t frame_offset, byte *buf, size_t buf_len) {
|
|
ANIMATION_HEADER ah;
|
|
FRAME_HEADER fh;
|
|
|
|
byte *write_p;
|
|
|
|
uint16 magic;
|
|
|
|
uint16 x_start;
|
|
uint16 y_start;
|
|
uint32 screen_w;
|
|
uint32 screen_h;
|
|
|
|
int mark_byte;
|
|
byte data_byte;
|
|
int new_row;
|
|
|
|
uint16 control_ch;
|
|
uint16 param_ch;
|
|
|
|
uint16 runcount;
|
|
int x_vector;
|
|
|
|
uint16 i;
|
|
|
|
if (!_initialized) {
|
|
return FAILURE;
|
|
}
|
|
|
|
MemoryReadStream headerReadS(resdata, resdata_len);
|
|
// Read animation header
|
|
ah.magic = headerReadS.readUint16LE();
|
|
ah.screen_w = headerReadS.readUint16LE();
|
|
ah.screen_h = headerReadS.readUint16LE();
|
|
ah.unknown06 = headerReadS.readByte();
|
|
ah.unknown07 = headerReadS.readByte();
|
|
ah.nframes = headerReadS.readByte();
|
|
ah.flags = headerReadS.readByte();
|
|
ah.unknown10 = headerReadS.readByte();
|
|
ah.unknown11 = headerReadS.readByte();
|
|
|
|
screen_w = ah.screen_w;
|
|
screen_h = ah.screen_h;
|
|
|
|
if ((screen_w * screen_h) > buf_len) {
|
|
// Buffer argument is too small to hold decoded frame, abort.
|
|
warning("ITE_DecodeFrame: Buffer size inadequate");
|
|
return FAILURE;
|
|
}
|
|
|
|
// Read frame header
|
|
MemoryReadStream readS(resdata + frame_offset, resdata_len - frame_offset);
|
|
|
|
// Check for frame magic byte
|
|
magic = readS.readByte();
|
|
if (magic != SAGA_FRAME_HEADER_MAGIC) {
|
|
warning("ITE_DecodeFrame: Invalid frame offset");
|
|
return FAILURE;
|
|
}
|
|
|
|
// For some strange reason, the animation header is in little
|
|
// endian format, but the actual RLE encoded frame data,
|
|
// including the frame header, is in big endian format.
|
|
|
|
fh.x_start = readS.readUint16BE();
|
|
fh.y_start = readS.readByte();
|
|
readS.readByte(); /* Skip pad byte */
|
|
fh.x_pos = readS.readUint16BE();
|
|
fh.y_pos = readS.readUint16BE();
|
|
fh.width = readS.readUint16BE();
|
|
fh.height = readS.readUint16BE();
|
|
|
|
x_start = fh.x_start;
|
|
y_start = fh.y_start;
|
|
|
|
// Setup write pointer to the draw origin
|
|
write_p = (buf + (y_start * screen_w) + x_start);
|
|
|
|
// Begin RLE decompression to output buffer
|
|
do {
|
|
mark_byte = readS.readByte();
|
|
switch (mark_byte) {
|
|
case 0x10: // Long Unencoded Run
|
|
runcount = readS.readSint16BE();
|
|
for (i = 0; i < runcount; i++) {
|
|
data_byte = readS.readByte();
|
|
if (data_byte != 0) {
|
|
*write_p = data_byte;
|
|
}
|
|
write_p++;
|
|
}
|
|
continue;
|
|
break;
|
|
case 0x20: // Long encoded run
|
|
runcount = readS.readSint16BE();
|
|
data_byte = readS.readByte();
|
|
for (i = 0; i < runcount; i++) {
|
|
*write_p++ = data_byte;
|
|
}
|
|
continue;
|
|
break;
|
|
case 0x2F: // End of row
|
|
x_vector = readS.readSint16BE();
|
|
new_row = readS.readByte();
|
|
// Set write pointer to the new draw origin
|
|
write_p = buf + ((y_start + new_row) * screen_w) + x_start + x_vector;
|
|
continue;
|
|
break;
|
|
case 0x30: // Reposition command
|
|
x_vector = readS.readSint16BE();
|
|
write_p += x_vector;
|
|
continue;
|
|
break;
|
|
case 0x3F: // End of frame marker
|
|
return SUCCESS;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
// Mask all but two high order control bits
|
|
control_ch = mark_byte & 0xC0U;
|
|
param_ch = mark_byte & 0x3FU;
|
|
switch (control_ch) {
|
|
case 0xC0: // 1100 0000
|
|
// Run of empty pixels
|
|
runcount = param_ch + 1;
|
|
write_p += runcount;
|
|
continue;
|
|
break;
|
|
case 0x80: // 1000 0000
|
|
// Run of compressed data
|
|
runcount = param_ch + 1;
|
|
data_byte = readS.readByte();
|
|
for (i = 0; i < runcount; i++) {
|
|
*write_p++ = data_byte;
|
|
}
|
|
continue;
|
|
break;
|
|
case 0x40: // 0100 0000
|
|
// Uncompressed run
|
|
runcount = param_ch + 1;
|
|
for (i = 0; i < runcount; i++) {
|
|
data_byte = readS.readByte();
|
|
if (data_byte != 0) {
|
|
*write_p = data_byte;
|
|
}
|
|
write_p++;
|
|
}
|
|
continue;
|
|
break;
|
|
default:
|
|
// Unknown marker found - abort
|
|
warning("ITE_DecodeFrame: Invalid RLE marker encountered");
|
|
return FAILURE;
|
|
break;
|
|
}
|
|
} while (mark_byte != 63); // end of frame marker
|
|
|
|
return SUCCESS;
|
|
}
|
|
|
|
int Anim::IHNM_DecodeFrame(byte *decode_buf, size_t decode_buf_len, const byte *thisf_p,
|
|
size_t thisf_len, const byte **nextf_p, size_t *nextf_len) {
|
|
int in_ch;
|
|
int decoded_data = 0;
|
|
int cont_flag = 1;
|
|
int control_ch;
|
|
int param_ch;
|
|
byte data_pixel;
|
|
int x_origin = 0;
|
|
int y_origin = 0;
|
|
int x_vector;
|
|
int new_row;
|
|
|
|
uint16 runcount;
|
|
uint16 c;
|
|
|
|
size_t in_ch_offset;
|
|
|
|
MemoryReadStream readS(thisf_p, thisf_len);
|
|
|
|
byte *outbuf_p = decode_buf;
|
|
byte *outbuf_endp = (decode_buf + decode_buf_len) - 1;
|
|
size_t outbuf_remain = decode_buf_len;
|
|
|
|
GAME_DISPLAYINFO di;
|
|
|
|
GAME_GetDisplayInfo(&di);
|
|
|
|
*nextf_p = NULL;
|
|
|
|
for (; cont_flag; decoded_data = 1) {
|
|
in_ch_offset = readS.pos();
|
|
in_ch = readS.readByte();
|
|
switch (in_ch) {
|
|
case 0x0F: // 15: Frame header
|
|
{
|
|
int param1;
|
|
int param2;
|
|
int param3;
|
|
int param4;
|
|
int param5;
|
|
int param6;
|
|
|
|
if (thisf_len - readS.pos() < 13) {
|
|
warning("0x%02X: Input buffer underrun", in_ch);
|
|
return FAILURE;
|
|
}
|
|
|
|
param1 = readS.readUint16BE();
|
|
param2 = readS.readUint16BE();
|
|
readS.readByte(); // skip 1?
|
|
param3 = readS.readUint16BE();
|
|
param4 = readS.readUint16BE();
|
|
param5 = readS.readUint16BE();
|
|
param6 = readS.readUint16BE();
|
|
|
|
x_origin = param1;
|
|
y_origin = param2;
|
|
|
|
outbuf_p = decode_buf + x_origin + (y_origin * di.logical_w);
|
|
|
|
if (outbuf_p > outbuf_endp) {
|
|
warning("0x%02X: (0x%X) Invalid output position. (x: %d, y: %d)",
|
|
in_ch, in_ch_offset, x_origin, y_origin);
|
|
return FAILURE;
|
|
}
|
|
|
|
outbuf_remain = (outbuf_endp - outbuf_p) + 1;
|
|
continue;
|
|
}
|
|
break;
|
|
case 0x10: // Long Unencoded Run
|
|
runcount = readS.readSint16BE();
|
|
if (thisf_len - readS.pos() < runcount) {
|
|
warning("0x%02X: Input buffer underrun", in_ch);
|
|
return FAILURE;
|
|
}
|
|
if (outbuf_remain < runcount) {
|
|
warning("0x%02X: Output buffer overrun", in_ch);
|
|
return FAILURE;
|
|
}
|
|
|
|
for (c = 0; c < runcount; c++) {
|
|
data_pixel = readS.readByte();
|
|
if (data_pixel != 0) {
|
|
*outbuf_p = data_pixel;
|
|
}
|
|
outbuf_p++;
|
|
}
|
|
|
|
outbuf_remain -= runcount;
|
|
continue;
|
|
break;
|
|
case 0x1F: // 31: Unusued?
|
|
if (thisf_len - readS.pos() < 3) {
|
|
warning("0x%02X: Input buffer underrun", in_ch);
|
|
return FAILURE;
|
|
}
|
|
|
|
readS.readByte();
|
|
readS.readByte();
|
|
readS.readByte();
|
|
continue;
|
|
break;
|
|
case 0x20: // Long compressed run
|
|
if (thisf_len - readS.pos() <= 3) {
|
|
warning("0x%02X: Input buffer underrun", in_ch);
|
|
return FAILURE;
|
|
}
|
|
|
|
runcount = readS.readSint16BE();
|
|
data_pixel = readS.readByte();
|
|
|
|
for (c = 0; c < runcount; c++) {
|
|
*outbuf_p++ = data_pixel;
|
|
}
|
|
|
|
outbuf_remain -= runcount;
|
|
continue;
|
|
break;
|
|
|
|
case 0x2F: // End of row
|
|
if (thisf_len - readS.pos() <= 4) {
|
|
return FAILURE;
|
|
}
|
|
|
|
x_vector = readS.readSint16BE();
|
|
new_row = readS.readSint16BE();
|
|
|
|
outbuf_p = decode_buf + ((y_origin + new_row) * di.logical_w) + x_origin + x_vector;
|
|
outbuf_remain = (outbuf_endp - outbuf_p) + 1;
|
|
continue;
|
|
break;
|
|
case 0x30: // Reposition command
|
|
if (thisf_len - readS.pos() < 2) {
|
|
return FAILURE;
|
|
}
|
|
|
|
x_vector = readS.readSint16BE();
|
|
|
|
if (((x_vector > 0) && ((size_t) x_vector > outbuf_remain)) || (-x_vector > outbuf_p - decode_buf)) {
|
|
warning("0x30: Invalid x_vector");
|
|
return FAILURE;
|
|
}
|
|
|
|
outbuf_p += x_vector;
|
|
outbuf_remain -= x_vector;
|
|
continue;
|
|
break;
|
|
|
|
case 0x3F: // 68: Frame end marker
|
|
debug(1, "0x3F: Frame end marker");
|
|
if (decoded_data && (thisf_len - readS.pos() > 0)) {
|
|
*nextf_p = thisf_p + readS.pos();
|
|
*nextf_len = thisf_len - readS.pos();
|
|
} else {
|
|
*nextf_p = NULL;
|
|
*nextf_len = 0;
|
|
}
|
|
|
|
cont_flag = 0;
|
|
continue;
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
control_ch = in_ch & 0xC0;
|
|
param_ch = in_ch & 0x3f;
|
|
switch (control_ch) {
|
|
|
|
case 0xC0: // Run of empty pixels
|
|
runcount = param_ch + 1;
|
|
if (outbuf_remain < runcount) {
|
|
return FAILURE;
|
|
}
|
|
|
|
outbuf_p += runcount;
|
|
outbuf_remain -= runcount;
|
|
continue;
|
|
break;
|
|
case 0x80: // Run of compressed data
|
|
runcount = param_ch + 1;
|
|
if ((outbuf_remain < runcount) || (thisf_len - readS.pos() <= 1)) {
|
|
return FAILURE;
|
|
}
|
|
|
|
data_pixel = readS.readByte();
|
|
|
|
for (c = 0; c < runcount; c++) {
|
|
*outbuf_p++ = data_pixel;
|
|
}
|
|
|
|
outbuf_remain -= runcount;
|
|
continue;
|
|
break;
|
|
case 0x40: // Uncompressed run
|
|
runcount = param_ch + 1;
|
|
if ((outbuf_remain < runcount) || (thisf_len - readS.pos() < runcount)) {
|
|
return FAILURE;
|
|
}
|
|
|
|
for (c = 0; c < runcount; c++) {
|
|
data_pixel = readS.readByte();
|
|
if (data_pixel != 0) {
|
|
*outbuf_p = data_pixel;
|
|
}
|
|
outbuf_p++;
|
|
}
|
|
|
|
outbuf_remain -= runcount;
|
|
|
|
continue;
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
return SUCCESS;
|
|
}
|
|
|
|
int Anim::getFrameOffset(const byte *resdata, size_t resdata_len, uint16 find_frame, size_t *frame_offset_p) {
|
|
ANIMATION_HEADER ah;
|
|
|
|
uint16 num_frames;
|
|
uint16 current_frame;
|
|
|
|
byte mark_byte;
|
|
uint16 control;
|
|
uint16 runcount;
|
|
uint16 magic;
|
|
|
|
int i;
|
|
|
|
if (!_initialized) {
|
|
return FAILURE;
|
|
}
|
|
|
|
MemoryReadStream readS(resdata, resdata_len);
|
|
|
|
// Read animation header
|
|
ah.magic = readS.readUint16LE();
|
|
ah.screen_w = readS.readUint16LE();
|
|
ah.screen_h = readS.readUint16LE();
|
|
ah.unknown06 = readS.readByte();
|
|
ah.unknown07 = readS.readByte();
|
|
ah.nframes = readS.readByte();
|
|
ah.flags = readS.readByte();
|
|
ah.unknown10 = readS.readByte();
|
|
ah.unknown11 = readS.readByte();
|
|
|
|
num_frames = ah.nframes;
|
|
|
|
if ((find_frame < 1) || (find_frame > num_frames)) {
|
|
return FAILURE;
|
|
}
|
|
|
|
for (current_frame = 1; current_frame < find_frame; current_frame++) {
|
|
magic = readS.readByte();
|
|
if (magic != SAGA_FRAME_HEADER_MAGIC) {
|
|
// Frame sync failure. Magic Number not found
|
|
return FAILURE;
|
|
}
|
|
|
|
// skip header
|
|
for (i = 0; i < SAGA_FRAME_HEADER_LEN; i++)
|
|
readS.readByte();
|
|
|
|
// For some strange reason, the animation header is in little
|
|
// endian format, but the actual RLE encoded frame data,
|
|
// including the frame header, is in big endian format. */
|
|
do {
|
|
mark_byte = readS.readByte();
|
|
switch (mark_byte) {
|
|
case 0x3F: // End of frame marker
|
|
continue;
|
|
break;
|
|
case 0x30: // Reposition command
|
|
readS.readByte();
|
|
readS.readByte();
|
|
continue;
|
|
break;
|
|
case 0x2F: // End of row marker
|
|
readS.readByte();
|
|
readS.readByte();
|
|
readS.readByte();
|
|
continue;
|
|
break;
|
|
case 0x20: // Long compressed run marker
|
|
readS.readByte();
|
|
readS.readByte();
|
|
readS.readByte();
|
|
continue;
|
|
break;
|
|
case 0x10: // (16) 0001 0000
|
|
// Long Uncompressed Run
|
|
runcount = readS.readSint16BE();
|
|
for (i = 0; i < runcount; i++)
|
|
readS.readByte();
|
|
continue;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
// Mask all but two high order (control) bits
|
|
control = mark_byte & 0xC0;
|
|
switch (control) {
|
|
case 0xC0:
|
|
// Run of empty pixels
|
|
continue;
|
|
break;
|
|
case 0x80:
|
|
// Run of compressed data
|
|
readS.readByte(); // Skip data byte
|
|
continue;
|
|
break;
|
|
case 0x40:
|
|
// Uncompressed run
|
|
runcount = (mark_byte & 0x3f) + 1;
|
|
for (i = 0; i < runcount; i++)
|
|
readS.readByte();
|
|
continue;
|
|
break;
|
|
default:
|
|
// Encountered unknown RLE marker, abort
|
|
return FAILURE;
|
|
break;
|
|
}
|
|
} while (mark_byte != 63);
|
|
}
|
|
|
|
*frame_offset_p = readS.pos();
|
|
return SUCCESS;
|
|
}
|
|
|
|
void Anim::animInfo() {
|
|
uint16 anim_ct;
|
|
uint16 i;
|
|
uint16 idx;
|
|
|
|
anim_ct = _anim_count;
|
|
|
|
_vm->_console->DebugPrintf("There are %d animations loaded:\n", anim_ct);
|
|
|
|
for (idx = 0, i = 0; i < anim_ct; idx++, i++) {
|
|
while (_anim_tbl[idx] == NULL) {
|
|
idx++;
|
|
}
|
|
|
|
_vm->_console->DebugPrintf("%02d: Frames: %u Flags: %u\n", i, _anim_tbl[idx]->n_frames, _anim_tbl[idx]->flags);
|
|
}
|
|
}
|
|
|
|
} // End of namespace Saga
|