aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--engines/glk/magnetic/graphics.cpp427
-rw-r--r--engines/glk/magnetic/magnetic.h25
-rw-r--r--engines/glk/magnetic/magnetic_types.h4
-rw-r--r--engines/glk/magnetic/sound.cpp40
4 files changed, 493 insertions, 3 deletions
diff --git a/engines/glk/magnetic/graphics.cpp b/engines/glk/magnetic/graphics.cpp
index d10dc3d1b7..1c635782c2 100644
--- a/engines/glk/magnetic/graphics.cpp
+++ b/engines/glk/magnetic/graphics.cpp
@@ -25,7 +25,6 @@
namespace Glk {
namespace Magnetic {
-
byte Magnetic::init_gfx1(size_t size) {
if (!(gfx_buf = new byte[MAX_PICTURE_SIZE]))
return 1;
@@ -75,5 +74,431 @@ 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, datasize, upsize, offset;
+
+ 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, command_count;
+ uint16 value1, value2;
+
+ 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
diff --git a/engines/glk/magnetic/magnetic.h b/engines/glk/magnetic/magnetic.h
index 27bdda0c9c..6cf05c942e 100644
--- a/engines/glk/magnetic/magnetic.h
+++ b/engines/glk/magnetic/magnetic.h
@@ -135,6 +135,27 @@ private:
*/
void ms_showpic(int c, byte mode);
+ /**
+ * Returns true if a given line is blank
+ */
+ bool is_blank(uint16 line, uint16 width) const;
+
+ byte *ms_extract1(byte pic, uint16 *w, uint16 *h, uint16 *pal);
+
+ int16 find_name_in_header(const Common::String &name, bool upper);
+
+ void extract_frame(const picture *pic);
+
+ byte *ms_extract2(const char *name, uint16 *w, uint16 *h, uint16 *pal, byte *is_anim);
+
+ byte *ms_extract(uint32 pic, uint16 *w, uint16 *h, uint16 *pal, byte *is_anim);
+
+ byte ms_animate(ms_position **positions, uint16 *count);
+
+ byte *ms_get_anim_frame(int16 number, uint16 *width, uint16 *height, byte **mask);
+
+ bool ms_anim_is_repeating() const { return anim_repeat; }
+
void write_reg(int i, int s, uint32 val) {
// TODO
}
@@ -148,6 +169,10 @@ private:
byte init_snd(size_t size);
+ int16 find_name_in_sndheader(const Common::String &name);
+
+ byte *sound_extract(const Common::String &name, uint32 *length, uint16 *tempo);
+
/**@}*/
public:
/**
diff --git a/engines/glk/magnetic/magnetic_types.h b/engines/glk/magnetic/magnetic_types.h
index b9933a3f95..cfef76557e 100644
--- a/engines/glk/magnetic/magnetic_types.h
+++ b/engines/glk/magnetic/magnetic_types.h
@@ -40,8 +40,8 @@ namespace Magnetic {
#define MAX_HITEMS 25
struct lookup {
- uint16 flag;
- uint16 count;
+ int16 flag;
+ int16 count;
lookup() : flag(0), count(0) {}
};
diff --git a/engines/glk/magnetic/sound.cpp b/engines/glk/magnetic/sound.cpp
index 7977920536..3ea74fb8fe 100644
--- a/engines/glk/magnetic/sound.cpp
+++ b/engines/glk/magnetic/sound.cpp
@@ -48,5 +48,45 @@ byte Magnetic::init_snd(size_t size) {
return 2;
}
+int16 Magnetic::find_name_in_sndheader(const Common::String &name) {
+ int16 header_pos = 0;
+
+ while (header_pos < snd_hsize) {
+ const char *hname = (const char *)(snd_hdr + header_pos);
+ if (name == hname)
+ return header_pos;
+ header_pos += 18;
+ }
+
+ return -1;
+}
+
+byte *Magnetic::sound_extract(const Common::String &name, uint32 *length, uint16 *tempo) {
+ uint32 offset = 0;
+ int16 header_pos = -1;
+
+ if (header_pos < 0)
+ header_pos = find_name_in_sndheader(name);
+ if (header_pos < 0)
+ return 0;
+
+ *tempo = READ_BE_UINT16(snd_hdr + header_pos + 8);
+ offset = READ_BE_UINT32(snd_hdr + header_pos + 10);
+ *length = READ_BE_UINT32(snd_hdr + header_pos + 14);
+
+ if (offset != 0) {
+ if (!snd_buf)
+ return nullptr;
+ if (!_sndFile.seek(offset))
+ return nullptr;
+ if (_sndFile.read(snd_buf, *length) != *length)
+ return nullptr;
+
+ return snd_buf;
+ }
+
+ return nullptr;
+}
+
} // End of namespace Magnetic
} // End of namespace Glk