mirror of
https://github.com/libretro/scummvm.git
synced 2024-12-27 12:16:59 +00:00
f3d340fb0c
o text formatting is not consistent with rules, just indent utility is too dumb for that o it does not use OSystem, i.e. it runs on direct SDL calls o it may not even compile on your box o if you enable it, expect zillions of warnings o no sound Now it runs ITE intro as reinherit did svn-id: r13564
1193 lines
22 KiB
C++
1193 lines
22 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$
|
|
*
|
|
*/
|
|
/*
|
|
Description:
|
|
|
|
Background animation management module
|
|
|
|
Notes:
|
|
*/
|
|
|
|
#include "reinherit.h"
|
|
|
|
#include "yslib.h"
|
|
|
|
/*
|
|
* Uses the following modules:
|
|
\*--------------------------------------------------------------------------*/
|
|
#include "cvar_mod.h"
|
|
#include "console_mod.h"
|
|
#include "game_mod.h"
|
|
#include "events_mod.h"
|
|
#include "render_mod.h"
|
|
|
|
/*
|
|
* Begin module
|
|
\*--------------------------------------------------------------------------*/
|
|
#include "animation_mod.h"
|
|
#include "animation.h"
|
|
|
|
namespace Saga {
|
|
|
|
static R_ANIMINFO AnimInfo;
|
|
|
|
int ANIM_Register(void)
|
|
{
|
|
CVAR_RegisterFunc(CF_anim_info, "anim_info", NULL, R_CVAR_NONE, 0, 0);
|
|
|
|
return R_SUCCESS;
|
|
}
|
|
|
|
int ANIM_Init(void)
|
|
{
|
|
|
|
AnimInfo.anim_limit = R_MAX_ANIMATIONS;
|
|
AnimInfo.anim_count = 0;
|
|
|
|
AnimInfo.initialized = 1;
|
|
|
|
return R_SUCCESS;
|
|
}
|
|
|
|
int ANIM_Shutdown(void)
|
|
{
|
|
uint i;
|
|
|
|
for (i = 0; i < R_MAX_ANIMATIONS; i++) {
|
|
|
|
free(AnimInfo.anim_tbl[i]);
|
|
}
|
|
|
|
AnimInfo.initialized = 0;
|
|
|
|
return R_SUCCESS;
|
|
}
|
|
|
|
int
|
|
ANIM_Load(const uchar * anim_resdata,
|
|
size_t anim_resdata_len, uint * anim_id_p)
|
|
{
|
|
R_ANIMATION *new_anim;
|
|
|
|
uint anim_id = 0;
|
|
uint i;
|
|
|
|
if (!AnimInfo.initialized) {
|
|
return R_FAILURE;
|
|
}
|
|
|
|
/* Find an unused animation slot
|
|
* \*------------------------------------------------------------- */
|
|
for (i = 0; i < R_MAX_ANIMATIONS; i++) {
|
|
|
|
if (AnimInfo.anim_tbl[i] == NULL) {
|
|
|
|
anim_id = i;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (i == R_MAX_ANIMATIONS) {
|
|
return R_FAILURE;
|
|
}
|
|
|
|
new_anim = (R_ANIMATION *)malloc(sizeof *new_anim);
|
|
if (new_anim == NULL) {
|
|
R_printf(R_STDERR, "Error: Allocation failure.\n");
|
|
|
|
return R_MEM;
|
|
}
|
|
|
|
new_anim->resdata = anim_resdata;
|
|
new_anim->resdata_len = anim_resdata_len;
|
|
|
|
if (GAME_GetGameType() == R_GAMETYPE_ITE) {
|
|
|
|
if (ANIM_GetNumFrames(anim_resdata,
|
|
&new_anim->n_frames) != R_SUCCESS) {
|
|
|
|
R_printf(R_STDERR,
|
|
"Error: Couldn't get animation frame count.\n");
|
|
|
|
return R_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) {
|
|
R_printf(R_STDERR, "Error: Allocation failure.\n");
|
|
|
|
return R_MEM;
|
|
}
|
|
|
|
for (i = 0; i < new_anim->n_frames; i++) {
|
|
|
|
ANIM_GetFrameOffset(anim_resdata,
|
|
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;
|
|
|
|
ANIM_GetNumFrames(anim_resdata, &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 = R_DEFAULT_FRAME_TIME;
|
|
new_anim->flags = 0;
|
|
new_anim->play_flag = 0;
|
|
new_anim->link_flag = 0;
|
|
new_anim->link_id = 0;
|
|
|
|
AnimInfo.anim_tbl[anim_id] = new_anim;
|
|
|
|
*anim_id_p = anim_id;
|
|
|
|
AnimInfo.anim_count++;
|
|
|
|
return R_SUCCESS;
|
|
}
|
|
|
|
int ANIM_Link(uint anim_id1, uint anim_id2)
|
|
{
|
|
R_ANIMATION *anim1;
|
|
R_ANIMATION *anim2;
|
|
|
|
if ((anim_id1 >= AnimInfo.anim_count) ||
|
|
(anim_id2 >= AnimInfo.anim_count)) {
|
|
|
|
return R_FAILURE;
|
|
}
|
|
|
|
anim1 = AnimInfo.anim_tbl[anim_id1];
|
|
anim2 = AnimInfo.anim_tbl[anim_id2];
|
|
|
|
if ((anim1 == NULL) || (anim2 == NULL)) {
|
|
return R_FAILURE;
|
|
}
|
|
|
|
anim1->link_id = anim_id2;
|
|
anim1->link_flag = 1;
|
|
|
|
anim2->frame_time = anim1->frame_time;
|
|
|
|
return R_SUCCESS;
|
|
}
|
|
|
|
int ANIM_Play(uint anim_id, int vector_time)
|
|
{
|
|
|
|
R_EVENT event;
|
|
R_ANIMATION *anim;
|
|
R_ANIMATION *link_anim;
|
|
uint link_anim_id;
|
|
|
|
R_BUFFER_INFO buf_info;
|
|
|
|
uchar *display_buf;
|
|
|
|
const uchar *nextf_p;
|
|
size_t nextf_len;
|
|
|
|
uint frame;
|
|
int result;
|
|
|
|
R_GAME_DISPLAYINFO disp_info;
|
|
|
|
if (anim_id >= AnimInfo.anim_count) {
|
|
|
|
return R_FAILURE;
|
|
}
|
|
|
|
GAME_GetDisplayInfo(&disp_info);
|
|
|
|
RENDER_GetBufferInfo(&buf_info);
|
|
display_buf = buf_info.r_bg_buf;
|
|
|
|
anim = AnimInfo.anim_tbl[anim_id];
|
|
if (anim == NULL) {
|
|
|
|
return R_FAILURE;
|
|
}
|
|
|
|
if (anim->play_flag) {
|
|
|
|
frame = anim->current_frame;
|
|
|
|
if (GAME_GetGameType() == R_GAMETYPE_ITE) {
|
|
|
|
result = ITE_DecodeFrame(anim->resdata,
|
|
anim->frame_offsets[frame - 1],
|
|
display_buf,
|
|
disp_info.logical_w * disp_info.logical_h);
|
|
|
|
if (result != R_SUCCESS) {
|
|
|
|
R_printf(R_STDERR,
|
|
"ANIM_Play: Error decoding frame %u",
|
|
anim->current_frame);
|
|
|
|
anim->play_flag = 0;
|
|
return R_FAILURE;
|
|
}
|
|
} else {
|
|
|
|
if (anim->cur_frame_p == NULL) {
|
|
R_printf(R_STDERR,
|
|
"ANIM_Play: Frames exhausted.\n");
|
|
|
|
return R_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 != R_SUCCESS) {
|
|
|
|
R_printf(R_STDERR,
|
|
"ANIM_Play: Error decoding frame %u",
|
|
anim->current_frame);
|
|
|
|
anim->play_flag = 0;
|
|
return R_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 = AnimInfo.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 = R_ONESHOT_EVENT;
|
|
event.code = R_SCENE_EVENT;
|
|
event.op = EVENT_END;
|
|
event.time = anim->frame_time + vector_time;
|
|
|
|
EVENT_Queue(&event);
|
|
}
|
|
return R_SUCCESS;
|
|
}
|
|
}
|
|
|
|
event.type = R_ONESHOT_EVENT;
|
|
event.code = R_ANIM_EVENT;
|
|
event.op = EVENT_FRAME;
|
|
event.param = anim_id;
|
|
event.time = anim->frame_time + vector_time;
|
|
|
|
EVENT_Queue(&event);
|
|
|
|
return R_SUCCESS;
|
|
}
|
|
|
|
int ANIM_Reset(void)
|
|
{
|
|
uint i;
|
|
|
|
for (i = 0; i < R_MAX_ANIMATIONS; i++) {
|
|
|
|
ANIM_Free(i);
|
|
}
|
|
|
|
AnimInfo.anim_count = 0;
|
|
|
|
return R_SUCCESS;
|
|
}
|
|
|
|
int ANIM_SetFlag(uint anim_id, uint flag)
|
|
{
|
|
R_ANIMATION *anim;
|
|
|
|
if (anim_id > AnimInfo.anim_count) {
|
|
|
|
return R_FAILURE;
|
|
}
|
|
|
|
anim = AnimInfo.anim_tbl[anim_id];
|
|
if (anim == NULL) {
|
|
|
|
return R_FAILURE;
|
|
}
|
|
|
|
anim->flags |= flag;
|
|
|
|
return R_SUCCESS;
|
|
}
|
|
|
|
int ANIM_SetFrameTime(uint anim_id, int time)
|
|
{
|
|
R_ANIMATION *anim;
|
|
|
|
if (anim_id > AnimInfo.anim_count) {
|
|
|
|
return R_FAILURE;
|
|
}
|
|
|
|
anim = AnimInfo.anim_tbl[anim_id];
|
|
if (anim == NULL) {
|
|
|
|
return R_FAILURE;
|
|
}
|
|
|
|
anim->frame_time = time;
|
|
|
|
return R_SUCCESS;
|
|
}
|
|
|
|
int ANIM_Free(uint anim_id)
|
|
{
|
|
R_ANIMATION *anim;
|
|
|
|
if (anim_id > AnimInfo.anim_count) {
|
|
|
|
return R_FAILURE;
|
|
}
|
|
|
|
anim = AnimInfo.anim_tbl[anim_id];
|
|
if (anim == NULL) {
|
|
|
|
return R_FAILURE;
|
|
}
|
|
|
|
if (GAME_GetGameType() == R_GAMETYPE_ITE) {
|
|
|
|
free(anim->frame_offsets);
|
|
anim->frame_offsets = NULL;
|
|
}
|
|
|
|
free(anim);
|
|
AnimInfo.anim_tbl[anim_id] = NULL;
|
|
AnimInfo.anim_count--;
|
|
|
|
return R_SUCCESS;
|
|
}
|
|
|
|
int ANIM_GetNumFrames(const uchar * anim_resource, uint * n_frames)
|
|
/*--------------------------------------------------------------------------*\
|
|
* 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'
|
|
\*--------------------------------------------------------------------------*/
|
|
{
|
|
R_ANIMATION_HEADER ah;
|
|
|
|
size_t offset;
|
|
int magic;
|
|
|
|
int x;
|
|
|
|
const uchar *read_p = anim_resource;
|
|
|
|
if (!AnimInfo.initialized) {
|
|
|
|
return R_FAILURE;
|
|
}
|
|
|
|
ah.magic = ys_read_u16_le(read_p, &read_p);
|
|
ah.screen_w = ys_read_u16_le(read_p, &read_p);
|
|
ah.screen_h = ys_read_u16_le(read_p, &read_p);
|
|
|
|
ah.unknown06 = ys_read_u8(read_p, &read_p);
|
|
ah.unknown07 = ys_read_u8(read_p, &read_p);
|
|
ah.nframes = ys_read_u8(read_p, NULL);
|
|
|
|
if (GAME_GetGameType() == R_GAMETYPE_IHNM) {
|
|
|
|
*n_frames = ah.nframes;
|
|
}
|
|
|
|
if (ah.magic == 68) {
|
|
|
|
for (x = ah.nframes; x > 0; x--) {
|
|
|
|
if (ANIM_GetFrameOffset(anim_resource,
|
|
x, &offset) != R_SUCCESS) {
|
|
|
|
return R_FAILURE;
|
|
}
|
|
|
|
magic = *(anim_resource + offset);
|
|
|
|
if (magic == SAGA_FRAME_HEADER_MAGIC) {
|
|
*n_frames = x;
|
|
|
|
return R_SUCCESS;
|
|
}
|
|
}
|
|
|
|
return R_FAILURE;
|
|
}
|
|
|
|
return R_FAILURE;
|
|
}
|
|
|
|
int
|
|
ITE_DecodeFrame(const uchar * resdata,
|
|
size_t frame_offset, uchar * buf, size_t buf_len)
|
|
{
|
|
|
|
R_ANIMATION_HEADER ah;
|
|
R_FRAME_HEADER fh;
|
|
|
|
const uchar *read_p = resdata;
|
|
uchar *write_p;
|
|
|
|
uint magic;
|
|
|
|
uint x_start;
|
|
uint y_start;
|
|
ulong screen_w;
|
|
ulong screen_h;
|
|
|
|
int mark_byte;
|
|
uchar data_byte;
|
|
int new_row;
|
|
|
|
uint control_ch;
|
|
uint param_ch;
|
|
|
|
uint runcount;
|
|
int x_vector;
|
|
|
|
uint i;
|
|
|
|
if (!AnimInfo.initialized) {
|
|
|
|
return R_FAILURE;
|
|
}
|
|
|
|
/* Read animation header
|
|
* \*------------------------------------------------------------- */
|
|
ah.magic = ys_read_u16_le(read_p, &read_p);
|
|
ah.screen_w = ys_read_u16_le(read_p, &read_p);
|
|
ah.screen_h = ys_read_u16_le(read_p, &read_p);
|
|
ah.unknown06 = ys_read_u8(read_p, &read_p);
|
|
ah.unknown07 = ys_read_u8(read_p, &read_p);
|
|
ah.nframes = ys_read_u8(read_p, &read_p);
|
|
ah.flags = ys_read_u8(read_p, &read_p);
|
|
ah.unknown10 = ys_read_u8(read_p, &read_p);
|
|
ah.unknown11 = ys_read_u8(read_p, &read_p);
|
|
|
|
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. */
|
|
R_printf(R_STDERR,
|
|
"ITE_DecodeFrame: Buffer size inadequate.\n");
|
|
|
|
return R_FAILURE;
|
|
}
|
|
|
|
/* Read frame header
|
|
* \*------------------------------------------------------------- */
|
|
read_p = resdata + frame_offset;
|
|
|
|
/* Check for frame magic byte */
|
|
magic = ys_read_u8(read_p, &read_p);
|
|
if (magic != SAGA_FRAME_HEADER_MAGIC) {
|
|
|
|
R_printf(R_STDERR, "ITE_DecodeFrame: Invalid frame offset.\n");
|
|
|
|
return R_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 = ys_read_u16_be(read_p, &read_p);
|
|
fh.y_start = ys_read_u8(read_p, &read_p);
|
|
read_p++; /* Skip pad byte */
|
|
fh.x_pos = ys_read_u16_be(read_p, &read_p);
|
|
fh.y_pos = ys_read_u16_be(read_p, &read_p);
|
|
fh.width = ys_read_u16_be(read_p, &read_p);
|
|
fh.height = ys_read_u16_be(read_p, &read_p);
|
|
|
|
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 = ys_read_u8(read_p, &read_p);
|
|
|
|
switch (mark_byte) {
|
|
|
|
case 0x10: /* Long Unencoded Run */
|
|
|
|
runcount = ys_read_s16_be(read_p, &read_p);
|
|
|
|
for (i = 0; i < runcount; i++) {
|
|
if (*read_p != 0) {
|
|
*write_p = *read_p;
|
|
}
|
|
write_p++;
|
|
read_p++;
|
|
}
|
|
continue;
|
|
break;
|
|
|
|
case 0x20: /* Long encoded run */
|
|
|
|
runcount = ys_read_s16_be(read_p, &read_p);
|
|
|
|
data_byte = *read_p++;
|
|
|
|
for (i = 0; i < runcount; i++) {
|
|
*write_p++ = data_byte;
|
|
}
|
|
continue;
|
|
break;
|
|
|
|
case 0x2F: /* End of row */
|
|
|
|
x_vector = ys_read_s16_be(read_p, &read_p);
|
|
new_row = ys_read_u8(read_p, &read_p);
|
|
|
|
/* 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 = ys_read_s16_be(read_p, &read_p);
|
|
|
|
write_p += x_vector;
|
|
continue;
|
|
break;
|
|
|
|
case 0x3F: /* End of frame marker */
|
|
|
|
return R_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 = *read_p++;
|
|
|
|
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++) {
|
|
if (*read_p != 0) {
|
|
*write_p = *read_p;
|
|
}
|
|
write_p++;
|
|
read_p++;
|
|
}
|
|
continue;
|
|
break;
|
|
|
|
default:
|
|
/* Unknown marker found - abort */
|
|
|
|
R_printf(R_STDERR,
|
|
"ITE_DecodeFrame: Invalid RLE marker "
|
|
"encountered.\n");
|
|
|
|
return R_FAILURE;
|
|
break;
|
|
}
|
|
|
|
} while (mark_byte != 63); /* end of frame marker */
|
|
|
|
return R_SUCCESS;
|
|
}
|
|
|
|
int
|
|
IHNM_DecodeFrame(uchar * decode_buf,
|
|
size_t decode_buf_len,
|
|
const uchar * thisf_p,
|
|
size_t thisf_len, const uchar ** nextf_p, size_t * nextf_len)
|
|
{
|
|
|
|
int in_ch;
|
|
|
|
int decoded_data = 0;
|
|
int cont_flag = 1;
|
|
|
|
int control_ch;
|
|
int param_ch;
|
|
|
|
uchar data_pixel;
|
|
|
|
int x_origin = 0;
|
|
int y_origin = 0;
|
|
int x_vector;
|
|
int new_row;
|
|
|
|
uint runcount;
|
|
uint c;
|
|
|
|
size_t in_ch_offset;
|
|
|
|
const uchar *inbuf_p = thisf_p;
|
|
size_t inbuf_remain = thisf_len;
|
|
|
|
uchar *outbuf_p = decode_buf;
|
|
uchar *outbuf_endp = (decode_buf + decode_buf_len) - 1;
|
|
size_t outbuf_remain = decode_buf_len;
|
|
|
|
R_GAME_DISPLAYINFO di;
|
|
|
|
GAME_GetDisplayInfo(&di);
|
|
|
|
*nextf_p = NULL;
|
|
|
|
for (; cont_flag; decoded_data = 1) {
|
|
|
|
in_ch_offset = (size_t) (inbuf_p - thisf_p);
|
|
|
|
in_ch = *inbuf_p++;
|
|
inbuf_remain--;
|
|
|
|
switch (in_ch) {
|
|
|
|
case 0x0F: /* 15: Frame header */
|
|
{
|
|
int param1;
|
|
int param2;
|
|
int param3;
|
|
int param4;
|
|
int param5;
|
|
int param6;
|
|
|
|
if (inbuf_remain < 13) {
|
|
R_printf(R_STDERR,
|
|
"0x%02X: Input buffer underrun.",
|
|
in_ch);
|
|
|
|
return R_FAILURE;
|
|
}
|
|
|
|
param1 = ys_read_u16_be(inbuf_p, &inbuf_p);
|
|
param2 = ys_read_u16_be(inbuf_p, &inbuf_p);
|
|
inbuf_p++; /* skip 1? */
|
|
param3 = ys_read_u16_be(inbuf_p, &inbuf_p);
|
|
param4 = ys_read_u16_be(inbuf_p, &inbuf_p);
|
|
param5 = ys_read_u16_be(inbuf_p, &inbuf_p);
|
|
param6 = ys_read_u16_be(inbuf_p, &inbuf_p);
|
|
|
|
inbuf_remain -= 13;
|
|
|
|
x_origin = param1;
|
|
y_origin = param2;
|
|
|
|
outbuf_p = decode_buf + x_origin +
|
|
(y_origin * di.logical_w);
|
|
|
|
if (outbuf_p > outbuf_endp) {
|
|
|
|
R_printf(R_STDERR,
|
|
"0x%02X: (0x%X) Invalid output position. "
|
|
"(x: %d, y: %d)\n",
|
|
in_ch,
|
|
in_ch_offset, x_origin, y_origin);
|
|
|
|
return R_FAILURE;
|
|
}
|
|
|
|
outbuf_remain = (outbuf_endp - outbuf_p) + 1;
|
|
|
|
continue;
|
|
}
|
|
break;
|
|
|
|
case 0x10: /* Long Unencoded Run */
|
|
|
|
runcount = ys_read_s16_be(inbuf_p, &inbuf_p);
|
|
|
|
if (inbuf_remain < runcount) {
|
|
|
|
R_printf(R_STDERR,
|
|
"0x%02X: Input buffer underrun.", in_ch);
|
|
|
|
return R_FAILURE;
|
|
}
|
|
|
|
if (outbuf_remain < runcount) {
|
|
|
|
R_printf(R_STDERR,
|
|
"0x%02X: Output buffer overrun.", in_ch);
|
|
|
|
return R_FAILURE;
|
|
}
|
|
|
|
for (c = 0; c < runcount; c++) {
|
|
if (*inbuf_p != 0) {
|
|
*outbuf_p = *inbuf_p;
|
|
}
|
|
outbuf_p++;
|
|
inbuf_p++;
|
|
}
|
|
|
|
inbuf_remain -= runcount;
|
|
outbuf_remain -= runcount;
|
|
|
|
continue;
|
|
break;
|
|
|
|
case 0x1F: /* 31: Unusued? */
|
|
|
|
if (inbuf_remain < 3) {
|
|
|
|
R_printf(R_STDERR,
|
|
"0x%02X: Input buffer underrun.", in_ch);
|
|
|
|
return R_FAILURE;
|
|
}
|
|
|
|
inbuf_p += 3;
|
|
inbuf_remain -= 3;
|
|
|
|
continue;
|
|
break;
|
|
|
|
case 0x20: /* Long compressed run */
|
|
|
|
if (inbuf_remain <= 3) {
|
|
|
|
R_printf(R_STDERR,
|
|
"0x%02X: Input buffer underrun.", in_ch);
|
|
|
|
return R_FAILURE;
|
|
}
|
|
|
|
runcount = ys_read_s16_be(inbuf_p, &inbuf_p);
|
|
data_pixel = *inbuf_p++;
|
|
|
|
for (c = 0; c < runcount; c++) {
|
|
*outbuf_p++ = data_pixel;
|
|
}
|
|
|
|
outbuf_remain -= runcount;
|
|
inbuf_remain -= 1;
|
|
|
|
continue;
|
|
break;
|
|
|
|
case 0x2F: /* End of row */
|
|
|
|
if (inbuf_remain <= 4) {
|
|
return R_FAILURE;
|
|
}
|
|
|
|
x_vector = ys_read_s16_be(inbuf_p, &inbuf_p);
|
|
new_row = ys_read_s16_be(inbuf_p, &inbuf_p);
|
|
|
|
outbuf_p =
|
|
decode_buf + ((y_origin + new_row) * di.logical_w)
|
|
+ x_origin + x_vector;
|
|
|
|
inbuf_remain -= 4;
|
|
outbuf_remain = (outbuf_endp - outbuf_p) + 1;
|
|
|
|
continue;
|
|
break;
|
|
|
|
case 0x30: /* Reposition command */
|
|
if (inbuf_remain < 2) {
|
|
return R_FAILURE;
|
|
}
|
|
|
|
x_vector = ys_read_s16_be(inbuf_p, &inbuf_p);
|
|
|
|
if (((x_vector > 0)
|
|
&& ((size_t) x_vector > outbuf_remain))
|
|
|| (-x_vector > outbuf_p - decode_buf)) {
|
|
|
|
R_printf(R_STDERR,
|
|
"0x30: Invalid x_vector.\n");
|
|
|
|
return R_FAILURE;
|
|
}
|
|
|
|
outbuf_p += x_vector;
|
|
outbuf_remain -= x_vector;
|
|
inbuf_remain -= 2;
|
|
|
|
continue;
|
|
break;
|
|
|
|
case 0x3F: /* 68: Frame end marker */
|
|
|
|
printf("0x3F: Frame end marker\n");
|
|
|
|
if (decoded_data && inbuf_remain > 0) {
|
|
|
|
*nextf_p = inbuf_p;
|
|
*nextf_len = inbuf_remain;
|
|
} else {
|
|
|
|
*nextf_p = NULL;
|
|
*nextf_len = 0;
|
|
}
|
|
|
|
cont_flag = 0;
|
|
continue;
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
|
|
} /* end switch() */
|
|
|
|
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 R_FAILURE;
|
|
}
|
|
|
|
outbuf_p += runcount;
|
|
outbuf_remain -= runcount;
|
|
|
|
continue;
|
|
break;
|
|
|
|
case 0x80: /* Run of compressed data */
|
|
|
|
runcount = param_ch + 1;
|
|
|
|
if ((outbuf_remain < runcount) || (inbuf_remain <= 1)) {
|
|
|
|
return R_FAILURE;
|
|
}
|
|
|
|
data_pixel = *inbuf_p++;
|
|
inbuf_remain--;
|
|
|
|
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) ||
|
|
(inbuf_remain < runcount)) {
|
|
|
|
return R_FAILURE;
|
|
}
|
|
|
|
for (c = 0; c < runcount; c++) {
|
|
if (*inbuf_p != 0) {
|
|
*outbuf_p = *inbuf_p;
|
|
}
|
|
outbuf_p++;
|
|
inbuf_p++;
|
|
}
|
|
|
|
inbuf_remain -= runcount;
|
|
outbuf_remain -= runcount;
|
|
|
|
continue;
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
} /* end switch */
|
|
|
|
} /* end while() */
|
|
|
|
return R_SUCCESS;
|
|
}
|
|
|
|
int
|
|
ANIM_GetFrameOffset(const uchar * resdata,
|
|
uint find_frame, size_t * frame_offset_p)
|
|
{
|
|
R_ANIMATION_HEADER ah;
|
|
|
|
uint num_frames;
|
|
uint current_frame;
|
|
|
|
const uchar *read_p = resdata;
|
|
const uchar *search_ptr;
|
|
|
|
uchar mark_byte;
|
|
uint control;
|
|
uint runcount;
|
|
|
|
uint magic;
|
|
|
|
if (!AnimInfo.initialized) {
|
|
|
|
return R_FAILURE;
|
|
}
|
|
|
|
/* Read animation header
|
|
* \*------------------------------------------------------------- */
|
|
ah.magic = ys_read_u16_le(read_p, &read_p);
|
|
ah.screen_w = ys_read_u16_le(read_p, &read_p);
|
|
ah.screen_h = ys_read_u16_le(read_p, &read_p);
|
|
ah.unknown06 = ys_read_u8(read_p, &read_p);
|
|
ah.unknown07 = ys_read_u8(read_p, &read_p);
|
|
ah.nframes = ys_read_u8(read_p, &read_p);
|
|
ah.flags = ys_read_u8(read_p, &read_p);
|
|
ah.unknown10 = ys_read_u8(read_p, &read_p);
|
|
ah.unknown11 = ys_read_u8(read_p, &read_p);
|
|
|
|
num_frames = ah.nframes;
|
|
|
|
if ((find_frame < 1) || (find_frame > num_frames)) {
|
|
|
|
return R_FAILURE;
|
|
}
|
|
|
|
search_ptr = read_p;
|
|
|
|
for (current_frame = 1; current_frame < find_frame; current_frame++) {
|
|
|
|
magic = ys_read_u8(search_ptr, &search_ptr);
|
|
|
|
if (magic != SAGA_FRAME_HEADER_MAGIC) {
|
|
|
|
/* Frame sync failure. Magic Number not found */
|
|
return R_FAILURE;
|
|
}
|
|
|
|
search_ptr += SAGA_FRAME_HEADER_LEN;
|
|
|
|
/* 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 = *search_ptr;
|
|
|
|
switch (mark_byte) {
|
|
|
|
case 0x3F: /* End of frame marker */
|
|
|
|
search_ptr++;
|
|
continue;
|
|
break;
|
|
|
|
case 0x30: /* Reposition command */
|
|
|
|
search_ptr += 3;
|
|
continue;
|
|
break;
|
|
|
|
case 0x2F: /* End of row marker */
|
|
|
|
search_ptr += 4;
|
|
continue;
|
|
break;
|
|
|
|
case 0x20: /* Long compressed run marker */
|
|
|
|
search_ptr += 4;
|
|
continue;
|
|
break;
|
|
|
|
case 0x10: /* (16) 0001 0000 */
|
|
/* Long Uncompressed Run */
|
|
search_ptr++;
|
|
runcount =
|
|
ys_read_s16_be(search_ptr, &search_ptr);
|
|
search_ptr += runcount;
|
|
continue;
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
|
|
}
|
|
|
|
/* Mask all but two high order (control) bits */
|
|
control = mark_byte & 0xC0;
|
|
|
|
switch (control) {
|
|
|
|
case 0xC0:
|
|
/* Run of empty pixels */
|
|
search_ptr++;
|
|
continue;
|
|
break;
|
|
|
|
case 0x80:
|
|
/* Run of compressed data */
|
|
search_ptr += 2; /* Skip data byte */
|
|
continue;
|
|
break;
|
|
|
|
case 0x40:
|
|
/* Uncompressed run */
|
|
search_ptr++;
|
|
runcount = (mark_byte & 0x3f) + 1;
|
|
search_ptr += runcount;
|
|
continue;
|
|
break;
|
|
|
|
default:
|
|
/* Encountered unknown RLE marker, abort */
|
|
return R_FAILURE;
|
|
break;
|
|
|
|
} /* end switch ( test_byte ) */
|
|
|
|
} while (mark_byte != 63); /* end of frame marker */
|
|
|
|
} /* end for( i = 0 ; i < find_frame ; i ++ ) */
|
|
|
|
*frame_offset_p = (search_ptr - resdata);
|
|
|
|
return R_SUCCESS;
|
|
}
|
|
|
|
static void CF_anim_info(int argc, char *argv[])
|
|
{
|
|
uint anim_ct;
|
|
uint i;
|
|
uint idx;
|
|
|
|
YS_IGNORE_PARAM(argc);
|
|
YS_IGNORE_PARAM(argv);
|
|
|
|
anim_ct = AnimInfo.anim_count;
|
|
|
|
CON_Print("There are %d animations loaded:", anim_ct);
|
|
|
|
for (idx = 0, i = 0; i < anim_ct; idx++, i++) {
|
|
|
|
while (AnimInfo.anim_tbl[idx] == NULL) {
|
|
idx++;
|
|
}
|
|
|
|
CON_Print("%02d: Frames: %u Flags: %u",
|
|
i,
|
|
AnimInfo.anim_tbl[idx]->n_frames,
|
|
AnimInfo.anim_tbl[idx]->flags);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
} // End of namespace Saga
|