mirror of
https://github.com/scummvm/scummvm.git
synced 2026-06-20 05:45:29 +00:00
507 lines
12 KiB
C++
507 lines
12 KiB
C++
/* 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.
|
|
*
|
|
*/
|
|
|
|
#include "glk/magnetic/magnetic.h"
|
|
|
|
namespace Glk {
|
|
namespace Magnetic {
|
|
|
|
byte Magnetic::init_gfx1(size_t size) {
|
|
if (!(gfx_buf = new byte[MAX_PICTURE_SIZE]))
|
|
return 1;
|
|
|
|
if (!(gfx_data = new byte[size])) {
|
|
delete[] gfx_buf;
|
|
gfx_buf = nullptr;
|
|
return 1;
|
|
}
|
|
|
|
if (_gfxFile.read(gfx_data, size) != size) {
|
|
delete[] gfx_data;
|
|
delete[] gfx_buf;
|
|
gfx_data = gfx_buf = nullptr;
|
|
return 1;
|
|
}
|
|
|
|
gfx_ver = 1;
|
|
return 2;
|
|
}
|
|
|
|
byte Magnetic::init_gfx2(size_t size) {
|
|
if (!(gfx_buf = new byte[MAX_PICTURE_SIZE])) {
|
|
return 1;
|
|
}
|
|
|
|
gfx2_hsize = size;
|
|
if (!(gfx2_hdr = new byte[gfx2_hsize])) {
|
|
delete[] gfx_buf;
|
|
gfx_buf = nullptr;
|
|
return 1;
|
|
}
|
|
|
|
if (_gfxFile.read(gfx2_hdr, gfx2_hsize) != gfx2_hsize) {
|
|
delete[] gfx_buf;
|
|
delete[] gfx2_hdr;
|
|
gfx_buf = nullptr;
|
|
gfx2_hdr = nullptr;
|
|
return 1;
|
|
}
|
|
|
|
gfx_ver = 2;
|
|
return 2;
|
|
}
|
|
|
|
void Magnetic::ms_showpic(int c, byte mode) {
|
|
// TODO
|
|
}
|
|
|
|
bool Magnetic::is_blank(uint16 line, uint16 width) const {
|
|
int i;
|
|
|
|
for (i = line * width; i < (line + 1) * width; i++)
|
|
if (gfx_buf[i])
|
|
return false;
|
|
return true;
|
|
}
|
|
|
|
byte *Magnetic::ms_extract1(byte pic, uint16 * w, uint16 * h, uint16 * pal) {
|
|
byte *table, *data, bit, val, *buffer;
|
|
uint16 tablesize, count;
|
|
uint32 i, j, upsize, offset;
|
|
//uint32 datasize;
|
|
|
|
offset = READ_LE_UINT32(gfx_data + 4 * pic);
|
|
buffer = gfx_data + offset - 8;
|
|
|
|
for (i = 0; i < 16; i++)
|
|
pal[i] = READ_LE_UINT16(buffer + 0x1c + 2 * i);
|
|
w[0] = (uint16)(READ_LE_UINT16(buffer + 4) - READ_LE_UINT16(buffer + 2));
|
|
h[0] = READ_LE_UINT16(buffer + 6);
|
|
|
|
tablesize = READ_LE_UINT16(buffer + 0x3c);
|
|
//datasize = READ_LE_UINT32(buffer + 0x3e);
|
|
table = buffer + 0x42;
|
|
data = table + tablesize * 2 + 2;
|
|
upsize = h[0] * w[0];
|
|
|
|
for (i = 0, j = 0, count = 0, val = 0, bit = 7; i < upsize; i++, count--) {
|
|
if (!count) {
|
|
count = tablesize;
|
|
while (count < 0x80) {
|
|
if (data[j] & (1 << bit))
|
|
count = table[2 * count];
|
|
else
|
|
count = table[2 * count + 1];
|
|
if (!bit)
|
|
j++;
|
|
bit = (byte)(bit ? bit - 1 : 7);
|
|
}
|
|
count &= 0x7f;
|
|
if (count >= 0x10)
|
|
count -= 0x10;
|
|
else
|
|
{
|
|
val = (byte)count;
|
|
count = 1;
|
|
}
|
|
}
|
|
gfx_buf[i] = val;
|
|
}
|
|
for (j = w[0]; j < upsize; j++)
|
|
gfx_buf[j] ^= gfx_buf[j - w[0]];
|
|
|
|
for (; h[0] > 0 && is_blank((uint16)(h[0] - 1), w[0]); h[0]--);
|
|
for (i = 0; h[0] > 0 && is_blank((uint16)i, w[0]); h[0]--, i++);
|
|
return gfx_buf + i * w[0];
|
|
}
|
|
|
|
|
|
byte *Magnetic::ms_extract2(const char *name, uint16 *w, uint16 *h, uint16 *pal, byte *is_anim) {
|
|
struct picture main_pic;
|
|
uint32 offset = 0, length = 0, i;
|
|
int16 header_pos = -1;
|
|
byte* anim_data;
|
|
uint32 j;
|
|
|
|
if (is_anim != 0)
|
|
*is_anim = 0;
|
|
gfx2_name = name;
|
|
|
|
pos_table_size = 0;
|
|
|
|
// Find the uppercase (no animation) version of the picture first
|
|
header_pos = find_name_in_header(name, 1);
|
|
|
|
if (header_pos < 0)
|
|
header_pos = find_name_in_header(name, 0);
|
|
if (header_pos < 0)
|
|
return 0;
|
|
|
|
offset = READ_LE_UINT32(gfx2_hdr + header_pos + 8);
|
|
length = READ_LE_UINT32(gfx2_hdr + header_pos + 12);
|
|
|
|
if (offset != 0) {
|
|
if (gfx2_buf) {
|
|
delete[] gfx2_buf;
|
|
gfx2_buf = nullptr;
|
|
}
|
|
|
|
gfx2_buf = new byte[length];
|
|
if (!gfx2_buf)
|
|
return 0;
|
|
|
|
if (!_gfxFile.seek(offset) || _gfxFile.read(gfx2_buf, length) != length) {
|
|
delete[] gfx2_buf;
|
|
gfx2_buf = nullptr;
|
|
return 0;
|
|
}
|
|
|
|
for (i = 0; i < 16; i++)
|
|
pal[i] = READ_LE_UINT16(gfx2_buf + 4 + (2 * i));
|
|
|
|
main_pic.data = gfx2_buf + 48;
|
|
main_pic.data_size = READ_LE_UINT32(gfx2_buf + 38);
|
|
main_pic.width = READ_LE_UINT16(gfx2_buf + 42);
|
|
main_pic.height = READ_LE_UINT16(gfx2_buf + 44);
|
|
main_pic.wbytes = (uint16)(main_pic.data_size / main_pic.height);
|
|
main_pic.plane_step = (uint16)(main_pic.wbytes / 4);
|
|
main_pic.mask = (byte*)0;
|
|
extract_frame(&main_pic);
|
|
|
|
*w = main_pic.width;
|
|
*h = main_pic.height;
|
|
|
|
// Check for an animation
|
|
anim_data = gfx2_buf + 48 + main_pic.data_size;
|
|
if ((anim_data[0] != 0xD0) || (anim_data[1] != 0x5E)) {
|
|
byte *current;
|
|
uint16 frame_count;
|
|
uint16 value1, value2;
|
|
//uint16 command_count;
|
|
|
|
if (is_anim != 0)
|
|
*is_anim = 1;
|
|
|
|
current = anim_data + 6;
|
|
frame_count = READ_LE_UINT16(anim_data + 2);
|
|
if (frame_count > MAX_ANIMS)
|
|
{
|
|
error("animation frame array too short");
|
|
return 0;
|
|
}
|
|
|
|
/* Loop through each animation frame */
|
|
for (i = 0; i < frame_count; i++)
|
|
{
|
|
anim_frame_table[i].data = current + 10;
|
|
anim_frame_table[i].data_size = READ_LE_UINT32(current);
|
|
anim_frame_table[i].width = READ_LE_UINT16(current + 4);
|
|
anim_frame_table[i].height = READ_LE_UINT16(current + 6);
|
|
anim_frame_table[i].wbytes = (uint16)(anim_frame_table[i].data_size / anim_frame_table[i].height);
|
|
anim_frame_table[i].plane_step = (uint16)(anim_frame_table[i].wbytes / 4);
|
|
anim_frame_table[i].mask = (byte*)0;
|
|
|
|
current += anim_frame_table[i].data_size + 12;
|
|
value1 = READ_LE_UINT16(current - 2);
|
|
value2 = READ_LE_UINT16(current);
|
|
|
|
/* Get the mask */
|
|
if ((value1 == anim_frame_table[i].width) && (value2 == anim_frame_table[i].height))
|
|
{
|
|
uint16 skip;
|
|
|
|
anim_frame_table[i].mask = (byte*)(current + 4);
|
|
skip = READ_LE_UINT16(current + 2);
|
|
current += skip + 6;
|
|
}
|
|
}
|
|
|
|
/* Get the positioning tables */
|
|
pos_table_size = READ_LE_UINT16(current - 2);
|
|
if (pos_table_size > MAX_POSITIONS)
|
|
{
|
|
error("animation position array too short");
|
|
return 0;
|
|
}
|
|
|
|
for (i = 0; i < pos_table_size; i++) {
|
|
pos_table_count[i] = READ_LE_UINT16(current + 2);
|
|
current += 4;
|
|
|
|
if (pos_table_count[i] > MAX_ANIMS)
|
|
{
|
|
error("animation position array too short");
|
|
return 0;
|
|
}
|
|
|
|
for (j = 0; j < pos_table_count[i]; j++)
|
|
{
|
|
pos_table[i][j].x = READ_LE_UINT16(current);
|
|
pos_table[i][j].y = READ_LE_UINT16(current + 2);
|
|
pos_table[i][j].number = READ_LE_UINT16(current + 4) - 1;
|
|
current += 8;
|
|
}
|
|
}
|
|
|
|
// Get the command sequence table
|
|
//command_count = READ_LE_UINT16(current);
|
|
command_table = current + 2;
|
|
|
|
for (i = 0; i < MAX_POSITIONS; i++)
|
|
{
|
|
anim_table[i].flag = -1;
|
|
anim_table[i].count = -1;
|
|
}
|
|
command_index = 0;
|
|
anim_repeat = 0;
|
|
pos_table_index = -1;
|
|
pos_table_max = -1;
|
|
}
|
|
|
|
return gfx_buf;
|
|
}
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
int16 Magnetic::find_name_in_header(const Common::String &name, bool upper) {
|
|
int16 header_pos = 0;
|
|
Common::String pic_name(name.c_str(), name.c_str() + 6);
|
|
|
|
if (upper)
|
|
pic_name.toUppercase();
|
|
|
|
while (header_pos < gfx2_hsize) {
|
|
const char *hname = (const char *)(gfx2_hdr + header_pos);
|
|
if (strncmp(hname, pic_name.c_str(), 6) == 0)
|
|
return header_pos;
|
|
header_pos += 16;
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
void Magnetic::extract_frame(const picture *pic) {
|
|
uint32 i, x, y, bit_x, mask, ywb, yw, value, values[4];
|
|
|
|
if (pic->width * pic->height > MAX_PICTURE_SIZE) {
|
|
error("picture too large");
|
|
return;
|
|
}
|
|
|
|
for (y = 0; y < pic->height; y++) {
|
|
ywb = y * pic->wbytes;
|
|
yw = y * pic->width;
|
|
|
|
for (x = 0; x < pic->width; x++) {
|
|
if ((x % 8) == 0) {
|
|
for (i = 0; i < 4; i++)
|
|
values[i] = pic->data[ywb + (x / 8) + (pic->plane_step * i)];
|
|
}
|
|
|
|
bit_x = 7 - (x & 7);
|
|
mask = 1 << bit_x;
|
|
value = ((values[0] & mask) >> bit_x) << 0 |
|
|
((values[1] & mask) >> bit_x) << 1 |
|
|
((values[2] & mask) >> bit_x) << 2 |
|
|
((values[3] & mask) >> bit_x) << 3;
|
|
value &= 15;
|
|
|
|
gfx_buf[yw + x] = (byte)value;
|
|
}
|
|
}
|
|
}
|
|
|
|
byte *Magnetic::ms_extract(uint32 pic, uint16 *w, uint16 *h, uint16 *pal, byte *is_anim) {
|
|
if (is_anim)
|
|
*is_anim = 0;
|
|
|
|
if (gfx_buf) {
|
|
switch (gfx_ver) {
|
|
case 1:
|
|
return ms_extract1((byte)pic, w, h, pal);
|
|
case 2:
|
|
return ms_extract2((const char *)(code + pic), w, h, pal, is_anim);
|
|
}
|
|
}
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
byte Magnetic::ms_animate(ms_position **positions, uint16 *count) {
|
|
byte got_anim = 0;
|
|
uint16 i, j, ttable;
|
|
|
|
if ((gfx_buf == 0) || (gfx2_buf == 0) || (gfx_ver != 2))
|
|
return 0;
|
|
if ((pos_table_size == 0) || (command_index < 0))
|
|
return 0;
|
|
|
|
*count = 0;
|
|
*positions = (struct ms_position*)0;
|
|
|
|
while (got_anim == 0)
|
|
{
|
|
if (pos_table_max >= 0)
|
|
{
|
|
if (pos_table_index < pos_table_max)
|
|
{
|
|
for (i = 0; i < pos_table_size; i++)
|
|
{
|
|
if (anim_table[i].flag > -1)
|
|
{
|
|
if (*count >= MAX_FRAMES)
|
|
{
|
|
error("returned animation array too short");
|
|
return 0;
|
|
}
|
|
|
|
pos_array[*count] = pos_table[i][anim_table[i].flag];
|
|
|
|
(*count)++;
|
|
|
|
if (anim_table[i].flag < (pos_table_count[i] - 1))
|
|
anim_table[i].flag++;
|
|
if (anim_table[i].count > 0)
|
|
anim_table[i].count--;
|
|
else
|
|
anim_table[i].flag = -1;
|
|
}
|
|
}
|
|
if (*count > 0)
|
|
{
|
|
*positions = pos_array;
|
|
got_anim = 1;
|
|
}
|
|
pos_table_index++;
|
|
}
|
|
}
|
|
|
|
if (got_anim == 0)
|
|
{
|
|
byte command = command_table[command_index];
|
|
command_index++;
|
|
|
|
pos_table_max = -1;
|
|
pos_table_index = -1;
|
|
|
|
switch (command)
|
|
{
|
|
case 0x00:
|
|
command_index = -1;
|
|
return 0;
|
|
case 0x01:
|
|
ttable = command_table[command_index];
|
|
command_index++;
|
|
|
|
if (ttable - 1 >= MAX_POSITIONS)
|
|
{
|
|
error("animation table too short");
|
|
return 0;
|
|
}
|
|
|
|
anim_table[ttable - 1].flag = (int16)(command_table[command_index] - 1);
|
|
command_index++;
|
|
anim_table[ttable - 1].count = (int16)(command_table[command_index] - 1);
|
|
command_index++;
|
|
|
|
/* Workaround for Wonderland "catter" animation */
|
|
if (v4_id == 0)
|
|
{
|
|
if (gfx2_name == "catter") {
|
|
if (command_index == 96)
|
|
anim_table[ttable - 1].count = 9;
|
|
if (command_index == 108)
|
|
anim_table[ttable - 1].flag = -1;
|
|
if (command_index == 156)
|
|
anim_table[ttable - 1].flag = -1;
|
|
}
|
|
}
|
|
break;
|
|
case 0x02:
|
|
pos_table_max = command_table[command_index];
|
|
pos_table_index = 0;
|
|
command_index++;
|
|
break;
|
|
case 0x03:
|
|
if (v4_id == 0)
|
|
{
|
|
command_index = -1;
|
|
return 0;
|
|
}
|
|
else
|
|
{
|
|
command_index = 0;
|
|
anim_repeat = 1;
|
|
pos_table_index = -1;
|
|
pos_table_max = -1;
|
|
for (j = 0; j < MAX_POSITIONS; j++)
|
|
{
|
|
anim_table[j].flag = -1;
|
|
anim_table[j].count = -1;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case 0x04:
|
|
command_index += 3;
|
|
return 0;
|
|
case 0x05:
|
|
ttable = next_table;
|
|
command_index++;
|
|
|
|
anim_table[ttable - 1].flag = 0;
|
|
anim_table[ttable - 1].count = command_table[command_index];
|
|
|
|
pos_table_max = command_table[command_index];
|
|
pos_table_index = 0;
|
|
command_index++;
|
|
command_index++;
|
|
next_table++;
|
|
break;
|
|
default:
|
|
error("unknown animation command");
|
|
command_index = -1;
|
|
return 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
return got_anim;
|
|
}
|
|
|
|
byte *Magnetic::ms_get_anim_frame(int16 number, uint16 *width, uint16 *height, byte **mask) {
|
|
if (number >= 0)
|
|
{
|
|
extract_frame(anim_frame_table + number);
|
|
*width = anim_frame_table[number].width;
|
|
*height = anim_frame_table[number].height;
|
|
*mask = anim_frame_table[number].mask;
|
|
return gfx_buf;
|
|
}
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
} // End of namespace Magnetic
|
|
} // End of namespace Glk
|