Initial commit

This commit is contained in:
2026-02-02 04:50:13 +01:00
commit 5b11698731
22592 changed files with 7677434 additions and 0 deletions

View File

@@ -0,0 +1,845 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
#include "common/endian.h"
#include "common/util.h"
#include "common/textconsole.h"
#include "engines/grim/movie/codecs/blocky16.h"
namespace Grim {
#if defined(SCUMM_NEED_ALIGNMENT)
#define COPY_4X1_LINE(dst, src) \
do { \
(dst)[0] = (src)[0]; \
(dst)[1] = (src)[1]; \
(dst)[2] = (src)[2]; \
(dst)[3] = (src)[3]; \
} while (0)
#if defined(SCUMM_BIG_ENDIAN)
#define WRITE_2X1_LINE(dst, v) \
do { \
(dst)[0] = (byte)((v >> 8) & 0xFF); \
(dst)[1] = (byte)((v >> 0) & 0xFF); \
} while (0)
#define WRITE_4X1_LINE(dst, v) \
do { \
(dst)[0] = (byte)((v >> 24) & 0xFF); \
(dst)[1] = (byte)((v >> 16) & 0XFF); \
(dst)[2] = (byte)((v >> 8) & 0xFF); \
(dst)[3] = (byte)((v >> 0) & 0xFF); \
} while (0)
#else /* SCUMM_BIG_ENDIAN */
#define WRITE_2X1_LINE(dst, v) \
do { \
(dst)[0] = (byte)((v >> 0) & 0xFF); \
(dst)[1] = (byte)((v >> 8) & 0xFF); \
} while (0)
#define WRITE_4X1_LINE(dst, v) \
do { \
(dst)[0] = (byte)((v >> 0) & 0xFF); \
(dst)[1] = (byte)((v >> 8) & 0XFF); \
(dst)[2] = (byte)((v >> 16) & 0xFF); \
(dst)[3] = (byte)((v >> 24) & 0xFF); \
} while (0)
#endif
#else /* SCUMM_NEED_ALIGNMENT */
#define COPY_4X1_LINE(dst, src) \
*(uint32 *)(dst) = *(const uint32 *)(src);
#define WRITE_2X1_LINE(dst, v) \
*(uint16 *)(dst) = v;
#define WRITE_4X1_LINE(dst, v) \
*(uint32 *)(dst) = v;
#endif
static int8 blocky16_table_small1[] = {
0, 1, 2, 3, 3, 3, 3, 2, 1, 0, 0, 0, 1, 2, 2, 1,
};
static int8 blocky16_table_small2[] = {
0, 0, 0, 0, 1, 2, 3, 3, 3, 3, 2, 1, 1, 1, 2, 2,
};
static int8 blocky16_table_big1[] = {
0, 2, 5, 7, 7, 7, 7, 7, 7, 5, 2, 0, 0, 0, 0, 0,
};
static int8 blocky16_table_big2[] = {
0, 0, 0, 0, 1, 3, 4, 6, 7, 7, 7, 7, 6, 4, 3, 1,
};
static int8 blocky16_table[] = {
0, 0, -1, -43, 6, -43, -9, -42, 13, -41,
-16, -40, 19, -39, -23, -36, 26, -34, -2, -33,
4, -33, -29, -32, -9, -32, 11, -31, -16, -29,
32, -29, 18, -28, -34, -26, -22, -25, -1, -25,
3, -25, -7, -24, 8, -24, 24, -23, 36, -23,
-12, -22, 13, -21, -38, -20, 0, -20, -27, -19,
-4, -19, 4, -19, -17, -18, -8, -17, 8, -17,
18, -17, 28, -17, 39, -17, -12, -15, 12, -15,
-21, -14, -1, -14, 1, -14, -41, -13, -5, -13,
5, -13, 21, -13, -31, -12, -15, -11, -8, -11,
8, -11, 15, -11, -2, -10, 1, -10, 31, -10,
-23, -9, -11, -9, -5, -9, 4, -9, 11, -9,
42, -9, 6, -8, 24, -8, -18, -7, -7, -7,
-3, -7, -1, -7, 2, -7, 18, -7, -43, -6,
-13, -6, -4, -6, 4, -6, 8, -6, -33, -5,
-9, -5, -2, -5, 0, -5, 2, -5, 5, -5,
13, -5, -25, -4, -6, -4, -3, -4, 3, -4,
9, -4, -19, -3, -7, -3, -4, -3, -2, -3,
-1, -3, 0, -3, 1, -3, 2, -3, 4, -3,
6, -3, 33, -3, -14, -2, -10, -2, -5, -2,
-3, -2, -2, -2, -1, -2, 0, -2, 1, -2,
2, -2, 3, -2, 5, -2, 7, -2, 14, -2,
19, -2, 25, -2, 43, -2, -7, -1, -3, -1,
-2, -1, -1, -1, 0, -1, 1, -1, 2, -1,
3, -1, 10, -1, -5, 0, -3, 0, -2, 0,
-1, 0, 1, 0, 2, 0, 3, 0, 5, 0,
7, 0, -10, 1, -7, 1, -3, 1, -2, 1,
-1, 1, 0, 1, 1, 1, 2, 1, 3, 1,
-43, 2, -25, 2, -19, 2, -14, 2, -5, 2,
-3, 2, -2, 2, -1, 2, 0, 2, 1, 2,
2, 2, 3, 2, 5, 2, 7, 2, 10, 2,
14, 2, -33, 3, -6, 3, -4, 3, -2, 3,
-1, 3, 0, 3, 1, 3, 2, 3, 4, 3,
19, 3, -9, 4, -3, 4, 3, 4, 7, 4,
25, 4, -13, 5, -5, 5, -2, 5, 0, 5,
2, 5, 5, 5, 9, 5, 33, 5, -8, 6,
-4, 6, 4, 6, 13, 6, 43, 6, -18, 7,
-2, 7, 0, 7, 2, 7, 7, 7, 18, 7,
-24, 8, -6, 8, -42, 9, -11, 9, -4, 9,
5, 9, 11, 9, 23, 9, -31, 10, -1, 10,
2, 10, -15, 11, -8, 11, 8, 11, 15, 11,
31, 12, -21, 13, -5, 13, 5, 13, 41, 13,
-1, 14, 1, 14, 21, 14, -12, 15, 12, 15,
-39, 17, -28, 17, -18, 17, -8, 17, 8, 17,
17, 18, -4, 19, 0, 19, 4, 19, 27, 19,
38, 20, -13, 21, 12, 22, -36, 23, -24, 23,
-8, 24, 7, 24, -3, 25, 1, 25, 22, 25,
34, 26, -18, 28, -32, 29, 16, 29, -11, 31,
9, 32, 29, 32, -4, 33, 2, 33, -26, 34,
23, 36, -19, 39, 16, 40, -13, 41, 9, 42,
-6, 43, 1, 43, 0, 0, 0, 0, 0, 0,
0, 0, 0
};
void Blocky16::makeTablesInterpolation(int param) {
int32 variable1, variable2;
int32 b1, b2;
int32 value_table47_1_2, value_table47_1_1, value_table47_2_2, value_table47_2_1;
int32 tableSmallBig[64], tmp, s;
int8 *table47_1 = nullptr, *table47_2 = nullptr;
int32 *ptr_small_big;
byte *ptr;
int i, x, y;
if (param == 8) {
table47_1 = blocky16_table_big1;
table47_2 = blocky16_table_big2;
ptr = _tableBig + 384;
for (i = 0; i < 256; i++) {
*ptr = 0;
ptr += 388;
}
ptr = _tableBig + 385;
for (i = 0; i < 256; i++) {
*ptr = 0;
ptr += 388;
}
} else if (param == 4) {
table47_1 = blocky16_table_small1;
table47_2 = blocky16_table_small2;
ptr = _tableSmall + 96;
for (i = 0; i < 256; i++) {
*ptr = 0;
ptr += 128;
}
ptr = _tableSmall + 97;
for (i = 0; i < 256; i++) {
*ptr = 0;
ptr += 128;
}
} else {
error("Blocky16::makeTablesInterpolation: unknown param %d", param);
}
s = 0;
for (x = 0; x < 16; x++) {
value_table47_1_1 = table47_1[x];
value_table47_2_1 = table47_2[x];
for (y = 0; y < 16; y++) {
value_table47_1_2 = table47_1[y];
value_table47_2_2 = table47_2[y];
if (value_table47_2_1 == 0) {
b1 = 0;
} else if (value_table47_2_1 == param - 1) {
b1 = 1;
} else if (value_table47_1_1 == 0) {
b1 = 2;
} else if (value_table47_1_1 == param - 1) {
b1 = 3;
} else {
b1 = 4;
}
if (value_table47_2_2 == 0) {
b2 = 0;
} else if (value_table47_2_2 == param - 1) {
b2 = 1;
} else if (value_table47_1_2 == 0) {
b2 = 2;
} else if (value_table47_1_2 == param - 1) {
b2 = 3;
} else {
b2 = 4;
}
memset(tableSmallBig, 0, param * param * 4);
variable2 = abs(value_table47_2_2 - value_table47_2_1);
tmp = abs(value_table47_1_2 - value_table47_1_1);
if (variable2 <= tmp) {
variable2 = tmp;
}
for (variable1 = 0; variable1 <= variable2; variable1++) {
int32 variable3, variable4;
if (variable2 > 0) {
// Linearly interpolate between value_table47_1_1 and value_table47_1_2
// respectively value_table47_2_1 and value_table47_2_2.
variable4 = (value_table47_1_1 * variable1 + value_table47_1_2 * (variable2 - variable1) + variable2 / 2) / variable2;
variable3 = (value_table47_2_1 * variable1 + value_table47_2_2 * (variable2 - variable1) + variable2 / 2) / variable2;
} else {
variable4 = value_table47_1_1;
variable3 = value_table47_2_1;
}
ptr_small_big = &tableSmallBig[param * variable3 + variable4];
*ptr_small_big = 1;
if ((b1 == 2 && b2 == 3) || (b2 == 2 && b1 == 3) ||
(b1 == 0 && b2 != 1) || (b2 == 0 && b1 != 1)) {
if (variable3 >= 0) {
i = variable3 + 1;
while (i--) {
*ptr_small_big = 1;
ptr_small_big -= param;
}
}
} else if ((b2 != 0 && b1 == 1) || (b1 != 0 && b2 == 1)) {
if (param > variable3) {
i = param - variable3;
while (i--) {
*ptr_small_big = 1;
ptr_small_big += param;
}
}
} else if ((b1 == 2 && b2 != 3) || (b2 == 2 && b1 != 3)) {
if (variable4 >= 0) {
i = variable4 + 1;
while (i--) {
*(ptr_small_big--) = 1;
}
}
} else if ((b1 == 0 && b2 == 1) || (b2 == 0 && b1 == 1) ||
(b1 == 3 && b2 != 2) || (b2 == 3 && b1 != 2)) {
if (param > variable4) {
i = param - variable4;
while (i--) {
*(ptr_small_big++) = 1;
}
}
}
}
if (param == 8) {
for (i = 64 - 1; i >= 0; i--) {
if (tableSmallBig[i] != 0) {
_tableBig[256 + s + _tableBig[384 + s]] = (byte)i;
_tableBig[384 + s]++;
} else {
_tableBig[320 + s + _tableBig[385 + s]] = (byte)i;
_tableBig[385 + s]++;
}
}
s += 388;
}
if (param == 4) {
for (i = 16 - 1; i >= 0; i--) {
if (tableSmallBig[i] != 0) {
_tableSmall[64 + s + _tableSmall[96 + s]] = (byte)i;
_tableSmall[96 + s]++;
} else {
_tableSmall[80 + s + _tableSmall[97 + s]] = (byte)i;
_tableSmall[97 + s]++;
}
}
s += 128;
}
}
}
}
void Blocky16::makeTables47(int width) {
if (_lastTableWidth == width)
return;
_lastTableWidth = width;
int32 a, c, d;
int16 tmp;
for (int l = 0; l < 512; l += 2) {
_table[l / 2] = (int16)(blocky16_table[l + 1] * width + blocky16_table[l]);
}
a = 0;
c = 0;
do {
for (d = 0; d < _tableSmall[96 + c]; d++) {
tmp = _tableSmall[64 + c + d];
tmp = (int16)((byte)(tmp >> 2) * width + (tmp & 3));
_tableSmall[c + d * 2] = (byte)tmp;
_tableSmall[c + d * 2 + 1] = tmp >> 8;
}
for (d = 0; d < _tableSmall[97 + c]; d++) {
tmp = _tableSmall[80 + c + d];
tmp = (int16)((byte)(tmp >> 2) * width + (tmp & 3));
_tableSmall[32 + c + d * 2] = (byte)tmp;
_tableSmall[32 + c + d * 2 + 1] = tmp >> 8;
}
for (d = 0; d < _tableBig[384 + a]; d++) {
tmp = _tableBig[256 + a + d];
tmp = (int16)((byte)(tmp >> 3) * width + (tmp & 7));
_tableBig[a + d * 2] = (byte)tmp;
_tableBig[a + d * 2 + 1] = tmp >> 8;
}
for (d = 0; d < _tableBig[385 + a]; d++) {
tmp = _tableBig[320 + a + d];
tmp = (int16)((byte)(tmp >> 3) * width + (tmp & 7));
_tableBig[128 + a + d * 2] = (byte)tmp;
_tableBig[128 + a + d * 2 + 1] = tmp >> 8;
}
a += 388;
c += 128;
} while (c < 32768);
}
void Blocky16::level3(byte *d_dst) {
int32 tmp2;
uint32 t;
byte code = *_d_src++;
int i;
if (code <= 0xF5) {
if (code == 0xF5) {
int16 tmp = READ_LE_UINT16(_d_src);
tmp2 = tmp * 2;
_d_src += 2;
} else {
tmp2 = _table[code] * 2;
}
tmp2 += _offset1;
for (i = 0; i < 2; i++) {
COPY_4X1_LINE(d_dst + 0, d_dst + tmp2 + 0);
d_dst += _d_pitch;
}
} else if ((code == 0xFF) || (code == 0xF8)) {
WRITE_2X1_LINE(d_dst + 0, READ_LE_UINT16(_d_src + 0));
WRITE_2X1_LINE(d_dst + 2, READ_LE_UINT16(_d_src + 2));
d_dst += _d_pitch;
WRITE_2X1_LINE(d_dst + 0, READ_LE_UINT16(_d_src + 4));
WRITE_2X1_LINE(d_dst + 2, READ_LE_UINT16(_d_src + 6));
_d_src += 8;
} else if (code == 0xFD) {
t = *_d_src++;
t = READ_LE_UINT16(_param6_7Ptr + t * 2);
t = (t << 16) | t;
for (i = 0; i < 2; i++) {
WRITE_4X1_LINE(d_dst + 0, t);
d_dst += _d_pitch;
}
} else if (code == 0xFE) {
t = READ_LE_UINT16(_d_src);
_d_src += 2;
t = (t << 16) | t;
for (i = 0; i < 2; i++) {
WRITE_4X1_LINE(d_dst + 0, t);
d_dst += _d_pitch;
}
} else if (code == 0xF6) {
tmp2 = _offset2;
for (i = 0; i < 2; i++) {
COPY_4X1_LINE(d_dst + 0, d_dst + tmp2 + 0);
d_dst += _d_pitch;
}
} else if (code == 0xF7) {
tmp2 = READ_LE_UINT32(_d_src);
_d_src += 4;
WRITE_2X1_LINE(d_dst + 0, READ_LE_UINT16(_param6_7Ptr + (byte)tmp2 * 2));
WRITE_2X1_LINE(d_dst + 2, READ_LE_UINT16(_param6_7Ptr + (byte)(tmp2 >> 8) * 2));
tmp2 >>= 16;
d_dst += _d_pitch;
WRITE_2X1_LINE(d_dst + 0, READ_LE_UINT16(_param6_7Ptr + (byte)tmp2 * 2));
WRITE_2X1_LINE(d_dst + 2, READ_LE_UINT16(_param6_7Ptr + (byte)(tmp2 >> 8) * 2));
} else if ((code >= 0xF9) && (code <= 0xFC)) {
t = READ_LE_UINT16(_paramPtr + code * 2);
t = (t << 16) | t;
for (i = 0; i < 2; i++) {
WRITE_4X1_LINE(d_dst + 0, t);
d_dst += _d_pitch;
}
}
}
void Blocky16::level2(byte *d_dst) {
int32 tmp2;
uint32 t = 0, val;
byte code = *_d_src++;
int i;
if (code <= 0xF5) {
if (code == 0xF5) {
int16 tmp = READ_LE_UINT16(_d_src);
tmp2 = tmp * 2;
_d_src += 2;
} else {
tmp2 = _table[code] * 2;
}
tmp2 += _offset1;
for (i = 0; i < 4; i++) {
COPY_4X1_LINE(d_dst + 0, d_dst + tmp2 + 0);
COPY_4X1_LINE(d_dst + 4, d_dst + tmp2 + 4);
d_dst += _d_pitch;
}
} else if (code == 0xFF) {
level3(d_dst);
d_dst += 4;
level3(d_dst);
d_dst += _d_pitch * 2 - 4;
level3(d_dst);
d_dst += 4;
level3(d_dst);
} else if (code == 0xF6) {
tmp2 = _offset2;
for (i = 0; i < 4; i++) {
COPY_4X1_LINE(d_dst + 0, d_dst + tmp2 + 0);
COPY_4X1_LINE(d_dst + 4, d_dst + tmp2 + 4);
d_dst += _d_pitch;
}
} else if ((code == 0xF7) || (code == 0xF8)) {
byte tmp = *_d_src++;
if (code == 0xF8) {
val = READ_LE_UINT32(_d_src);
_d_src += 4;
} else {
tmp2 = READ_LE_UINT16(_d_src);
val = READ_LE_UINT16(_param6_7Ptr + (byte)(tmp2 >> 8) * 2) << 16;
val |= READ_LE_UINT16(_param6_7Ptr + (byte)tmp2 * 2);
_d_src += 2;
}
byte *tmp_ptr = _tableSmall + (tmp * 128);
byte l = tmp_ptr[96];
int16 *tmp_ptr2 = (int16 *)tmp_ptr;
while (l--) {
WRITE_2X1_LINE(d_dst + READ_LE_UINT16(tmp_ptr2) * 2, val);
tmp_ptr2++;
}
l = tmp_ptr[97];
val >>= 16;
tmp_ptr2 = (int16 *)(tmp_ptr + 32);
while (l--) {
WRITE_2X1_LINE(d_dst + READ_LE_UINT16(tmp_ptr2) * 2, val);
tmp_ptr2++;
}
} else if (code >= 0xF9) {
if (code == 0xFD) {
t = *_d_src++;
t = READ_LE_UINT16(_param6_7Ptr + t * 2);
t = (t << 16) | t;
} else if (code == 0xFE) {
t = READ_LE_UINT16(_d_src);
t = (t << 16) | t;
_d_src += 2;
} else if ((code >= 0xF9) && (code <= 0xFC)) {
t = READ_LE_UINT16(_paramPtr + code * 2);
t = (t << 16) | t;
}
for (i = 0; i < 4; i++) {
WRITE_4X1_LINE(d_dst + 0, t);
WRITE_4X1_LINE(d_dst + 4, t);
d_dst += _d_pitch;
}
}
}
void Blocky16::level1(byte *d_dst) {
int32 tmp2;
uint32 t = 0, val;
byte code = *_d_src++;
int i;
if (code <= 0xF5) {
if (code == 0xF5) {
int16 tmp = READ_LE_UINT16(_d_src);
tmp2 = tmp * 2;
_d_src += 2;
} else {
tmp2 = _table[code] * 2;
}
tmp2 += _offset1;
for (i = 0; i < 8; i++) {
COPY_4X1_LINE(d_dst + 0, d_dst + tmp2 + 0);
COPY_4X1_LINE(d_dst + 4, d_dst + tmp2 + 4);
COPY_4X1_LINE(d_dst + 8, d_dst + tmp2 + 8);
COPY_4X1_LINE(d_dst + 12, d_dst + tmp2 + 12);
d_dst += _d_pitch;
}
} else if (code == 0xFF) {
level2(d_dst);
d_dst += 8;
level2(d_dst);
d_dst += _d_pitch * 4 - 8;
level2(d_dst);
d_dst += 8;
level2(d_dst);
} else if (code == 0xF6) {
tmp2 = _offset2;
for (i = 0; i < 8; i++) {
COPY_4X1_LINE(d_dst + 0, d_dst + tmp2 + 0);
COPY_4X1_LINE(d_dst + 4, d_dst + tmp2 + 4);
COPY_4X1_LINE(d_dst + 8, d_dst + tmp2 + 8);
COPY_4X1_LINE(d_dst + 12, d_dst + tmp2 + 12);
d_dst += _d_pitch;
}
} else if ((code == 0xF7) || (code == 0xF8)) {
byte tmp = *_d_src++;
if (code == 0xF8) {
val = READ_LE_UINT32(_d_src);
_d_src += 4;
} else {
tmp2 = READ_LE_UINT16(_d_src);
val = READ_LE_UINT16(_param6_7Ptr + (byte)(tmp2 >> 8) * 2) << 16;
val |= READ_LE_UINT16(_param6_7Ptr + (byte)tmp2 * 2);
_d_src += 2;
}
byte *tmp_ptr = _tableBig + (tmp * 388);
byte l = tmp_ptr[384];
int16 *tmp_ptr2 = (int16 *)tmp_ptr;
while (l--) {
WRITE_2X1_LINE(d_dst + READ_LE_UINT16(tmp_ptr2) * 2, val);
tmp_ptr2++;
}
l = tmp_ptr[385];
val >>= 16;
tmp_ptr2 = (int16 *)(tmp_ptr + 128);
while (l--) {
WRITE_2X1_LINE(d_dst + READ_LE_UINT16(tmp_ptr2) * 2, val);
tmp_ptr2++;
}
} else if (code >= 0xF9) {
if (code == 0xFD) {
t = *_d_src++;
t = READ_LE_UINT16(_param6_7Ptr + t * 2);
t = (t << 16) | t;
} else if (code == 0xFE) {
t = READ_LE_UINT16(_d_src);
t = (t << 16) | t;
_d_src += 2;
} else if ((code >= 0xF9) && (code <= 0xFC)) {
t = READ_LE_UINT16(_paramPtr + code * 2);
t = (t << 16) | t;
}
for (i = 0; i < 8; i++) {
WRITE_4X1_LINE(d_dst + 0, t);
WRITE_4X1_LINE(d_dst + 4, t);
WRITE_4X1_LINE(d_dst + 8, t);
WRITE_4X1_LINE(d_dst + 12, t);
d_dst += _d_pitch;
}
}
}
void Blocky16::decode2(byte *dst, const byte *src, int width, int height, const byte *param_ptr, const byte *param6_7_ptr) {
_d_src = src;
_paramPtr = param_ptr - 0xf9 - 0xf9;
_param6_7Ptr = param6_7_ptr;
int bh = _blocksHeight;
int next_line = width * 2 * 7;
_d_pitch = width * 2;
do {
int tmp_bw = _blocksWidth;
do {
level1(dst);
dst += 16;
} while (--tmp_bw);
dst += next_line;
} while (--bh);
}
void Blocky16::init(int width, int height) {
deinit();
_width = width;
_height = height;
makeTablesInterpolation(4);
makeTablesInterpolation(8);
_blocksWidth = (width + 7) / 8;
_blocksHeight = (height + 7) / 8;
_frameSize = _width * _height * 2;
// some animations, like tb_kitty.snm don't have a multiple of 8 width or height,
// so set the size of the buffer in _blocksWidth * 8 * _blocksHeight * 8, instead
// of using _frameSize.
int size = _blocksWidth * 8 * _blocksHeight * 8 * 2;
_offset = size - _frameSize;
// workaround for read over buffer by increasing buffer
// 200 bytes is enough for smush anims:
// lol, byeruba, crushed, eldepot, heltrain, hostage
uint32 deltaSize = size * 3 + 200;
_deltaBuf = new byte[deltaSize]();
_deltaBufs[0] = _deltaBuf;
_deltaBufs[1] = _deltaBuf + _frameSize;
_curBuf = _deltaBuf + _frameSize * 2;
}
Blocky16::Blocky16() {
_tableBig = new byte[99328]();
_tableSmall = new byte[32768]();
_deltaBuf = nullptr;
_deltaBufs[0] = nullptr;
_deltaBufs[1] = nullptr;
_curBuf = nullptr;
_prevSeqNb = 0;
_lastTableWidth = 0;
_d_src = nullptr;
_paramPtr = nullptr;
_param6_7Ptr = nullptr;
_blocksHeight = _blocksWidth = 0;
_height = _width = 0;
_offset = _offset1 = _offset2 = 0;
_frameSize = 0;
_d_pitch = 0;
}
void Blocky16::deinit() {
_lastTableWidth = -1;
if (_deltaBuf) {
delete[] _deltaBuf;
_deltaBuf = nullptr;
_deltaBufs[0] = nullptr;
_deltaBufs[1] = nullptr;
}
}
Blocky16::~Blocky16() {
deinit();
if (_tableBig) {
delete[] _tableBig;
_tableBig = nullptr;
}
if (_tableSmall) {
delete[] _tableSmall;
_tableSmall = nullptr;
}
}
static int bomp_left;
static int bomp_num;
static int bomp_color;
static const byte *bomp_src;
static byte bompDecode() {
byte code, result;
const byte *src;
if (bomp_left == 2) {
src = bomp_src;
bomp_num = (*src >> 1) + 1;
code = *(src++) & 1;
bomp_src = src;
if (code != 0) {
bomp_left = 1;
bomp_color = *src++;
bomp_src = src;
} else {
bomp_left = 0;
}
} else {
src = bomp_src;
}
if (bomp_left != 0) {
if (bomp_left - 1 == 0) {
result = bomp_color;
} else {
result = 255;
}
} else {
result = *(src++);
bomp_src = src;
}
bomp_num--;
if (bomp_num == 0) {
bomp_left = 2;
}
return result;
}
static void bompInit(const byte *src) {
bomp_left = 2;
bomp_src = src;
}
static void bompDecodeMain(byte *dst, const byte *src, int size) {
size /= 2;
bompInit(src);
while (size--) {
#ifdef SCUMM_BIG_ENDIAN
*(dst + 1) = bompDecode();
*(dst + 0) = bompDecode();
#else
*(dst + 0) = bompDecode();
*(dst + 1) = bompDecode();
#endif
dst += 2;
}
}
void Blocky16::decode(byte *dst, const byte *src) {
_offset1 = ((_deltaBufs[1] - _curBuf) / 2) * 2;
_offset2 = ((_deltaBufs[0] - _curBuf) / 2) * 2;
int32 seq_nb = READ_LE_UINT16(src + 16);
const byte *gfx_data = src + 560;
if (seq_nb == 0) {
makeTables47(_width);
if (src[32] == src[33]) {
memset(_deltaBufs[0], src[32], _frameSize);
memset(_deltaBufs[1], src[32], _frameSize);
} else {
int count = _frameSize / 2;
uint16 *ptr1 = (uint16 *)_deltaBufs[0];
uint16 *ptr2 = (uint16 *)_deltaBufs[1];
uint16 val = READ_LE_UINT16(src + 32);
while (count--) {
*(uint16 *)(ptr1++) = val;
*(uint16 *)(ptr2++) = val;
};
}
_prevSeqNb = -1;
}
switch (src[18]) {
case 0:
#if defined(SCUMM_BIG_ENDIAN)
for (int i = 0; i < _width * _height; i++) {
((uint16 *)_curBuf)[i] = READ_LE_UINT16(gfx_data + i * 2);
}
#else
memcpy(_curBuf, gfx_data, _frameSize);
#endif
break;
case 1:
error("blocky16: not implemented decode1 proc");
break;
case 2:
if (seq_nb == _prevSeqNb + 1) {
decode2(_curBuf, gfx_data, _width, _height, src + 24, src + 40);
}
break;
case 3:
memcpy(_curBuf, _deltaBufs[1], _frameSize);
break;
case 4:
memcpy(_curBuf, _deltaBufs[0], _frameSize);
break;
case 5:
bompDecodeMain(_curBuf, gfx_data, READ_LE_UINT32(src + 36));
break;
case 6:
{
int count = _frameSize / 2;
uint16 *ptr = (uint16 *)_curBuf;
while (count--) {
int offset = *gfx_data++ * 2;
*(uint16 *)ptr++ = READ_LE_UINT16(src + 40 + offset);
};
break;
}
case 7:
error("blocky16: not implemented decode7 proc");
break;
case 8:
{
bompInit(gfx_data);
int count = _frameSize / 2;
uint16 *ptr = (uint16 *)_curBuf;
while (count--) {
int offset = bompDecode() * 2;
*(uint16 *)ptr++ = READ_LE_UINT16(src + 40 + offset);
};
break;
}
}
memcpy(dst, _curBuf, _frameSize);
if (seq_nb == _prevSeqNb + 1) {
byte *tmp_ptr = nullptr;
if (src[19] == 1) {
tmp_ptr = _curBuf;
_curBuf = _deltaBufs[1];
_deltaBufs[1] = tmp_ptr;
} else if (src[19] == 2) {
tmp_ptr = _deltaBufs[0];
_deltaBufs[0] = _deltaBufs[1];
_deltaBufs[1] = _curBuf - _offset; // subtract the offset here or else a black bar will appear
_curBuf = tmp_ptr; // on top of tb_kitty.snm.
}
}
_prevSeqNb = seq_nb;
}
} // end of namespace Grim

View File

@@ -0,0 +1,65 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef GRIM_BLOCKY16_H
#define GRIM_BLOCKY16_H
#include "common/scummsys.h"
namespace Grim {
class Blocky16 {
private:
byte *_deltaBufs[2];
byte *_deltaBuf;
byte *_curBuf;
int32 _prevSeqNb;
int _lastTableWidth;
const byte *_d_src, *_paramPtr, *_param6_7Ptr;
int _d_pitch;
int32 _offset1, _offset2;
byte *_tableBig;
byte *_tableSmall;
int16 _table[256];
int32 _frameSize;
int _offset;
int _width, _height;
int _blocksWidth, _blocksHeight;
void makeTablesInterpolation(int param);
void makeTables47(int width);
void level1(byte *d_dst);
void level2(byte *d_dst);
void level3(byte *d_dst);
void decode2(byte *dst, const byte *src, int width, int height, const byte *param_ptr, const byte *param6_7_ptr);
public:
Blocky16();
~Blocky16();
void init(int width, int height);
void deinit();
void decode(byte *dst, const byte *src);
};
} // end of namespace Grim
#endif

View File

@@ -0,0 +1,659 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
#include "common/endian.h"
#include "common/util.h"
#include "common/textconsole.h"
#include "engines/grim/movie/codecs/blocky8.h"
namespace Grim {
#if defined(SCUMM_NEED_ALIGNMENT)
#define COPY_4X1_LINE(dst, src) \
do { \
(dst)[0] = (src)[0]; \
(dst)[1] = (src)[1]; \
(dst)[2] = (src)[2]; \
(dst)[3] = (src)[3]; \
} while (0)
#define COPY_2X1_LINE(dst, src) \
do { \
(dst)[0] = (src)[0]; \
(dst)[1] = (src)[1]; \
} while (0)
#else /* SCUMM_NEED_ALIGNMENT */
#define COPY_4X1_LINE(dst, src) \
*(uint32 *)(dst) = *(const uint32 *)(src)
#define COPY_2X1_LINE(dst, src) \
*(uint16 *)(dst) = *(const uint16 *)(src)
#endif
#define FILL_4X1_LINE(dst, val) \
do { \
(dst)[0] = val; \
(dst)[1] = val; \
(dst)[2] = val; \
(dst)[3] = val; \
} while (0)
#define FILL_2X1_LINE(dst, val) \
do { \
(dst)[0] = val; \
(dst)[1] = val; \
} while (0)
static const int8 blocky8_table_small1[] = {
0, 1, 2, 3, 3, 3, 3, 2, 1, 0, 0, 0, 1, 2, 2, 1,
};
static const int8 blocky8_table_small2[] = {
0, 0, 0, 0, 1, 2, 3, 3, 3, 3, 2, 1, 1, 1, 2, 2,
};
static const int8 blocky8_table_big1[] = {
0, 2, 5, 7, 7, 7, 7, 7, 7, 5, 2, 0, 0, 0, 0, 0,
};
static const int8 blocky8_table_big2[] = {
0, 0, 0, 0, 1, 3, 4, 6, 7, 7, 7, 7, 6, 4, 3, 1,
};
static const int8 blocky8_table[] = {
0, 0, -1, -43, 6, -43, -9, -42, 13, -41,
-16, -40, 19, -39, -23, -36, 26, -34, -2, -33,
4, -33, -29, -32, -9, -32, 11, -31, -16, -29,
32, -29, 18, -28, -34, -26, -22, -25, -1, -25,
3, -25, -7, -24, 8, -24, 24, -23, 36, -23,
-12, -22, 13, -21, -38, -20, 0, -20, -27, -19,
-4, -19, 4, -19, -17, -18, -8, -17, 8, -17,
18, -17, 28, -17, 39, -17, -12, -15, 12, -15,
-21, -14, -1, -14, 1, -14, -41, -13, -5, -13,
5, -13, 21, -13, -31, -12, -15, -11, -8, -11,
8, -11, 15, -11, -2, -10, 1, -10, 31, -10,
-23, -9, -11, -9, -5, -9, 4, -9, 11, -9,
42, -9, 6, -8, 24, -8, -18, -7, -7, -7,
-3, -7, -1, -7, 2, -7, 18, -7, -43, -6,
-13, -6, -4, -6, 4, -6, 8, -6, -33, -5,
-9, -5, -2, -5, 0, -5, 2, -5, 5, -5,
13, -5, -25, -4, -6, -4, -3, -4, 3, -4,
9, -4, -19, -3, -7, -3, -4, -3, -2, -3,
-1, -3, 0, -3, 1, -3, 2, -3, 4, -3,
6, -3, 33, -3, -14, -2, -10, -2, -5, -2,
-3, -2, -2, -2, -1, -2, 0, -2, 1, -2,
2, -2, 3, -2, 5, -2, 7, -2, 14, -2,
19, -2, 25, -2, 43, -2, -7, -1, -3, -1,
-2, -1, -1, -1, 0, -1, 1, -1, 2, -1,
3, -1, 10, -1, -5, 0, -3, 0, -2, 0,
-1, 0, 1, 0, 2, 0, 3, 0, 5, 0,
7, 0, -10, 1, -7, 1, -3, 1, -2, 1,
-1, 1, 0, 1, 1, 1, 2, 1, 3, 1,
-43, 2, -25, 2, -19, 2, -14, 2, -5, 2,
-3, 2, -2, 2, -1, 2, 0, 2, 1, 2,
2, 2, 3, 2, 5, 2, 7, 2, 10, 2,
14, 2, -33, 3, -6, 3, -4, 3, -2, 3,
-1, 3, 0, 3, 1, 3, 2, 3, 4, 3,
19, 3, -9, 4, -3, 4, 3, 4, 7, 4,
25, 4, -13, 5, -5, 5, -2, 5, 0, 5,
2, 5, 5, 5, 9, 5, 33, 5, -8, 6,
-4, 6, 4, 6, 13, 6, 43, 6, -18, 7,
-2, 7, 0, 7, 2, 7, 7, 7, 18, 7,
-24, 8, -6, 8, -42, 9, -11, 9, -4, 9,
5, 9, 11, 9, 23, 9, -31, 10, -1, 10,
2, 10, -15, 11, -8, 11, 8, 11, 15, 11,
31, 12, -21, 13, -5, 13, 5, 13, 41, 13,
-1, 14, 1, 14, 21, 14, -12, 15, 12, 15,
-39, 17, -28, 17, -18, 17, -8, 17, 8, 17,
17, 18, -4, 19, 0, 19, 4, 19, 27, 19,
38, 20, -13, 21, 12, 22, -36, 23, -24, 23,
-8, 24, 7, 24, -3, 25, 1, 25, 22, 25,
34, 26, -18, 28, -32, 29, 16, 29, -11, 31,
9, 32, 29, 32, -4, 33, 2, 33, -26, 34,
23, 36, -19, 39, 16, 40, -13, 41, 9, 42,
-6, 43, 1, 43, 0, 0, 0, 0, 0, 0,
0, 0, 0
};
void Blocky8::makeTablesInterpolation(int param) {
int32 variable1, variable2;
int32 b1, b2;
int32 value_table47_1_2, value_table47_1_1, value_table47_2_2, value_table47_2_1;
int32 tableSmallBig[64], tmp, s;
const int8 *table47_1 = nullptr, *table47_2 = nullptr;
int32 *ptr_small_big;
byte *ptr;
int i, x, y;
if (param == 8) {
table47_1 = blocky8_table_big1;
table47_2 = blocky8_table_big2;
ptr = _tableBig;
for (i = 0; i < 256; i++) {
ptr[384] = 0;
ptr[385] = 0;
ptr += 388;
}
} else if (param == 4) {
table47_1 = blocky8_table_small1;
table47_2 = blocky8_table_small2;
ptr = _tableSmall;
for (i = 0; i < 256; i++) {
ptr[96] = 0;
ptr[97] = 0;
ptr += 128;
}
} else {
error("Blocky8::makeTablesInterpolation: unknown param %d", param);
}
s = 0;
for (x = 0; x < 16; x++) {
value_table47_1_1 = table47_1[x];
value_table47_2_1 = table47_2[x];
for (y = 0; y < 16; y++) {
value_table47_1_2 = table47_1[y];
value_table47_2_2 = table47_2[y];
if (value_table47_2_1 == 0) {
b1 = 0;
} else if (value_table47_2_1 == param - 1) {
b1 = 1;
} else if (value_table47_1_1 == 0) {
b1 = 2;
} else if (value_table47_1_1 == param - 1) {
b1 = 3;
} else {
b1 = 4;
}
if (value_table47_2_2 == 0) {
b2 = 0;
} else if (value_table47_2_2 == param - 1) {
b2 = 1;
} else if (value_table47_1_2 == 0) {
b2 = 2;
} else if (value_table47_1_2 == param - 1) {
b2 = 3;
} else {
b2 = 4;
}
memset(tableSmallBig, 0, param * param * 4);
variable2 = ABS(value_table47_2_2 - value_table47_2_1);
tmp = ABS(value_table47_1_2 - value_table47_1_1);
if (variable2 <= tmp) {
variable2 = tmp;
}
for (variable1 = 0; variable1 <= variable2; variable1++) {
int32 variable3, variable4;
if (variable2 > 0) {
// Linearly interpolate between value_table47_1_1 and value_table47_1_2
// respectively value_table47_2_1 and value_table47_2_2.
variable4 = (value_table47_1_1 * variable1 + value_table47_1_2 * (variable2 - variable1) + variable2 / 2) / variable2;
variable3 = (value_table47_2_1 * variable1 + value_table47_2_2 * (variable2 - variable1) + variable2 / 2) / variable2;
} else {
variable4 = value_table47_1_1;
variable3 = value_table47_2_1;
}
ptr_small_big = &tableSmallBig[param * variable3 + variable4];
*ptr_small_big = 1;
if ((b1 == 2 && b2 == 3) || (b2 == 2 && b1 == 3) ||
(b1 == 0 && b2 != 1) || (b2 == 0 && b1 != 1)) {
if (variable3 >= 0) {
i = variable3 + 1;
while (i--) {
*ptr_small_big = 1;
ptr_small_big -= param;
}
}
} else if ((b2 != 0 && b1 == 1) || (b1 != 0 && b2 == 1)) {
if (param > variable3) {
i = param - variable3;
while (i--) {
*ptr_small_big = 1;
ptr_small_big += param;
}
}
} else if ((b1 == 2 && b2 != 3) || (b2 == 2 && b1 != 3)) {
if (variable4 >= 0) {
i = variable4 + 1;
while (i--) {
*(ptr_small_big--) = 1;
}
}
} else if ((b1 == 0 && b2 == 1) || (b2 == 0 && b1 == 1) ||
(b1 == 3 && b2 != 2) || (b2 == 3 && b1 != 2)) {
if (param > variable4) {
i = param - variable4;
while (i--) {
*(ptr_small_big++) = 1;
}
}
}
}
if (param == 8) {
for (i = 64 - 1; i >= 0; i--) {
if (tableSmallBig[i] != 0) {
_tableBig[256 + s + _tableBig[384 + s]] = (byte)i;
_tableBig[384 + s]++;
} else {
_tableBig[320 + s + _tableBig[385 + s]] = (byte)i;
_tableBig[385 + s]++;
}
}
s += 388;
}
if (param == 4) {
for (i = 16 - 1; i >= 0; i--) {
if (tableSmallBig[i] != 0) {
_tableSmall[64 + s + _tableSmall[96 + s]] = (byte)i;
_tableSmall[96 + s]++;
} else {
_tableSmall[80 + s + _tableSmall[97 + s]] = (byte)i;
_tableSmall[97 + s]++;
}
}
s += 128;
}
}
}
}
void Blocky8::makeTables47(int width) {
if (_lastTableWidth == width)
return;
_lastTableWidth = width;
int32 a, c, d;
int16 tmp;
for (int l = 0; l < 512; l += 2) {
_table[l / 2] = (int16)(blocky8_table[l + 1] * width + blocky8_table[l]);
}
a = 0;
c = 0;
do {
for (d = 0; d < _tableSmall[96 + c]; d++) {
tmp = _tableSmall[64 + c + d];
tmp = (int16)((byte)(tmp >> 2) * width + (tmp & 3));
_tableSmall[c + d * 2] = (byte)tmp;
_tableSmall[c + d * 2 + 1] = tmp >> 8;
}
for (d = 0; d < _tableSmall[97 + c]; d++) {
tmp = _tableSmall[80 + c + d];
tmp = (int16)((byte)(tmp >> 2) * width + (tmp & 3));
_tableSmall[32 + c + d * 2] = (byte)tmp;
_tableSmall[32 + c + d * 2 + 1] = tmp >> 8;
}
for (d = 0; d < _tableBig[384 + a]; d++) {
tmp = _tableBig[256 + a + d];
tmp = (int16)((byte)(tmp >> 3) * width + (tmp & 7));
_tableBig[a + d * 2] = (byte)tmp;
_tableBig[a + d * 2 + 1] = tmp >> 8;
}
for (d = 0; d < _tableBig[385 + a]; d++) {
tmp = _tableBig[320 + a + d];
tmp = (int16)((byte)(tmp >> 3) * width + (tmp & 7));
_tableBig[128 + a + d * 2] = (byte)tmp;
_tableBig[128 + a + d * 2 + 1] = tmp >> 8;
}
a += 388;
c += 128;
} while (c < 32768);
}
#ifdef USE_ARM_SMUSH_ASM
extern "C" {
#ifndef IPHONE
#define ARM_Blocky8_decode2 _ARM_Blocky8_decode2
#endif
}
extern "C" void ARM_Blocky8_decode2(byte *dst, const byte *src, int width, int height, const byte *param_ptr,
int16 *_table, byte *_tableBig, int32 offset1, int32 offset2, byte *_tableSmall);
#define decode2(SRC,DST,WIDTH,HEIGHT,PARAM) \
ARM_Blocky8_decode2(SRC,DST,WIDTH,HEIGHT,PARAM,_table,_tableBig, \
_offset1,_offset2,_tableSmall)
#else
void Blocky8::level3(byte *d_dst) {
int32 tmp;
byte code = *_d_src++;
if (code < 0xF8) {
tmp = _table[code] + _offset1;
COPY_2X1_LINE(d_dst, d_dst + tmp);
COPY_2X1_LINE(d_dst + _d_pitch, d_dst + _d_pitch + tmp);
} else if (code == 0xFF) {
COPY_2X1_LINE(d_dst, _d_src + 0);
COPY_2X1_LINE(d_dst + _d_pitch, _d_src + 2);
_d_src += 4;
} else if (code == 0xFE) {
byte t = *_d_src++;
FILL_2X1_LINE(d_dst, t);
FILL_2X1_LINE(d_dst + _d_pitch, t);
} else if (code == 0xFC) {
tmp = _offset2;
COPY_2X1_LINE(d_dst, d_dst + tmp);
COPY_2X1_LINE(d_dst + _d_pitch, d_dst + _d_pitch + tmp);
} else {
byte t = _paramPtr[code];
FILL_2X1_LINE(d_dst, t);
FILL_2X1_LINE(d_dst + _d_pitch, t);
}
}
void Blocky8::level2(byte *d_dst) {
int32 tmp;
byte code = *_d_src++;
int i;
if (code < 0xF8) {
tmp = _table[code] + _offset1;
for (i = 0; i < 4; i++) {
COPY_4X1_LINE(d_dst, d_dst + tmp);
d_dst += _d_pitch;
}
} else if (code == 0xFF) {
level3(d_dst);
d_dst += 2;
level3(d_dst);
d_dst += _d_pitch * 2 - 2;
level3(d_dst);
d_dst += 2;
level3(d_dst);
} else if (code == 0xFE) {
byte t = *_d_src++;
for (i = 0; i < 4; i++) {
FILL_4X1_LINE(d_dst, t);
d_dst += _d_pitch;
}
} else if (code == 0xFD) {
byte *tmp_ptr = _tableSmall + *_d_src++ * 128;
int32 l = tmp_ptr[96];
byte val = *_d_src++;
int16 *tmp_ptr2 = (int16 *)tmp_ptr;
while (l--) {
*(d_dst + READ_LE_UINT16(tmp_ptr2)) = val;
tmp_ptr2++;
}
l = tmp_ptr[97];
val = *_d_src++;
tmp_ptr2 = (int16 *)(tmp_ptr + 32);
while (l--) {
*(d_dst + READ_LE_UINT16(tmp_ptr2)) = val;
tmp_ptr2++;
}
} else if (code == 0xFC) {
tmp = _offset2;
for (i = 0; i < 4; i++) {
COPY_4X1_LINE(d_dst, d_dst + tmp);
d_dst += _d_pitch;
}
} else {
byte t = _paramPtr[code];
for (i = 0; i < 4; i++) {
FILL_4X1_LINE(d_dst, t);
d_dst += _d_pitch;
}
}
}
void Blocky8::level1(byte *d_dst) {
int32 tmp, tmp2;
byte code = *_d_src++;
int i;
if (code < 0xF8) {
tmp2 = _table[code] + _offset1;
for (i = 0; i < 8; i++) {
COPY_4X1_LINE(d_dst + 0, d_dst + tmp2);
COPY_4X1_LINE(d_dst + 4, d_dst + tmp2 + 4);
d_dst += _d_pitch;
}
} else if (code == 0xFF) {
level2(d_dst);
d_dst += 4;
level2(d_dst);
d_dst += _d_pitch * 4 - 4;
level2(d_dst);
d_dst += 4;
level2(d_dst);
} else if (code == 0xFE) {
byte t = *_d_src++;
for (i = 0; i < 8; i++) {
FILL_4X1_LINE(d_dst, t);
FILL_4X1_LINE(d_dst + 4, t);
d_dst += _d_pitch;
}
} else if (code == 0xFD) {
tmp = *_d_src++;
byte *tmp_ptr = _tableBig + tmp * 388;
byte l = tmp_ptr[384];
byte val = *_d_src++;
int16 *tmp_ptr2 = (int16 *)tmp_ptr;
while (l--) {
*(d_dst + READ_LE_UINT16(tmp_ptr2)) = val;
tmp_ptr2++;
}
l = tmp_ptr[385];
val = *_d_src++;
tmp_ptr2 = (int16 *)(tmp_ptr + 128);
while (l--) {
*(d_dst + READ_LE_UINT16(tmp_ptr2)) = val;
tmp_ptr2++;
}
} else if (code == 0xFC) {
tmp2 = _offset2;
for (i = 0; i < 8; i++) {
COPY_4X1_LINE(d_dst + 0, d_dst + tmp2);
COPY_4X1_LINE(d_dst + 4, d_dst + tmp2 + 4);
d_dst += _d_pitch;
}
} else {
byte t = _paramPtr[code];
for (i = 0; i < 8; i++) {
FILL_4X1_LINE(d_dst, t);
FILL_4X1_LINE(d_dst + 4, t);
d_dst += _d_pitch;
}
}
}
void Blocky8::decode2(byte *dst, const byte *src, int width, int height, const byte *param_ptr) {
_d_src = src;
_paramPtr = param_ptr - 0xf8;
int bw = (width + 7) / 8;
int bh = (height + 7) / 8;
int next_line = width * 7;
_d_pitch = width;
do {
int tmp_bw = bw;
do {
level1(dst);
dst += 8;
} while (--tmp_bw);
dst += next_line;
} while (--bh);
}
#endif
static void bompDecodeLine(byte *dst, const byte *src, int len) {
assert(len > 0);
int num;
byte code, color;
while (len > 0) {
code = *src++;
num = (code >> 1) + 1;
if (num > len)
num = len;
len -= num;
if (code & 1) {
color = *src++;
memset(dst, color, num);
} else {
memcpy(dst, src, num);
src += num;
}
dst += num;
}
}
Blocky8::Blocky8() {
_tableBig = new byte[99328]();
_tableSmall = new byte[32768]();
_deltaBuf = nullptr;
_width = -1;
_height = -1;
_frameSize = 0;
_offset1 = 0;
_offset2 = 0;
_prevSeqNb = 0;
_lastTableWidth = 0;
_deltaBufs[0] = nullptr;
_deltaBufs[1] = nullptr;
_curBuf = nullptr;
_d_pitch = 0;
_d_src = nullptr;
_paramPtr = nullptr;
}
void Blocky8::init(int width, int height) {
if (_width == width && _height == height)
return;
deinit();
_width = width;
_height = height;
makeTablesInterpolation(4);
makeTablesInterpolation(8);
_frameSize = _width * _height;
uint32 deltaSize = _frameSize * 3;
_deltaBuf = new byte[deltaSize]();
_deltaBufs[0] = _deltaBuf;
_deltaBufs[1] = _deltaBuf + _frameSize;
_curBuf = _deltaBuf + _frameSize * 2;
}
void Blocky8::deinit() {
_lastTableWidth = -1;
if (_deltaBuf) {
delete[] _deltaBuf;
_deltaBuf = nullptr;
_deltaBufs[0] = nullptr;
_deltaBufs[1] = nullptr;
}
}
Blocky8::~Blocky8() {
deinit();
if (_tableBig) {
delete[] _tableBig;
_tableBig = nullptr;
}
if (_tableSmall) {
delete[] _tableSmall;
_tableSmall = nullptr;
}
}
bool Blocky8::decode(byte *dst, const byte *src) {
if ((_tableBig == nullptr) || (_tableSmall == nullptr) || (_deltaBuf == nullptr))
return false;
_offset1 = _deltaBufs[1] - _curBuf;
_offset2 = _deltaBufs[0] - _curBuf;
int32 seq_nb = READ_LE_UINT16(src + 0);
const byte *gfx_data = src + 26;
if (seq_nb == 0) {
makeTables47(_width);
memset(_deltaBufs[0], src[12], _frameSize);
memset(_deltaBufs[1], src[13], _frameSize);
_prevSeqNb = -1;
}
if ((src[4] & 1) != 0) {
gfx_data += 32896;
}
switch (src[2]) {
case 0:
memcpy(_curBuf, gfx_data, _frameSize);
break;
case 1:
// Used by Outlaws, but not by any SCUMM game.
error("blocky8: not implemented decode1 proc");
break;
case 2:
if (seq_nb == _prevSeqNb + 1) {
decode2(_curBuf, gfx_data, _width, _height, src + 8);
}
break;
case 3:
memcpy(_curBuf, _deltaBufs[1], _frameSize);
break;
case 4:
memcpy(_curBuf, _deltaBufs[0], _frameSize);
break;
case 5:
bompDecodeLine(_curBuf, gfx_data, READ_LE_UINT32(src + 14));
break;
}
memcpy(dst, _curBuf, _frameSize);
if (seq_nb == _prevSeqNb + 1) {
if (src[3] == 1) {
SWAP(_curBuf, _deltaBufs[1]);
} else if (src[3] == 2) {
SWAP(_deltaBufs[0], _deltaBufs[1]);
SWAP(_deltaBufs[1], _curBuf);
}
}
_prevSeqNb = seq_nb;
return true;
}
} // End of namespace Grim

View File

@@ -0,0 +1,63 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef SMUSH_BLOCKY8_H
#define SMUSH_BLOCKY8_H
#include "common/scummsys.h"
namespace Grim {
class Blocky8 {
private:
byte *_deltaBufs[2];
byte *_deltaBuf;
byte *_curBuf;
int32 _prevSeqNb;
int _lastTableWidth;
const byte *_d_src, *_paramPtr;
int _d_pitch;
int32 _offset1, _offset2;
byte *_tableBig;
byte *_tableSmall;
int16 _table[256];
int32 _frameSize;
int _width, _height;
void makeTablesInterpolation(int param);
void makeTables47(int width);
void level1(byte *d_dst);
void level2(byte *d_dst);
void level3(byte *d_dst);
void decode2(byte *dst, const byte *src, int width, int height, const byte *param_ptr);
public:
Blocky8();
~Blocky8();
void init(int width, int height);
void deinit();
bool decode(byte *dst, const byte *src);
};
} // End of namespace Grim
#endif

View File

@@ -0,0 +1,368 @@
@ 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 3 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, see <http://www.gnu.org/licenses/>.
@
@ @author Robin Watts (robin@wss.co.uk)
@
@ This file, provides an ARM optimised version of sections of blocky8.cpp.
@ The algorithm is essentially the same as that within blocky8.cpp
@ so to understand this file you should understand blocky8.cpp first.
.text
.global _ARM_Blocky8_decode2
_ARM_Blocky8_decode2:
@ r0 = dst
@ r1 = src
@ r2 = width
@ r3 = height
@ r4 = param
@ <> = _table
@ <> = _tableBig
@ <> = _offset1
@ <> = _offset2
@ <> = _tableSmall
STMFD r13!,{r2,r4-r11,R14}
LDR r4,[r13,#40] @ r4 = param (40 = (9+1)*4)
@ stall
@ stall
SUB r4,r4,#0xF8
@ r0 = dst
@ r1 = _d_src
@ r2 = _d_pitch
@ r3 = height
@ r4 = param
ADD r7,r2,#7 @ r14 = bw
MOV r7,r7,LSR #3
y_loop:
x_loop:
@ LEVEL 1
LDRB r6,[r1],#1 @ r6 = *_d_src++
@ stall
@ stall
CMP r6,#0xF8
BLT level1codeSMALL
CMP r6,#0xFC
BLT level1codeMID
BEQ level1codeFC
CMP r6,#0xFE
BGT level1codeFF
BEQ level1codeFE
level1codeFD:
LDRB r6,[r1],#1 @ r6 = tmp = *_d_src++
LDR r8,[r13,#48] @ r8 = _tableBig (48 = (9+1+2)*4)
@ stall
ADD r12,r6,r6,LSL #1 @ r12= tmp*3
ADD r6,r6,r12,LSL #5 @ r6 = tmp*97
ADD r8,r8,r6,LSL #2 @ r8 = _tableBig + tmp*388
LDRB r9,[r8,#384] @ r9 = l = tmp_ptr[384]
LDRB r6,[r1],#1 @ r6 = val = *_d_src++
ADD r12,r8,#384 @ r12= &tmp_ptr[384]
@ I don''t really believe the next 2 lines are necessary, but...
CMP r9,#0
BEQ level1codeFD_over1
level1codeFD_loop1:
LDRB r10,[r8],#1
LDRB r11,[r8],#1
SUBS r9,r9,#1
ADD r10,r10,r0
STRB r6,[r10,r11,LSL #8] @ *(_d_dst + (*tmp_ptr2++)) = val
BGT level1codeFD_loop1
level1codeFD_over1:
LDRB r9,[r12,#1] @ r9 = l = tmp_ptr[385]
LDRB r6,[r1],#1 @ r6 = val = *_d_src++
SUB r12,r12,#256 @ r12= &tmp_ptr[128] (256 = 384-128)
@ I don''t really believe the next 2 lines are necessary, but...
CMP r9,#0
BEQ level1codeFD_over2
level1codeFD_loop2:
LDRB r10,[r12],#1
LDRB r11,[r12],#1
SUBS r9,r9,#1
ADD r10,r10,r0
STRB r6,[r10,r11,LSL #8] @ *(_d_dst + (*tmp_ptr2++)) = val
BGT level1codeFD_loop2
level1codeFD_over2:
level1_end:
ADD r0,r0,#8
SUBS r7,r7,#1
BGT x_loop
ADD r7,r2,#7
MOV r7,r7,LSR #3
ADD r0,r0,r2,LSL #3
SUB r0,r0,r7,LSL #3 @ r0 = dst += next_line
SUBS r3,r3,#8 @ if (--bh > 0)
BGT y_loop @ loop back
LDMFD r13!,{r2,r4-r11,PC}
level1codeSMALL:
LDR r8,[r13,#44] @ r8 = _table (44 = (9+1+1)*4)
LDR r9,[r13,#52] @ r9 = _offset1 (52 = (9+1+3)*4)
MOV r6,r6,LSL #1 @ r6 = code<<1
LDRSH r8,[r8,r6] @ tmp2 = _table[code]
level1codeFC:
@ EQ => FC
LDREQ r9,[r13,#56] @ r9 = _offset2 (56 = (9+1+4)*4)
MOVEQ r8,#0
SUB r11,r2,#7 @ r11 = _d_pitch-7
ADD r9,r9,r0 @ tmp2 = _d_dst+_offset
ADD r8,r8,r9 @ tmp2 = _d_dst+_table[code]+_offset
@ r8 = &_dst[tmp2]
MOV r12,#8
level1codeSMALL_loop:
LDRB r5, [r8],#1 @ r5 = d_dst[tmp2]
LDRB r6, [r8],#1 @ r10 = d_dst[tmp2]
LDRB r9, [r8],#1 @ r10 = d_dst[tmp2]
LDRB r10,[r8],#1 @ r10 = d_dst[tmp2]
STRB r5, [r0],#1 @ d_dst[0] = r5
STRB r6, [r0],#1 @ d_dst[1] = r6
STRB r9, [r0],#1 @ d_dst[2] = r9
STRB r10,[r0],#1 @ d_dst[3] = r10
LDRB r5, [r8],#1 @ r5 = d_dst[tmp2]
LDRB r6, [r8],#1 @ r10 = d_dst[tmp2]
LDRB r9, [r8],#1 @ r10 = d_dst[tmp2]
LDRB r10,[r8],r11 @ r10 = d_dst[tmp2]
STRB r5, [r0],#1 @ d_dst[4] = r5
STRB r6, [r0],#1 @ d_dst[5] = r6
STRB r9, [r0],#1 @ d_dst[6] = r9
STRB r10,[r0],r11 @ d_dst[7] = r10 d_dst += d_pitch
SUBS r12,r12,#1
BGT level1codeSMALL_loop
SUB r0,r0,r2,LSL #3 @ revert d_dst
B level1_end
level1codeMID:
@ LT => F8<=code<FC case
@ EQ => FE case
LDRB r6,[r4,r6] @ r6 = t = _paramPtr[code]
level1codeFE:
LDREQB r6,[r1],#1 @ r6 = t = *_d_src++
MOV r12,#8
SUB r11,r2,#7 @ r11 = _d_pitch-7
level1codeMID_loop:
STRB r6,[r0],#1
STRB r6,[r0],#1
STRB r6,[r0],#1
STRB r6,[r0],#1
STRB r6,[r0],#1
STRB r6,[r0],#1
STRB r6,[r0],#1
STRB r6,[r0],r11
SUBS r12,r12,#1
BGT level1codeMID_loop
SUB r0,r0,r2,LSL #3 @ revert d_dst
B level1_end
level1codeFF:
BL level2
ADD r0,r0,#4
BL level2
ADD r0,r0,r2,LSL #2
SUB r0,r0,#4
BL level2
ADD r0,r0,#4
BL level2
SUB r0,r0,#4
SUB r0,r0,r2,LSL #2
B level1_end
level2:
@ r0 = _d_dst
@ r1 = _d_src
@ r2 = _d_pitch
@ r3 = PRESERVE
@ r4 = param
@ r7 = PRESERVE
@ r14= return address
LDRB r6,[r1],#1 @ r6 = *_d_src++
@ stall
@ stall
CMP r6,#0xF8
BLT level2codeSMALL
CMP r6,#0xFC
BLT level2codeMID
BEQ level2codeFC
CMP r6,#0xFE
BGT level2codeFF
BEQ level2codeFE
level2codeFD:
LDRB r6,[r1],#1 @ r6 = tmp = *_d_src++
LDR r8,[r13,#60] @ r8 = _tableSmall (60 = (9+1+5)*4)
@ stall
@ stall
ADD r8,r8,r6,LSL #7 @ r8 = _tableSmall + tmp*128
LDRB r9,[r8,#96] @ r9 = l = tmp_ptr[96]
LDRB r6,[r1],#1 @ r6 = val = *_d_src++
ADD r12,r8,#32 @ r12 = tmp_ptr + 32
@ I don''t really believe the next 2 lines are necessary, but...
CMP r9,#0
BEQ level2codeFD_over1
level2codeFD_loop1:
LDRB r10,[r8],#1
LDRB r11,[r8],#1
SUBS r9,r9,#1
ADD r10,r10,r0
STRB r6,[r10,r11,LSL #8] @ *(_d_dst + (*tmp_ptr2++)) = val
BGT level2codeFD_loop1
level2codeFD_over1:
LDRB r9,[r12,#65] @ r9 = l = tmp_ptr[97] (65 = 97-32)
LDRB r6,[r1],#1 @ r6 = val = *_d_src++
@ I don''t really believe the next 2 lines are necessary, but...
CMP r9,#0
MOVEQ PC,R14
level2codeFD_loop2:
LDRB r10,[r12],#1
LDRB r11,[r12],#1
SUBS r9,r9,#1
ADD r10,r10,r0
STRB r6,[r10,r11,LSL #8] @ *(_d_dst + (*tmp_ptr2++)) = val
BGT level2codeFD_loop2
MOV PC,R14
level2codeSMALL:
LDR r8,[r13,#44] @ r8 = _table (44 = (9+1+1)*4)
LDR r9,[r13,#52] @ r9 = _offset1 (52 = (9+1+3)*4)
MOV r6,r6,LSL #1 @ r6 = code<<1
LDRSH r8,[r8,r6] @ tmp2 = _table[code]
level2codeFC:
@ EQ => FC
LDREQ r9,[r13,#56] @ r9 = _offset2 (56 = (9+1+4)*4)
MOVEQ r8,#0
SUB r11,r2,#3 @ r11 = _d_pitch-3
ADD r9,r9,r0 @ tmp2 = _d_dst + _table[code]
ADD r8,r8,r9 @ tmp2 = _d_dst+_table[code]+_offset1
@ r8 = &_dst[tmp2]
MOV r12,#4
level2codeSMALL_loop:
LDRB r5, [r8],#1 @ r5 = d_dst[tmp2]
LDRB r6, [r8],#1 @ r10 = d_dst[tmp2]
LDRB r9, [r8],#1 @ r10 = d_dst[tmp2]
LDRB r10,[r8],r11 @ r10 = d_dst[tmp2]
STRB r5, [r0],#1 @ d_dst[4] = r5
STRB r6, [r0],#1 @ d_dst[5] = r6
STRB r9, [r0],#1 @ d_dst[6] = r9
STRB r10,[r0],r11 @ d_dst[7] = r10 d_dst += d_pitch
SUBS r12,r12,#1
BGT level2codeSMALL_loop
SUB r0,r0,r2,LSL #2 @ revert d_dst
MOV PC,R14
level2codeMID:
@ LT => F8<=code<FC case
@ EQ => FE case
LDRB r6,[r4,r6] @ r6 = t = _paramPtr[code]
level2codeFE:
LDREQB r6,[r1],#1 @ r6 = t = *_d_src++
MOV r12,#4
SUB r11,r2,#3 @ r11 = _d_pitch-7
level2codeMID_loop:
STRB r6,[r0],#1
STRB r6,[r0],#1
STRB r6,[r0],#1
STRB r6,[r0],r11
SUBS r12,r12,#1
BGT level2codeMID_loop
SUB r0,r0,r2,LSL #2 @ revert d_dst
MOV PC,R14
level2codeFF:
MOV r5,r14
BL level3
ADD r0,r0,#2
BL level3
ADD r0,r0,r2,LSL #1
SUB r0,r0,#2
BL level3
ADD r0,r0,#2
BL level3
SUB r0,r0,#2
SUB r0,r0,r2,LSL #1
MOV PC,R5
level3:
@ r0 = _d_dst
@ r1 = _d_src
@ r2 = _d_pitch
@ r3 = PRESERVE
@ r4 = param
@ r5 = preserve
@ r7 = PRESERVE
@ r14= return address
LDRB r6,[r1],#1 @ r6 = code = *_d_src++
@ stall
@ stall
CMP r6,#0xF8
BLT level3codeSMALL
CMP r6,#0xFC
BLT level3codeMID
BEQ level3codeFC
CMP r6,#0xFE
BGT level3codeFF
level3codeFE:
LDRB r6,[r1],#1 @ r6 = t = *_d_src++
level3codeMID:
@ LT => F8<=code<FC case
@ EQ => FE case
LDRLTB r6,[r4,r6] @ r6 = t = _paramPtr[code]
@ stall
@ stall
STRB r6,[r0,#1]
STRB r6,[r0],r2
STRB r6,[r0,#1]
STRB r6,[r0],-r2
MOV PC,R14
level3codeFF:
LDRB r6,[r1],#1
LDRB r9,[r1],#1
LDRB r10,[r1],#1
LDRB r11,[r1],#1
STRB r9, [r0,#1]
STRB r6, [r0],r2
STRB r11,[r0,#1]
STRB r10,[r0],-r2
MOV PC,R14
level3codeSMALL:
LDR r8,[r13,#44] @ r8 = _table (44 = (9+1+1)*4)
LDR r9,[r13,#52] @ r9 = _offset1 (52 = (9+1+3)*4)
MOV r6,r6,LSL #1 @ r6 = code<<1
LDRSH r8,[r8,r6] @ tmp2 = _table[code]
level3codeFC:
@ EQ => FC
LDREQ r9,[r13,#56] @ r9 = _offset2 (56 = (9+1+4)*4)
MOVEQ r8,#0
ADD r9,r9,r0 @ tmp2 = _d_dst+offset
ADD r8,r8,r9 @ tmp2 = _d_dst+_table[code]+_offset
@ r8 = &_dst[tmp2]
LDRB r6, [r8,#1] @ r6 = d_dst[tmp2+1]
LDRB r9, [r8],r2 @ r9 = d_dst[tmp2+0]
LDRB r10,[r8,#1] @ r10= d_dst[tmp2+dst+1]
LDRB r11,[r8],-r2 @ r11= d_dst[tmp2+dst]
STRB r6, [r0,#1] @ d_dst[1 ] = r6
STRB r9, [r0],r2 @ d_dst[0 ] = r9
STRB r10,[r0,#1] @ d_dst[dst+1] = r10
STRB r11,[r0],-r2 @ d_dst[dst ] = r11
MOV PC,R14

View File

@@ -0,0 +1,570 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
#include "common/endian.h"
#include "common/util.h"
#include "common/textconsole.h"
#include "engines/grim/movie/codecs/codec48.h"
namespace Grim {
Codec48Decoder::Codec48Decoder() {
_frameSize = 640 * 480; // Yes, this is correct. Looks like the buffers are always this size
_curBuf = 0;
_deltaBuf[0] = new byte[_frameSize * 2];
_deltaBuf[1] = _deltaBuf[0] + _frameSize;
_offsetTable = new int16[255];
_tableLastPitch = -1;
_tableLastIndex = -1;
_interTable = nullptr;
}
void Codec48Decoder::init(int width, int height) {
if (_width == width && _height == height)
return;
deinit();
_width = width;
_height = height;
_blockX = (_width + 7) / 8;
_blockY = (_height + 7) / 8;
_pitch = _blockX * 8;
// don't support when this is not equal yet
assert(_width == _pitch);
}
void Codec48Decoder::deinit() {
}
Codec48Decoder::~Codec48Decoder() {
delete[] _deltaBuf[0];
delete[] _offsetTable;
delete[] _interTable;
}
bool Codec48Decoder::decode(byte *dst, const byte *src) {
// The header is identical to codec 37, except the flags field is somewhat different
const byte *gfxData = src + 0x10;
makeTable(_pitch, src[1]);
int16 seqNb = READ_LE_UINT16(src + 2);
if (seqNb == 0)
memset(_deltaBuf[0], 0, _frameSize * 2);
if (src[12] & (1 << 3)) {
// Interpolation table present
if (!_interTable)
_interTable = new byte[65536];
byte *ptr = _interTable;
for (int i = 0; i < 256; i++) {
byte *ptr1 = ptr + i;
byte *ptr2 = ptr + i;
for (int j = 256 - i; j > 0; j--) {
byte pixel = *gfxData++;
*ptr2 = pixel;
*ptr1++ = pixel;
ptr2 += 256;
}
ptr += 256;
}
}
switch (src[0]) {
case 0:
// Raw frame
memcpy(_deltaBuf[_curBuf], gfxData, READ_LE_UINT32(src + 4));
break;
case 2:
// Blast object
bompDecodeLine(_deltaBuf[_curBuf], gfxData, _width * _height);
break;
case 3:
// 8x8 block encoding
if (!(seqNb && seqNb != _prevSeqNb + 1)) {
if (seqNb & 1 || !(src[12] & 1) || src[12] & 0x10)
_curBuf ^= 1;
decode3(_deltaBuf[_curBuf], gfxData, _deltaBuf[_curBuf ^ 1] - _deltaBuf[_curBuf]);
}
break;
case 5:
// Some other encoding, but it's unused. (Good)
warning("SmushDecoder::decode() codec 48 frame type 5 encountered! Please report!");
break;
default:
warning("SmushDecoder::decode() Unknown codec 48 frame type %d", src[0]);
break;
}
_prevSeqNb = seqNb;
memcpy(dst, _deltaBuf[_curBuf], _pitch * _height);
return true;
}
void Codec48Decoder::bompDecodeLine(byte *dst, const byte *src, int len) {
while (len > 0) {
byte code = *src++;
byte num = (code >> 1) + 1;
if (num > len)
num = len;
len -= num;
if (code & 1) {
byte color = *src++;
memset(dst, color, num);
} else {
memcpy(dst, src, num);
src += num;
}
dst += num;
}
}
void Codec48Decoder::makeTable(int pitch, int index) {
// codec48's table is codec47's table appended by the first
// part of codec37's table
// This is essentially Codec37Decoder::makeTable() with a different table
static const int8 table[] = {
0, 0, -1, -43, 6, -43, -9, -42, 13, -41,
-16, -40, 19, -39, -23, -36, 26, -34, -2, -33,
4, -33, -29, -32, -9, -32, 11, -31, -16, -29,
32, -29, 18, -28, -34, -26, -22, -25, -1, -25,
3, -25, -7, -24, 8, -24, 24, -23, 36, -23,
-12, -22, 13, -21, -38, -20, 0, -20, -27, -19,
-4, -19, 4, -19, -17, -18, -8, -17, 8, -17,
18, -17, 28, -17, 39, -17, -12, -15, 12, -15,
-21, -14, -1, -14, 1, -14, -41, -13, -5, -13,
5, -13, 21, -13, -31, -12, -15, -11, -8, -11,
8, -11, 15, -11, -2, -10, 1, -10, 31, -10,
-23, -9, -11, -9, -5, -9, 4, -9, 11, -9,
42, -9, 6, -8, 24, -8, -18, -7, -7, -7,
-3, -7, -1, -7, 2, -7, 18, -7, -43, -6,
-13, -6, -4, -6, 4, -6, 8, -6, -33, -5,
-9, -5, -2, -5, 0, -5, 2, -5, 5, -5,
13, -5, -25, -4, -6, -4, -3, -4, 3, -4,
9, -4, -19, -3, -7, -3, -4, -3, -2, -3,
-1, -3, 0, -3, 1, -3, 2, -3, 4, -3,
6, -3, 33, -3, -14, -2, -10, -2, -5, -2,
-3, -2, -2, -2, -1, -2, 0, -2, 1, -2,
2, -2, 3, -2, 5, -2, 7, -2, 14, -2,
19, -2, 25, -2, 43, -2, -7, -1, -3, -1,
-2, -1, -1, -1, 0, -1, 1, -1, 2, -1,
3, -1, 10, -1, -5, 0, -3, 0, -2, 0,
-1, 0, 1, 0, 2, 0, 3, 0, 5, 0,
7, 0, -10, 1, -7, 1, -3, 1, -2, 1,
-1, 1, 0, 1, 1, 1, 2, 1, 3, 1,
-43, 2, -25, 2, -19, 2, -14, 2, -5, 2,
-3, 2, -2, 2, -1, 2, 0, 2, 1, 2,
2, 2, 3, 2, 5, 2, 7, 2, 10, 2,
14, 2, -33, 3, -6, 3, -4, 3, -2, 3,
-1, 3, 0, 3, 1, 3, 2, 3, 4, 3,
19, 3, -9, 4, -3, 4, 3, 4, 7, 4,
25, 4, -13, 5, -5, 5, -2, 5, 0, 5,
2, 5, 5, 5, 9, 5, 33, 5, -8, 6,
-4, 6, 4, 6, 13, 6, 43, 6, -18, 7,
-2, 7, 0, 7, 2, 7, 7, 7, 18, 7,
-24, 8, -6, 8, -42, 9, -11, 9, -4, 9,
5, 9, 11, 9, 23, 9, -31, 10, -1, 10,
2, 10, -15, 11, -8, 11, 8, 11, 15, 11,
31, 12, -21, 13, -5, 13, 5, 13, 41, 13,
-1, 14, 1, 14, 21, 14, -12, 15, 12, 15,
-39, 17, -28, 17, -18, 17, -8, 17, 8, 17,
17, 18, -4, 19, 0, 19, 4, 19, 27, 19,
38, 20, -13, 21, 12, 22, -36, 23, -24, 23,
-8, 24, 7, 24, -3, 25, 1, 25, 22, 25,
34, 26, -18, 28, -32, 29, 16, 29, -11, 31,
9, 32, 29, 32, -4, 33, 2, 33, -26, 34,
23, 36, -19, 39, 16, 40, -13, 41, 9, 42,
-6, 43, 1, 43, 0, 0, 0, 0, 0, 0,
0, 0, 1, 0, 2, 0, 3, 0, 5, 0,
8, 0, 13, 0, 21, 0, -1, 0, -2, 0,
-3, 0, -5, 0, -8, 0, -13, 0, -17, 0,
-21, 0, 0, 1, 1, 1, 2, 1, 3, 1,
5, 1, 8, 1, 13, 1, 21, 1, -1, 1,
-2, 1, -3, 1, -5, 1, -8, 1, -13, 1,
-17, 1, -21, 1, 0, 2, 1, 2, 2, 2,
3, 2, 5, 2, 8, 2, 13, 2, 21, 2,
-1, 2, -2, 2, -3, 2, -5, 2, -8, 2,
-13, 2, -17, 2, -21, 2, 0, 3, 1, 3,
2, 3, 3, 3, 5, 3, 8, 3, 13, 3,
21, 3, -1, 3, -2, 3, -3, 3, -5, 3,
-8, 3, -13, 3, -17, 3, -21, 3, 0, 5,
1, 5, 2, 5, 3, 5, 5, 5, 8, 5,
13, 5, 21, 5, -1, 5, -2, 5, -3, 5,
-5, 5, -8, 5, -13, 5, -17, 5, -21, 5,
0, 8, 1, 8, 2, 8, 3, 8, 5, 8,
8, 8, 13, 8, 21, 8, -1, 8, -2, 8,
-3, 8, -5, 8, -8, 8, -13, 8, -17, 8,
-21, 8, 0, 13, 1, 13, 2, 13, 3, 13,
5, 13, 8, 13, 13, 13, 21, 13, -1, 13,
-2, 13, -3, 13, -5, 13, -8, 13, -13, 13,
-17, 13, -21, 13, 0, 21, 1, 21, 2, 21,
3, 21, 5, 21, 8, 21, 13, 21, 21, 21,
-1, 21, -2, 21, -3, 21, -5, 21, -8, 21,
-13, 21, -17, 21, -21, 21, 0, -1, 1, -1,
2, -1, 3, -1, 5, -1, 8, -1, 13, -1,
21, -1, -1, -1, -2, -1, -3, -1, -5, -1,
-8, -1, -13, -1, -17, -1, -21, -1, 0, -2,
1, -2, 2, -2, 3, -2, 5, -2, 8, -2,
13, -2, 21, -2, -1, -2, -2, -2, -3, -2,
-5, -2, -8, -2, -13, -2, -17, -2, -21, -2,
0, -3, 1, -3, 2, -3, 3, -3, 5, -3,
8, -3, 13, -3, 21, -3, -1, -3, -2, -3,
-3, -3, -5, -3, -8, -3, -13, -3, -17, -3,
-21, -3, 0, -5, 1, -5, 2, -5, 3, -5,
5, -5, 8, -5, 13, -5, 21, -5, -1, -5,
-2, -5, -3, -5, -5, -5, -8, -5, -13, -5,
-17, -5, -21, -5, 0, -8, 1, -8, 2, -8,
3, -8, 5, -8, 8, -8, 13, -8, 21, -8,
-1, -8, -2, -8, -3, -8, -5, -8, -8, -8,
-13, -8, -17, -8, -21, -8, 0, -13, 1, -13,
2, -13, 3, -13, 5, -13, 8, -13, 13, -13,
21, -13, -1, -13, -2, -13, -3, -13, -5, -13,
-8, -13, -13, -13, -17, -13, -21, -13, 0, -17,
1, -17, 2, -17, 3, -17, 5, -17, 8, -17,
13, -17, 21, -17, -1, -17, -2, -17, -3, -17,
-5, -17, -8, -17, -13, -17, -17, -17, -21, -17,
0, -21, 1, -21, 2, -21, 3, -21, 5, -21,
8, -21, 13, -21, 21, -21, -1, -21, -2, -21,
-3, -21, -5, -21, -8, -21, -13, -21, -17, -21
};
if (_tableLastPitch == pitch && _tableLastIndex == index)
return;
_tableLastPitch = pitch;
_tableLastIndex = index;
index *= 255;
assert(index + 254 < (int32)(sizeof(table) / 2));
for (int32 i = 0; i < 255; i++) {
int32 j = (i + index) * 2;
_offsetTable[i] = table[j + 1] * pitch + table[j];
}
}
void Codec48Decoder::decode3(byte *dst, const byte *src, int bufOffset) {
for (int i = 0; i < _blockY; i++) {
for (int j = 0; j < _blockX; j++) {
byte opcode = *src++;
switch (opcode) {
case 0xFF: {
// Interpolate a 4x4 block based on 1 pixel, then scale to 8x8
byte scaleBuffer[16];
scaleBuffer[15] = *src++;
scaleBuffer[7] = _interTable[(dst[-_pitch + 7] << 8) | scaleBuffer[15]];
scaleBuffer[3] = _interTable[(dst[-_pitch + 7] << 8) | scaleBuffer[7]];
scaleBuffer[11] = _interTable[(scaleBuffer[15] << 8) | scaleBuffer[7]];
scaleBuffer[1] = _interTable[(dst[-1] << 8) | scaleBuffer[3]];
scaleBuffer[0] = _interTable[(dst[-1] << 8) | scaleBuffer[1]];
scaleBuffer[2] = _interTable[(scaleBuffer[3] << 8) | scaleBuffer[1]];
scaleBuffer[5] = _interTable[(dst[_pitch * 2 - 1] << 8) | scaleBuffer[7]];
scaleBuffer[4] = _interTable[(dst[_pitch * 2 - 1] << 8) | scaleBuffer[5]];
scaleBuffer[6] = _interTable[(scaleBuffer[7] << 8) | scaleBuffer[5]];
scaleBuffer[9] = _interTable[(dst[_pitch * 3 - 1] << 8) | scaleBuffer[11]];
scaleBuffer[8] = _interTable[(dst[_pitch * 3 - 1] << 8) | scaleBuffer[9]];
scaleBuffer[10] = _interTable[(scaleBuffer[11] << 8) | scaleBuffer[9]];
scaleBuffer[13] = _interTable[(dst[_pitch * 4 - 1] << 8) | scaleBuffer[15]];
scaleBuffer[12] = _interTable[(dst[_pitch * 4 - 1] << 8) | scaleBuffer[13]];
scaleBuffer[14] = _interTable[(scaleBuffer[15] << 8) | scaleBuffer[13]];
scaleBlock(dst, scaleBuffer);
break;
}
case 0xFE:
// Copy a block using an absolute offset
copyBlock(dst, bufOffset, (int16)READ_LE_UINT16(src));
src += 2;
break;
case 0xFD: {
// Interpolate a 4x4 block based on 4 pixels, then scale to 8x8
byte scaleBuffer[16];
scaleBuffer[5] = src[0];
scaleBuffer[7] = src[1];
scaleBuffer[13] = src[2];
scaleBuffer[15] = src[3];
scaleBuffer[1] = _interTable[(dst[-_pitch + 3] << 8) | scaleBuffer[5]];
scaleBuffer[3] = _interTable[(dst[-_pitch + 7] << 8) | scaleBuffer[7]];
scaleBuffer[11] = _interTable[(scaleBuffer[15] << 8) | scaleBuffer[7]];
scaleBuffer[9] = _interTable[(scaleBuffer[13] << 8) | scaleBuffer[5]];
scaleBuffer[0] = _interTable[(dst[-1] << 8) | scaleBuffer[1]];
scaleBuffer[2] = _interTable[(scaleBuffer[3] << 8) | scaleBuffer[1]];
scaleBuffer[4] = _interTable[(dst[_pitch * 2 - 1] << 8) | scaleBuffer[5]];
scaleBuffer[6] = _interTable[(scaleBuffer[7] << 8) | scaleBuffer[5]];
scaleBuffer[8] = _interTable[(dst[_pitch * 3 - 1] << 8) | scaleBuffer[9]];
scaleBuffer[10] = _interTable[(scaleBuffer[11] << 8) | scaleBuffer[9]];
scaleBuffer[12] = _interTable[(dst[_pitch * 4 - 1] << 8) | scaleBuffer[13]];
scaleBuffer[14] = _interTable[(scaleBuffer[15] << 8) | scaleBuffer[13]];
scaleBlock(dst, scaleBuffer);
src += 4;
break;
}
case 0xFC:
// Copy 4 4x4 blocks using the offset table
*((uint32 *)dst) = *((uint32 *)(dst + bufOffset + _offsetTable[src[0]]));
*((uint32 *)(dst + _pitch)) = *((uint32 *)(dst + bufOffset + _offsetTable[src[0]] + _pitch));
*((uint32 *)(dst + _pitch * 2)) = *((uint32 *)(dst + bufOffset + _offsetTable[src[0]] + _pitch * 2));
*((uint32 *)(dst + _pitch * 3)) = *((uint32 *)(dst + bufOffset + _offsetTable[src[0]] + _pitch * 3));
*((uint32 *)(dst + 4)) = *((uint32 *)(dst + bufOffset + _offsetTable[src[1]] + 4));
*((uint32 *)(dst + _pitch + 4)) = *((uint32 *)(dst + bufOffset + _offsetTable[src[1]] + _pitch + 4));
*((uint32 *)(dst + _pitch * 2 + 4)) = *((uint32 *)(dst + bufOffset + _offsetTable[src[1]] + _pitch * 2 + 4));
*((uint32 *)(dst + _pitch * 3 + 4)) = *((uint32 *)(dst + bufOffset + _offsetTable[src[1]] + _pitch * 3 + 4));
*((uint32 *)(dst + _pitch * 4)) = *((uint32 *)(dst + bufOffset + _offsetTable[src[2]] + _pitch * 4));
*((uint32 *)(dst + _pitch * 5)) = *((uint32 *)(dst + bufOffset + _offsetTable[src[2]] + _pitch * 5));
*((uint32 *)(dst + _pitch * 6)) = *((uint32 *)(dst + bufOffset + _offsetTable[src[2]] + _pitch * 6));
*((uint32 *)(dst + _pitch * 7)) = *((uint32 *)(dst + bufOffset + _offsetTable[src[2]] + _pitch * 7));
*((uint32 *)(dst + _pitch * 4 + 4)) = *((uint32 *)(dst + bufOffset + _offsetTable[src[3]] + _pitch * 4 + 4));
*((uint32 *)(dst + _pitch * 5 + 4)) = *((uint32 *)(dst + bufOffset + _offsetTable[src[3]] + _pitch * 5 + 4));
*((uint32 *)(dst + _pitch * 6 + 4)) = *((uint32 *)(dst + bufOffset + _offsetTable[src[3]] + _pitch * 6 + 4));
*((uint32 *)(dst + _pitch * 7 + 4)) = *((uint32 *)(dst + bufOffset + _offsetTable[src[3]] + _pitch * 7 + 4));
src += 4;
break;
case 0xFB:
// Copy 4 4x4 blocks using absolute offsets
*((uint32 *)dst) = *((uint32 *)(dst + bufOffset + (int16)READ_LE_UINT16(src)));
*((uint32 *)(dst + _pitch)) = *((uint32 *)(dst + bufOffset + (int16)READ_LE_UINT16(src) + _pitch));
*((uint32 *)(dst + _pitch * 2)) = *((uint32 *)(dst + bufOffset + (int16)READ_LE_UINT16(src) + _pitch * 2));
*((uint32 *)(dst + _pitch * 3)) = *((uint32 *)(dst + bufOffset + (int16)READ_LE_UINT16(src) + _pitch * 3));
*((uint32 *)(dst + 4)) = *((uint32 *)(dst + bufOffset + (int16)READ_LE_UINT16(src + 2) + 4));
*((uint32 *)(dst + _pitch + 4)) = *((uint32 *)(dst + bufOffset + (int16)READ_LE_UINT16(src + 2) + _pitch + 4));
*((uint32 *)(dst + _pitch * 2 + 4)) = *((uint32 *)(dst + bufOffset + (int16)READ_LE_UINT16(src + 2) + _pitch * 2 + 4));
*((uint32 *)(dst + _pitch * 3 + 4)) = *((uint32 *)(dst + bufOffset + (int16)READ_LE_UINT16(src + 2) + _pitch * 3 + 4));
*((uint32 *)(dst + _pitch * 4)) = *((uint32 *)(dst + bufOffset + (int16)READ_LE_UINT16(src + 4) + _pitch * 4));
*((uint32 *)(dst + _pitch * 5)) = *((uint32 *)(dst + bufOffset + (int16)READ_LE_UINT16(src + 4) + _pitch * 5));
*((uint32 *)(dst + _pitch * 6)) = *((uint32 *)(dst + bufOffset + (int16)READ_LE_UINT16(src + 4) + _pitch * 6));
*((uint32 *)(dst + _pitch * 7)) = *((uint32 *)(dst + bufOffset + (int16)READ_LE_UINT16(src + 4) + _pitch * 7));
*((uint32 *)(dst + _pitch * 4 + 4)) = *((uint32 *)(dst + bufOffset + (int16)READ_LE_UINT16(src + 6) + _pitch * 4 + 4));
*((uint32 *)(dst + _pitch * 5 + 4)) = *((uint32 *)(dst + bufOffset + (int16)READ_LE_UINT16(src + 6) + _pitch * 5 + 4));
*((uint32 *)(dst + _pitch * 6 + 4)) = *((uint32 *)(dst + bufOffset + (int16)READ_LE_UINT16(src + 6) + _pitch * 6 + 4));
*((uint32 *)(dst + _pitch * 7 + 4)) = *((uint32 *)(dst + bufOffset + (int16)READ_LE_UINT16(src + 6) + _pitch * 7 + 4));
src += 8;
break;
case 0xFA:
// Scale a 4x4 block to an 8x8 block
scaleBlock(dst, src);
src += 16;
break;
case 0xF9:
// Copy 16 2x2 blocks using the offset table
*((uint16 *)dst) = *((uint16 *)(dst + bufOffset + _offsetTable[src[0]]));
*((uint16 *)(dst + _pitch)) = *((uint16 *)(dst + bufOffset + _offsetTable[src[0]] + _pitch));
*((uint16 *)(dst + 2)) = *((uint16 *)(dst + bufOffset + _offsetTable[src[1]] + 2));
*((uint16 *)(dst + _pitch + 2)) = *((uint16 *)(dst + bufOffset + _offsetTable[src[1]] + _pitch + 2));
*((uint16 *)(dst + 4)) = *((uint16 *)(dst + bufOffset + _offsetTable[src[2]] + 4));
*((uint16 *)(dst + _pitch + 4)) = *((uint16 *)(dst + bufOffset + _offsetTable[src[2]] + _pitch + 4));
*((uint16 *)(dst + 6)) = *((uint16 *)(dst + bufOffset + _offsetTable[src[3]] + 6));
*((uint16 *)(dst + _pitch + 6)) = *((uint16 *)(dst + bufOffset + _offsetTable[src[3]] + _pitch + 6));
*((uint16 *)(dst + _pitch * 2)) = *((uint16 *)(dst + bufOffset + _offsetTable[src[4]] + _pitch * 2));
*((uint16 *)(dst + _pitch * 3)) = *((uint16 *)(dst + bufOffset + _offsetTable[src[4]] + _pitch * 3));
*((uint16 *)(dst + _pitch * 2 + 2)) = *((uint16 *)(dst + bufOffset + _offsetTable[src[5]] + _pitch * 2 + 2));
*((uint16 *)(dst + _pitch * 3 + 2)) = *((uint16 *)(dst + bufOffset + _offsetTable[src[5]] + _pitch * 3 + 2));
*((uint16 *)(dst + _pitch * 2 + 4)) = *((uint16 *)(dst + bufOffset + _offsetTable[src[6]] + _pitch * 2 + 4));
*((uint16 *)(dst + _pitch * 3 + 4)) = *((uint16 *)(dst + bufOffset + _offsetTable[src[6]] + _pitch * 3 + 4));
*((uint16 *)(dst + _pitch * 2 + 6)) = *((uint16 *)(dst + bufOffset + _offsetTable[src[7]] + _pitch * 2 + 6));
*((uint16 *)(dst + _pitch * 3 + 6)) = *((uint16 *)(dst + bufOffset + _offsetTable[src[7]] + _pitch * 3 + 6));
*((uint16 *)(dst + _pitch * 4)) = *((uint16 *)(dst + bufOffset + _offsetTable[src[8]] + _pitch * 4));
*((uint16 *)(dst + _pitch * 5)) = *((uint16 *)(dst + bufOffset + _offsetTable[src[8]] + _pitch * 5));
*((uint16 *)(dst + _pitch * 4 + 2)) = *((uint16 *)(dst + bufOffset + _offsetTable[src[9]] + _pitch * 4 + 2));
*((uint16 *)(dst + _pitch * 5 + 2)) = *((uint16 *)(dst + bufOffset + _offsetTable[src[9]] + _pitch * 5 + 2));
*((uint16 *)(dst + _pitch * 4 + 4)) = *((uint16 *)(dst + bufOffset + _offsetTable[src[10]] + _pitch * 4 + 4));
*((uint16 *)(dst + _pitch * 5 + 4)) = *((uint16 *)(dst + bufOffset + _offsetTable[src[10]] + _pitch * 5 + 4));
*((uint16 *)(dst + _pitch * 4 + 6)) = *((uint16 *)(dst + bufOffset + _offsetTable[src[11]] + _pitch * 4 + 6));
*((uint16 *)(dst + _pitch * 5 + 6)) = *((uint16 *)(dst + bufOffset + _offsetTable[src[11]] + _pitch * 5 + 6));
*((uint16 *)(dst + _pitch * 6)) = *((uint16 *)(dst + bufOffset + _offsetTable[src[12]] + _pitch * 6));
*((uint16 *)(dst + _pitch * 7)) = *((uint16 *)(dst + bufOffset + _offsetTable[src[12]] + _pitch * 7));
*((uint16 *)(dst + _pitch * 6 + 2)) = *((uint16 *)(dst + bufOffset + _offsetTable[src[13]] + _pitch * 6 + 2));
*((uint16 *)(dst + _pitch * 7 + 2)) = *((uint16 *)(dst + bufOffset + _offsetTable[src[13]] + _pitch * 7 + 2));
*((uint16 *)(dst + _pitch * 6 + 4)) = *((uint16 *)(dst + bufOffset + _offsetTable[src[14]] + _pitch * 6 + 4));
*((uint16 *)(dst + _pitch * 7 + 4)) = *((uint16 *)(dst + bufOffset + _offsetTable[src[14]] + _pitch * 7 + 4));
*((uint16 *)(dst + _pitch * 6 + 6)) = *((uint16 *)(dst + bufOffset + _offsetTable[src[15]] + _pitch * 6 + 6));
*((uint16 *)(dst + _pitch * 7 + 6)) = *((uint16 *)(dst + bufOffset + _offsetTable[src[15]] + _pitch * 7 + 6));
src += 16;
break;
case 0xF8:
// Copy 16 2x2 blocks using absolute offsets
*((uint16 *)dst) = *((uint16 *)(dst + bufOffset + (int16)READ_LE_UINT16(src)));
*((uint16 *)(dst + _pitch)) = *((uint16 *)(dst + bufOffset + (int16)READ_LE_UINT16(src) + _pitch));
*((uint16 *)(dst + 2)) = *((uint16 *)(dst + bufOffset + (int16)READ_LE_UINT16(src + 2) + 2));
*((uint16 *)(dst + _pitch + 2)) = *((uint16 *)(dst + bufOffset + (int16)READ_LE_UINT16(src + 2) + _pitch + 2));
*((uint16 *)(dst + 4)) = *((uint16 *)(dst + bufOffset + (int16)READ_LE_UINT16(src + 4) + 4));
*((uint16 *)(dst + _pitch + 4)) = *((uint16 *)(dst + bufOffset + (int16)READ_LE_UINT16(src + 4) + _pitch + 4));
*((uint16 *)(dst + 6)) = *((uint16 *)(dst + bufOffset + (int16)READ_LE_UINT16(src + 6) + 6));
*((uint16 *)(dst + _pitch + 6)) = *((uint16 *)(dst + bufOffset + (int16)READ_LE_UINT16(src + 6) + _pitch + 6));
*((uint16 *)(dst + _pitch * 2)) = *((uint16 *)(dst + bufOffset + (int16)READ_LE_UINT16(src + 8) + _pitch * 2));
*((uint16 *)(dst + _pitch * 3)) = *((uint16 *)(dst + bufOffset + (int16)READ_LE_UINT16(src + 8) + _pitch * 3));
*((uint16 *)(dst + _pitch * 2 + 2)) = *((uint16 *)(dst + bufOffset + (int16)READ_LE_UINT16(src + 10) + _pitch * 2 + 2));
*((uint16 *)(dst + _pitch * 3 + 2)) = *((uint16 *)(dst + bufOffset + (int16)READ_LE_UINT16(src + 10) + _pitch * 3 + 2));
*((uint16 *)(dst + _pitch * 2 + 4)) = *((uint16 *)(dst + bufOffset + (int16)READ_LE_UINT16(src + 12) + _pitch * 2 + 4));
*((uint16 *)(dst + _pitch * 3 + 4)) = *((uint16 *)(dst + bufOffset + (int16)READ_LE_UINT16(src + 12) + _pitch * 3 + 4));
*((uint16 *)(dst + _pitch * 2 + 6)) = *((uint16 *)(dst + bufOffset + (int16)READ_LE_UINT16(src + 14) + _pitch * 2 + 6));
*((uint16 *)(dst + _pitch * 3 + 6)) = *((uint16 *)(dst + bufOffset + (int16)READ_LE_UINT16(src + 14) + _pitch * 3 + 6));
*((uint16 *)(dst + _pitch * 4)) = *((uint16 *)(dst + bufOffset + (int16)READ_LE_UINT16(src + 16) + _pitch * 4));
*((uint16 *)(dst + _pitch * 5)) = *((uint16 *)(dst + bufOffset + (int16)READ_LE_UINT16(src + 16) + _pitch * 5));
*((uint16 *)(dst + _pitch * 4 + 2)) = *((uint16 *)(dst + bufOffset + (int16)READ_LE_UINT16(src + 18) + _pitch * 4 + 2));
*((uint16 *)(dst + _pitch * 5 + 2)) = *((uint16 *)(dst + bufOffset + (int16)READ_LE_UINT16(src + 18) + _pitch * 5 + 2));
*((uint16 *)(dst + _pitch * 4 + 4)) = *((uint16 *)(dst + bufOffset + (int16)READ_LE_UINT16(src + 20) + _pitch * 4 + 4));
*((uint16 *)(dst + _pitch * 5 + 4)) = *((uint16 *)(dst + bufOffset + (int16)READ_LE_UINT16(src + 20) + _pitch * 5 + 4));
*((uint16 *)(dst + _pitch * 4 + 6)) = *((uint16 *)(dst + bufOffset + (int16)READ_LE_UINT16(src + 22) + _pitch * 4 + 6));
*((uint16 *)(dst + _pitch * 5 + 6)) = *((uint16 *)(dst + bufOffset + (int16)READ_LE_UINT16(src + 22) + _pitch * 5 + 6));
*((uint16 *)(dst + _pitch * 6)) = *((uint16 *)(dst + bufOffset + (int16)READ_LE_UINT16(src + 24) + _pitch * 6));
*((uint16 *)(dst + _pitch * 7)) = *((uint16 *)(dst + bufOffset + (int16)READ_LE_UINT16(src + 24) + _pitch * 7));
*((uint16 *)(dst + _pitch * 6 + 2)) = *((uint16 *)(dst + bufOffset + (int16)READ_LE_UINT16(src + 26) + _pitch * 6 + 2));
*((uint16 *)(dst + _pitch * 7 + 2)) = *((uint16 *)(dst + bufOffset + (int16)READ_LE_UINT16(src + 26) + _pitch * 7 + 2));
*((uint16 *)(dst + _pitch * 6 + 4)) = *((uint16 *)(dst + bufOffset + (int16)READ_LE_UINT16(src + 28) + _pitch * 6 + 4));
*((uint16 *)(dst + _pitch * 7 + 4)) = *((uint16 *)(dst + bufOffset + (int16)READ_LE_UINT16(src + 28) + _pitch * 7 + 4));
*((uint16 *)(dst + _pitch * 6 + 6)) = *((uint16 *)(dst + bufOffset + (int16)READ_LE_UINT16(src + 30) + _pitch * 6 + 6));
*((uint16 *)(dst + _pitch * 7 + 6)) = *((uint16 *)(dst + bufOffset + (int16)READ_LE_UINT16(src + 30) + _pitch * 7 + 6));
src += 32;
break;
case 0xF7:
// Raw 8x8 block
*((uint32 *)dst) = *((const uint32 *)src);
*((uint32 *)(dst + 4)) = *((const uint32 *)(src + 4));
*((uint32 *)(dst + _pitch)) = *((const uint32 *)(src + 8));
*((uint32 *)(dst + _pitch + 4)) = *((const uint32 *)(src + 12));
*((uint32 *)(dst + _pitch * 2)) = *((const uint32 *)(src + 16));
*((uint32 *)(dst + _pitch * 2 + 4)) = *((const uint32 *)(src + 20));
*((uint32 *)(dst + _pitch * 3)) = *((const uint32 *)(src + 24));
*((uint32 *)(dst + _pitch * 3 + 4)) = *((const uint32 *)(src + 28));
*((uint32 *)(dst + _pitch * 4)) = *((const uint32 *)(src + 32));
*((uint32 *)(dst + _pitch * 4 + 4)) = *((const uint32 *)(src + 36));
*((uint32 *)(dst + _pitch * 5)) = *((const uint32 *)(src + 40));
*((uint32 *)(dst + _pitch * 5 + 4)) = *((const uint32 *)(src + 44));
*((uint32 *)(dst + _pitch * 6)) = *((const uint32 *)(src + 48));
*((uint32 *)(dst + _pitch * 6 + 4)) = *((const uint32 *)(src + 52));
*((uint32 *)(dst + _pitch * 7)) = *((const uint32 *)(src + 56));
*((uint32 *)(dst + _pitch * 7 + 4)) = *((const uint32 *)(src + 60));
src += 64;
break;
default:
// Copy a block using the offset table
copyBlock(dst, bufOffset, _offsetTable[opcode]);
break;
}
dst += 8;
}
dst += _pitch * 7;
}
}
void Codec48Decoder::copyBlock(byte *dst, int deltaBufOffset, int offset) {
const byte *src = dst + deltaBufOffset + offset;
for (int i = 0; i < 8; i++) {
*((uint32 *)(dst + _pitch * i)) = *((const uint32 *)(src + _pitch * i));
*((uint32 *)(dst + _pitch * i + 4)) = *((const uint32 *)(src + _pitch * i + 4));
}
}
void Codec48Decoder::scaleBlock(byte *dst, const byte *src) {
// This is doing a 2x scale of data
for (int i = 0; i < 4; i++) {
uint16 pixels = src[0];
pixels = (pixels << 8) | pixels;
*((uint16 *)dst) = pixels;
*((uint16 *)(dst + _pitch)) = pixels;
pixels = src[1];
pixels = (pixels << 8) | pixels;
*((uint16 *)(dst + 2)) = pixels;
*((uint16 *)(dst + _pitch + 2)) = pixels;
pixels = src[2];
pixels = (pixels << 8) | pixels;
*((uint16 *)(dst + 4)) = pixels;
*((uint16 *)(dst + _pitch + 4)) = pixels;
pixels = src[3];
pixels = (pixels << 8) | pixels;
*((uint16 *)(dst + 6)) = pixels;
*((uint16 *)(dst + _pitch + 6)) = pixels;
src += 4;
dst += _pitch * 2;
}
}
} // end of namespace Grim

View File

@@ -0,0 +1,60 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef CODEC48_H
#define CODEC48_H
#include "common/scummsys.h"
namespace Grim {
class Codec48Decoder {
public:
Codec48Decoder();
~Codec48Decoder();
void init(int width, int height);
void deinit();
bool decode(byte *dst, const byte *src);
private:
void makeTable(int pitch, int index);
void bompDecodeLine(byte *dst, const byte *src, int len);
void decode3(byte *dst, const byte *src, int bufOffset);
void scaleBlock(byte *dst, const byte *src);
void copyBlock(byte *dst, int deltaBufOffset, int offset);
int _curBuf;
byte *_deltaBuf[2];
int _blockX, _blockY;
int _pitch;
int16 *_offsetTable;
int _tableLastPitch, _tableLastIndex;
int16 _prevSeqNb;
int32 _frameSize;
int _width, _height;
byte *_interTable;
};
} // end of namespace Grim
#endif

View File

@@ -0,0 +1,738 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
#include "common/endian.h"
#include "common/events.h"
#include "common/file.h"
#include "common/rational.h"
#include "common/system.h"
#include "common/timer.h"
#include "common/memstream.h"
#include "audio/audiostream.h"
#include "audio/mixer.h"
#include "audio/decoders/raw.h"
#include "engines/grim/debug.h"
#include "engines/grim/movie/codecs/codec48.h"
#include "engines/grim/movie/codecs/blocky8.h"
#include "engines/grim/movie/codecs/blocky16.h"
#include "engines/grim/movie/codecs/smush_decoder.h"
#include "engines/grim/movie/codecs/vima.h"
namespace Grim {
#define ANNO_HEADER "MakeAnim animation type 'Bl16' parameters: "
#define BUFFER_SIZE 16385
#define SMUSH_SPEED 66667
bool SmushDecoder::_demo = false;
static uint16 smushDestTable[5786];
SmushDecoder::SmushDecoder() {
_file = nullptr;
_videoLooping = false;
_startPos = 0;
_frames = nullptr;
_videoTrack = nullptr;
_audioTrack = nullptr;
_videoPause = false;
}
SmushDecoder::~SmushDecoder() {
delete _videoTrack;
delete _audioTrack;
delete[] _frames;
}
void SmushDecoder::init() {
_videoTrack->init();
_audioTrack->init();
}
void SmushDecoder::initFrames() {
delete[] _frames;
_frames = new Frame[_videoTrack->getFrameCount()];
int seekPos = _file->pos();
int curFrame = -1;
_file->seek(_startPos, SEEK_SET);
while (curFrame < _videoTrack->getFrameCount() - 1) {
Frame &frame = _frames[++curFrame];
frame.frame = curFrame;
frame.pos = _file->pos();
frame.keyframe = false;
uint32 tag = _file->readUint32BE();
uint32 size;
if (tag == MKTAG('A', 'N', 'N', 'O')) {
size = _file->readUint32BE();
_file->seek(size, SEEK_CUR);
tag = _file->readUint32BE();
}
assert(tag == MKTAG('F', 'R', 'M', 'E'));
size = _file->readUint32BE();
while (size > 0) {
uint32 subType = _file->readUint32BE();
uint32 subSize = _file->readUint32BE();
int32 subPos = _file->pos();
if (subType == MKTAG('B', 'l', '1', '6')) {
_file->seek(18, SEEK_CUR);
if (_file->readByte() == 0) {
frame.keyframe = true;
}
}
size -= subSize + 8 + (subSize & 1);
_file->seek(subPos + subSize + (subSize & 1), SEEK_SET);
}
_file->seek(size, SEEK_CUR);
}
_file->seek(seekPos, SEEK_SET);
}
void SmushDecoder::close() {
VideoDecoder::close();
_audioTrack = nullptr;
_videoTrack = nullptr;
_videoLooping = false;
_startPos = 0;
delete[] _frames;
_frames = nullptr;
if (_file) {
delete _file;
_file = nullptr;
}
}
bool SmushDecoder::readHeader() {
if (!_file) {
return false;
}
uint32 mainTag = _file->readUint32BE();
uint32 pos = _file->pos();
uint32 expectedTag = 0;
uint32 size = _file->readUint32BE(); // file-size
// Verify that we have the correct combination of headers.
if (mainTag == MKTAG('A', 'N', 'I', 'M')) { // Demo
expectedTag = MKTAG('A', 'H', 'D', 'R');
} else if (mainTag == MKTAG('S', 'A', 'N', 'M')) { // Retail
expectedTag = MKTAG('S', 'H', 'D', 'R');
} else {
error("Invalid SMUSH-header");
}
uint32 tag = _file->readUint32BE();
size = _file->readUint32BE();
pos = _file->pos();
assert(tag == expectedTag);
(void)expectedTag;
if (tag == MKTAG('A', 'H', 'D', 'R')) { // Demo
uint32 version = _file->readUint16LE();
uint16 nbFrames = _file->readUint16LE();
_file->readUint16BE(); // unknown
int width = -1;
int height = -1;
_videoLooping = false;
_startPos = 0;
_videoTrack = new SmushVideoTrack(width, height, SMUSH_SPEED, nbFrames, false);
_videoTrack->_x = -1;
_videoTrack->_y = -1;
addTrack(_videoTrack);
_file->read(_videoTrack->getPal(), 0x300);
int audioRate = 11025;
if (version == 2) {
_file->readUint32LE(); // framerate
_file->readUint32LE();
audioRate = _file->readUint32LE();
}
_file->readUint32BE();
_file->readUint32BE();
_audioTrack = new SmushAudioTrack(getSoundType(), false, audioRate, 2);
addTrack(_audioTrack);
return true;
} else if (tag == MKTAG('S', 'H', 'D', 'R')) { // Retail
_file->readUint16LE();
uint16 nbFrames = _file->readUint32LE();
_file->readUint16LE();
int width = _file->readUint16LE();
int height = _file->readUint16LE();
_file->readUint16LE();
int frameRate = _file->readUint32LE();
int16 flags = _file->readUint16LE();
// Output information for checking out the flags
if (Debug::isChannelEnabled(Debug::Movie | Debug::Info)) {
warning("SMUSH Flags:");
for (int i = 0; i < 16; i++) {
warning(" %d", (flags & (1 << i)) != 0);
}
}
_file->seek(pos + size + (size & 1), SEEK_SET);
_videoLooping = true;
// If the video is NOT looping, setLooping will set the speed to the proper value
_videoTrack = new SmushVideoTrack(width, height, frameRate, nbFrames, true);
addTrack(_videoTrack);
return handleFramesHeader();
}
return false;
}
bool SmushDecoder::handleFramesHeader() {
uint32 tag;
int32 size;
int pos = 0;
int freq = 0;
int channels = 0;
tag = _file->readUint32BE();
if (tag != MKTAG('F', 'L', 'H', 'D')) {
return false;
}
size = _file->readUint32BE();
byte *f_header = new byte[size];
_file->read(f_header, size);
do {
if (READ_BE_UINT32(f_header + pos) == MKTAG('B', 'l', '1', '6')) {
pos += READ_BE_UINT32(f_header + pos + 4) + 8;
} else if (READ_BE_UINT32(f_header + pos) == MKTAG('W', 'a', 'v', 'e')) {
freq = READ_LE_UINT32(f_header + pos + 8);
channels = READ_LE_UINT32(f_header + pos + 12);
pos += 20;
} else {
error("SmushDecoder::handleFramesHeader() unknown tag");
}
} while (pos < size);
delete[] f_header;
_audioTrack = new SmushAudioTrack(getSoundType(), true, freq, channels);
addTrack(_audioTrack);
return true;
}
bool SmushDecoder::loadStream(Common::SeekableReadStream *stream) {
close();
_file = stream;
// Load the video
if (!readHeader()) {
warning("Failure loading SMUSH-file");
return false;
}
_startPos = _file->pos();
init();
return true;
}
const Graphics::Surface *SmushDecoder::decodeNextFrame() {
handleFrame();
// We might be interested in getting the last frame even after the video ends:
if (endOfVideo()) {
return _videoTrack->decodeNextFrame();
}
return VideoDecoder::decodeNextFrame();
}
void SmushDecoder::setLooping(bool l) {
_videoLooping = l;
if (!_videoLooping) {
_videoTrack->setMsPerFrame(SMUSH_SPEED);
}
}
void SmushDecoder::handleFrame() {
uint32 tag;
int32 size;
if (isPaused()) {
return;
}
if (_videoTrack->endOfTrack()) { // Looping is handled outside, by rewinding the video.
_audioTrack->stop(); // HACK: Avoids the movie playing past the last frame
// pauseVideo(true);
return;
}
tag = _file->readUint32BE();
size = _file->readUint32BE();
if (tag == MKTAG('A', 'N', 'N', 'O')) {
char *anno;
byte *data;
data = new byte[size];
_file->read(data, size);
anno = (char *)data;
if (strncmp(anno, ANNO_HEADER, sizeof(ANNO_HEADER) - 1) == 0) {
//char *annoData = anno + sizeof(ANNO_HEADER);
// Examples:
// Water streaming around boat from Manny's balcony
// MakeAnim animation type 'Bl16' parameters: 10000;12000;100;1;0;0;0;0;25;0;
// Water in front of the Blue Casket
// MakeAnim animation type 'Bl16' parameters: 20000;25000;100;1;0;0;0;0;25;0;
// Scrimshaw exterior:
// MakeAnim animation type 'Bl16' parameters: 6000;8000;100;0;0;0;0;0;2;0;
// Lola engine room (loops a limited number of times?):
// MakeAnim animation type 'Bl16' parameters: 6000;8000;90;1;0;0;0;0;2;0;
Debug::debug(Debug::Movie, "Announcement data: %s\n", anno);
// It looks like the announcement data is actually for setting some of the
// header parameters, not for any looping purpose
} else {
Debug::debug(Debug::Movie, "Announcement header not understood: %s\n", anno);
}
delete[] anno;
tag = _file->readUint32BE();
size = _file->readUint32BE();
}
assert(tag == MKTAG('F', 'R', 'M', 'E'));
handleFRME(_file, size);
_videoTrack->finishFrame();
}
void SmushDecoder::handleFRME(Common::SeekableReadStream *stream, uint32 size) {
int blockSize = size;
byte *block = new byte[size];
stream->read(block, size);
Common::MemoryReadStream *memStream = new Common::MemoryReadStream(block, size, DisposeAfterUse::NO);
while (size > 0) {
uint32 subType = memStream->readUint32BE();
uint32 subSize = memStream->readUint32BE();
uint32 subPos = memStream->pos();
switch (subType) {
// Retail only:
case MKTAG('B', 'l', '1', '6'):
_videoTrack->handleBlocky16(memStream, subSize);
break;
case MKTAG('W', 'a', 'v', 'e'):
_audioTrack->handleVIMA(memStream, blockSize);
break;
// Demo only:
case MKTAG('F', 'O', 'B', 'J'):
_videoTrack->handleFrameObject(memStream, subSize);
break;
case MKTAG('I', 'A', 'C', 'T'):
_audioTrack->handleIACT(memStream, subSize);
break;
case MKTAG('X', 'P', 'A', 'L'):
_videoTrack->handleDeltaPalette(memStream, subSize);
break;
default:
Debug::error(Debug::Movie, "SmushDecoder::handleFrame() unknown tag");
}
size -= subSize + 8 + (subSize & 1);
memStream->seek(subPos + subSize + (subSize & 1), SEEK_SET);
}
delete memStream;
delete[] block;
}
bool SmushDecoder::rewind() {
return seekToFrame(0);
}
bool SmushDecoder::seekIntern(const Audio::Timestamp &time) {
int32 wantedFrame = (uint32)((time.msecs() / 1000.0f) * _videoTrack->getFrameRate().toDouble());
if (wantedFrame != 0) {
Debug::debug(Debug::Movie, "Seek to time: %d, frame: %d", time.msecs(), wantedFrame);
Debug::debug(Debug::Movie, "Current frame: %d", _videoTrack->getCurFrame());
}
if (wantedFrame > _videoTrack->getFrameCount()) {
return false;
}
if (!_frames) {
initFrames();
}
// Track down the keyframe
int keyframe = 0;
for (int i = wantedFrame; i >= 0; --i) {
if (_frames[i].keyframe) {
keyframe = i;
break;
}
}
_videoTrack->setFrameStart(keyframe);
// VIMA frames are 50 frames ahead of time, so we have to make sure we have 50 frames
// of audio before the wantedFrame. Here we use 51 to have a bit of safe margin
if (wantedFrame - keyframe < 51) {
keyframe = wantedFrame - 51;
}
if (keyframe < 0) {
keyframe = 0;
}
_file->seek(_frames[keyframe].pos, SEEK_SET);
_videoTrack->setCurFrame(keyframe - 1);
while (_videoTrack->getCurFrame() < wantedFrame - 1) {
decodeNextFrame();
}
// As said, VIMA is 50 frames ahead of time. Every frame it pushes 1470 samples, and 50 * 1470 = 73500.
// The first frame, instead of 1470, it pushes 73500 samples to have this 50-frames-time.
// So if we have used frame 0 as keyframe we can remove safely time * rate samples, and we will
// still have the 50 frames margin. If we have used a later frame as keyframe we don't have the 73500
// samples pushed the first frame, so we have to be careful not to remove too much data,
// otherwise the audio will start at a later point. (72030 == 73500 - 1470)
int offset = (keyframe == 0 ? 0 : 72030);
// Skip decoded audio between the keyframe and the target frame
Audio::Timestamp delay = 0;
if (_videoTrack->getCurFrame() > 0) {
delay = _videoTrack->getFrameTime(_videoTrack->getCurFrame());
}
if (keyframe > 0) {
delay = delay - _videoTrack->getFrameTime(keyframe);
}
int32 sampleCount = (delay.msecs() / 1000.f) * _audioTrack->getRate() - offset;
_audioTrack->skipSamples(sampleCount);
VideoDecoder::seekIntern(time);
return true;
}
SmushDecoder::SmushVideoTrack::SmushVideoTrack(int width, int height, int fps, int numFrames, bool is16Bit) {
if (!is16Bit) { // Demo
_format = Graphics::PixelFormat::createFormatCLUT8();
_codec48 = new Codec48Decoder();
_blocky8 = new Blocky8();
_blocky16 = nullptr;
} else {
_format = Graphics::PixelFormat(2, 5, 6, 5, 0, 11, 5, 0, 0);
_codec48 = nullptr;
_blocky8 = nullptr;
_blocky16 = new Blocky16();
_blocky16->init(width, height);
}
_width = width;
_height = height;
_nbframes = numFrames;
_is16Bit = is16Bit;
_x = 0;
_y = 0;
setMsPerFrame(fps);
_curFrame = 0;
for (int i = 0; i < 0x300; i++) {
_palette[i] = 0;
_deltaPal[i] = 0;
_dirtyPalette = false;
}
_frameStart = 0;
}
SmushDecoder::SmushVideoTrack::~SmushVideoTrack() {
delete _codec48;
delete _blocky8;
delete _blocky16;
_surface.free();
}
void SmushDecoder::SmushVideoTrack::init() {
_curFrame = -1;
_frameStart = -1;
if (_is16Bit) { // Retail only
_surface.create(_width, _height, _format);
}
}
void SmushDecoder::SmushVideoTrack::finishFrame() {
_curFrame++;
}
void SmushDecoder::SmushVideoTrack::setFrameStart(int frame) {
_frameStart = frame - 1;
}
void SmushDecoder::SmushVideoTrack::handleBlocky16(Common::SeekableReadStream *stream, uint32 size) {
if (_curFrame < _frameStart) {
return;
}
assert(_is16Bit);
byte *ptr = new byte[size];
stream->read(ptr, size);
_blocky16->decode((byte *)_surface.getPixels(), ptr);
delete[] ptr;
}
void SmushDecoder::SmushVideoTrack::handleFrameObject(Common::SeekableReadStream *stream, uint32 size) {
if (_curFrame < _frameStart) {
return;
}
assert(!_is16Bit);
assert(size >= 14);
byte codec = stream->readByte();
assert(codec == 47 || codec == 48);
/* byte codecParam = */ stream->readByte();
_x = stream->readSint16LE();
_y = stream->readSint16LE();
uint16 width = stream->readUint16LE();
uint16 height = stream->readUint16LE();
if (width != _width || height != _height) {
_width = width;
_height = height;
_surface.create(_width, _height, _format);
_codec48->init(_width, _height);
_blocky8->init(_width, _height);
}
stream->readUint16LE();
stream->readUint16LE();
size -= 14;
byte *ptr = new byte[size];
stream->read(ptr, size);
if (codec == 47) {
_blocky8->decode((byte *)_surface.getPixels(), ptr);
} else if (codec == 48) {
_codec48->decode((byte *)_surface.getPixels(), ptr);
}
delete[] ptr;
}
static byte delta_color(byte org_color, int16 delta_color) {
int t = (org_color * 129 + delta_color) / 128;
return CLIP(t, 0, 255);
}
void SmushDecoder::SmushVideoTrack::handleDeltaPalette(Common::SeekableReadStream *stream, int32 size) {
if (size == 0x300 * 3 + 4) {
stream->seek(4, SEEK_CUR);
for (int i = 0; i < 0x300; i++) {
_deltaPal[i] = stream->readUint16LE();
}
stream->read(_palette, 0x300);
_dirtyPalette = true;
} else if (size == 6) {
for (int i = 0; i < 0x300; i++) {
_palette[i] = delta_color(_palette[i], _deltaPal[i]);
}
_dirtyPalette = true;
} else {
error("SmushDecoder::handleDeltaPalette() Wrong size for DeltaPalette");
}
}
Graphics::Surface *SmushDecoder::SmushVideoTrack::decodeNextFrame() {
return &_surface;
}
void SmushDecoder::SmushVideoTrack::setMsPerFrame(int ms) {
_frameRate = Common::Rational(1000000, ms);
}
SmushDecoder::SmushAudioTrack::SmushAudioTrack(Audio::Mixer::SoundType soundType, bool isVima, int freq, int channels) :
AudioTrack(soundType) {
_isVima = isVima;
_channels = channels;
_freq = freq;
_queueStream = Audio::makeQueuingAudioStream(_freq, (_channels == 2));
_IACTpos = 0;
}
SmushDecoder::SmushAudioTrack::~SmushAudioTrack() {
delete _queueStream;
}
void SmushDecoder::SmushAudioTrack::init() {
_IACTpos = 0;
if (_isVima) {
vimaInit(smushDestTable);
}
}
void SmushDecoder::SmushAudioTrack::handleVIMA(Common::SeekableReadStream *stream, uint32 size) {
if (size < 8)
return;
int decompressedSize = stream->readUint32BE();
if (decompressedSize == MKTAG('P', 'S', 'A', 'D')) {
decompressedSize = stream->readUint32BE();
if (decompressedSize > (int)size - 8)
decompressedSize = size - 8;
if (decompressedSize < 10)
return;
stream->skip(10);
decompressedSize -= 10;
byte *src = (byte *)malloc(decompressedSize);
stream->read(src, decompressedSize);
int flags = Audio::FLAG_16BITS | Audio::FLAG_LITTLE_ENDIAN;
if (_channels == 2) {
flags |= Audio::FLAG_STEREO;
}
if (!_queueStream) {
_queueStream = Audio::makeQueuingAudioStream(_freq, (_channels == 2));
}
_queueStream->queueBuffer(src, decompressedSize, DisposeAfterUse::YES, flags);
return;
}
if (decompressedSize < 0) {
stream->readUint32BE();
decompressedSize = stream->readUint32BE();
}
byte *src = new byte[size];
stream->read(src, size);
// this will be deleted using free() by the stream, so allocate it using malloc().
int16 *dst = (int16 *)malloc(decompressedSize * _channels * 2);
decompressVima(src, dst, decompressedSize * _channels * 2, smushDestTable, true);
int flags = Audio::FLAG_16BITS | Audio::FLAG_LITTLE_ENDIAN;
if (_channels == 2) {
flags |= Audio::FLAG_STEREO;
}
if (!_queueStream) {
_queueStream = Audio::makeQueuingAudioStream(_freq, (_channels == 2));
}
_queueStream->queueBuffer((byte *)dst, decompressedSize * _channels * 2, DisposeAfterUse::YES, flags);
delete[] src;
}
void SmushDecoder::SmushAudioTrack::handleIACT(Common::SeekableReadStream *stream, int32 size) {
byte *src = new byte[size];
stream->read(src, size);
int32 bsize = size - 18;
const byte *d_src = src + 18;
while (bsize > 0) {
if (_IACTpos >= 2) {
int32 len = READ_BE_UINT16(_IACToutput) + 2;
len -= _IACTpos;
if (len > bsize) {
memcpy(_IACToutput + _IACTpos, d_src, bsize);
_IACTpos += bsize;
bsize = 0;
} else {
// this will be deleted using free() by the stream, so allocate it using malloc().
byte *output_data = (byte *)malloc(4096);
memcpy(_IACToutput + _IACTpos, d_src, len);
byte *dst = output_data;
byte *d_src2 = _IACToutput;
d_src2 += 2;
int32 count = 1024;
byte variable1 = *d_src2++;
byte variable2 = variable1 / 16;
variable1 &= 0x0f;
do {
byte value;
value = *(d_src2++);
if (value == 0x80) {
*dst++ = *d_src2++;
*dst++ = *d_src2++;
} else {
int16 val = (int8)value << variable2;
*dst++ = val >> 8;
*dst++ = (byte)(val);
}
value = *(d_src2++);
if (value == 0x80) {
*dst++ = *d_src2++;
*dst++ = *d_src2++;
} else {
int16 val = (int8)value << variable1;
*dst++ = val >> 8;
*dst++ = (byte)(val);
}
} while (--count);
if (!_queueStream) {
_queueStream = Audio::makeQueuingAudioStream(22050, true);
}
_queueStream->queueBuffer(output_data, 0x1000, DisposeAfterUse::YES, Audio::FLAG_STEREO | Audio::FLAG_16BITS);
bsize -= len;
d_src += len;
_IACTpos = 0;
}
} else {
if (bsize > 1 && _IACTpos == 0) {
*(_IACToutput + 0) = *d_src++;
_IACTpos = 1;
bsize--;
}
*(_IACToutput + _IACTpos) = *d_src++;
_IACTpos++;
bsize--;
}
}
delete[] src;
}
bool SmushDecoder::SmushAudioTrack::seek(const Audio::Timestamp &time) {
return true;
}
void SmushDecoder::SmushAudioTrack::skipSamples(int sampleCount) {
if (sampleCount <= 0)
return;
if (_queueStream->isStereo())
sampleCount *= 2;
int16 *tempBuffer = new int16[sampleCount];
_queueStream->readBuffer(tempBuffer, sampleCount);
delete[] tempBuffer;
}
} // end of namespace Grim

View File

@@ -0,0 +1,156 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef GRIM_SMUSH_DECODER_H
#define GRIM_SMUSH_DECODER_H
#include "audio/audiostream.h"
#include "video/video_decoder.h"
#include "graphics/surface.h"
namespace Audio {
class QueuingAudioStream;
}
namespace Grim {
class Codec48Decoder;
class Blocky8;
class Blocky16;
class SmushDecoder : public Video::VideoDecoder {
public:
SmushDecoder();
~SmushDecoder();
int getX() const { return _videoTrack->_x; }
int getY() const { return _videoTrack->_y; }
void setLooping(bool l);
bool isRewindable() const override { return true; }
bool isSeekable() const override { return true; }
bool rewind() override;
bool seekIntern(const Audio::Timestamp &time) override;
bool loadStream(Common::SeekableReadStream *stream) override;
protected:
bool readHeader();
void handleFrameDemo();
void handleFrame();
bool handleFramesHeader();
void handleFRME(Common::SeekableReadStream *stream, uint32 size);
void init();
void close() override;
const Graphics::Surface *decodeNextFrame() override;
class SmushVideoTrack : public FixedRateVideoTrack {
public:
SmushVideoTrack(int width, int height, int fps, int numFrames, bool is16Bit);
~SmushVideoTrack();
uint16 getWidth() const override { return _width; }
uint16 getHeight() const override { return _height; }
Graphics::PixelFormat getPixelFormat() const override { return _format; }
int getCurFrame() const override { return _curFrame; }
void setCurFrame(int frame) { _curFrame = frame; }
int getFrameCount() const override { return _nbframes; }
Common::Rational getFrameRate() const override { return _frameRate; }
void setMsPerFrame(int ms);
void finishFrame();
bool isSeekable() const override { return true; }
bool seek(const Audio::Timestamp &time) override { return true; }
void setFrameStart(int frame);
void handleBlocky16(Common::SeekableReadStream *stream, uint32 size);
void handleFrameObject(Common::SeekableReadStream *stream, uint32 size);
void handleDeltaPalette(Common::SeekableReadStream *stream, int32 size);
void init();
Graphics::Surface *decodeNextFrame() override;
const byte *getPalette() const override { _dirtyPalette = false; return _palette; }
bool hasDirtyPalette() const override { return _dirtyPalette; }
byte *getPal() { _dirtyPalette = true; return _palette; }
int _x, _y;
private:
void convertDemoFrame();
bool _is16Bit;
int32 _curFrame;
byte _palette[0x300];
int16 _deltaPal[0x300];
mutable bool _dirtyPalette;
int _width, _height;
Graphics::Surface _surface;
Graphics::PixelFormat _format;
Common::Rational _frameRate;
Blocky8 *_blocky8;
Blocky16 *_blocky16;
Codec48Decoder *_codec48;
int32 _nbframes;
int _frameStart;
};
class SmushAudioTrack : public AudioTrack {
public:
SmushAudioTrack(Audio::Mixer::SoundType soundType, bool isVima, int freq = 22050, int channels = -1);
~SmushAudioTrack();
Audio::AudioStream *getAudioStream() const override { return _queueStream; }
bool isSeekable() const override { return true; }
bool seek(const Audio::Timestamp &time) override;
void skipSamples(int samples);
inline int getRate() const { return _queueStream->getRate(); }
void handleVIMA(Common::SeekableReadStream *stream, uint32 size);
void handleIACT(Common::SeekableReadStream *stream, int32 size);
void init();
private:
bool _isVima;
byte _IACToutput[4096];
int32 _IACTpos;
int _channels;
int _freq;
Audio::QueuingAudioStream *_queueStream;
};
private:
void initFrames();
SmushAudioTrack *_audioTrack;
SmushVideoTrack *_videoTrack;
Common::SeekableReadStream *_file;
uint32 _startPos;
bool _videoPause;
bool _videoLooping;
struct Frame {
int frame;
int pos;
bool keyframe;
};
Frame *_frames;
static bool _demo;
};
} // end of namespace Grim
#endif

View File

@@ -0,0 +1,254 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
#include "common/endian.h"
#include "common/util.h"
namespace Grim {
static int16 imcTable1[] = {
7, 8, 9, 10, 11, 12, 13, 14, 16, 17,
19, 21, 23, 25, 28, 31, 34, 37, 41, 45,
50, 55, 60, 66, 73, 80, 88, 97, 107, 118,
130, 143, 157, 173, 190, 209, 230, 253, 279, 307,
337, 371, 408, 449, 494, 544, 598, 658, 724, 796,
876, 963, 1060, 1166, 1282, 1411, 1552, 1707, 1878, 2066,
2272, 2499, 2749, 3024, 3327, 3660, 4026, 4428, 4871, 5358,
5894, 6484, 7132, 7845, 8630, 9493, 10442, 11487, 12635, 13899,
15289, 16818, 18500, 20350, 22385, 24623, 27086, 29794, 32767
};
static int8 imcTable2[] = {
4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
4, 4, 4, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 5, 5, 5, 5, 5,
5, 5, 5, 5, 5, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
6, 6, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7
};
static int8 imcOtherTable1[] = {
-1, 4, -1, 4
};
static int8 imcOtherTable2[] = {
-1, -1, 2, 6, -1, -1, 2, 6
};
static int8 imcOtherTable3[] = {
-1, -1, -1, -1, 1, 2, 4, 6,
-1, -1, -1, -1, 1, 2, 4, 6
};
static int8 imcOtherTable4[] = {
-1, -1, -1, -1, -1, -1, -1, -1,
1, 1, 1, 2, 2, 4, 5, 6,
-1, -1, -1, -1, -1, -1, -1, -1,
1, 1, 1, 2, 2, 4, 5, 6
};
static int8 imcOtherTable5[] = {
-1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1,
1, 1, 1, 1, 1, 2, 2, 2,
2, 4, 4, 4, 5, 5, 6, 6,
-1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1,
1, 1, 1, 1, 1, 2, 2, 2,
2, 4, 4, 4, 5, 5, 6, 6
};
static int8 imcOtherTable6[] = {
-1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 2, 2, 2, 2, 2, 2,
2, 2, 4, 4, 4, 4, 4, 4,
5, 5, 5, 5, 6, 6, 6, 6,
-1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 2, 2, 2, 2, 2, 2,
2, 2, 4, 4, 4, 4, 4, 4,
5, 5, 5, 5, 6, 6, 6, 6
};
static int8 *offsets[] = {
imcOtherTable1, imcOtherTable2, imcOtherTable3,
imcOtherTable4, imcOtherTable5, imcOtherTable6
};
void vimaInit(uint16 *destTable) {
int destTableStartPos, incer;
for (destTableStartPos = 0, incer = 0; destTableStartPos < 64; destTableStartPos++, incer++) {
unsigned int destTablePos, imcTable1Pos;
for (imcTable1Pos = 0, destTablePos = destTableStartPos;
imcTable1Pos < sizeof(imcTable1) / sizeof(imcTable1[0]); imcTable1Pos++, destTablePos += 64) {
int put = 0, count, tableValue;
for (count = 32, tableValue = imcTable1[imcTable1Pos]; count != 0; count >>= 1, tableValue >>= 1) {
if (incer & count) {
put += tableValue;
}
}
destTable[destTablePos] = put;
}
}
}
void decompressVima(const byte *src, int16 *dest, int destLen, uint16 *destTable, bool isSmush) {
int numChannels = 1;
byte sBytes[2];
int16 sWords[2];
sBytes[0] = *src++;
if (sBytes[0] & 0x80) {
sBytes[0] = ~sBytes[0];
numChannels = 2;
}
sWords[0] = READ_BE_UINT16(src);
src += 2;
if (numChannels > 1) {
sBytes[1] = *src++;
sWords[1] = READ_BE_UINT16(src);
src += 2;
}
int numSamples = destLen / (numChannels * 2);
if (READ_BE_UINT32(src) == MKTAG('I', 'M', 'A', '4')) {
int outputWord = 0;
int currTablePos = 0;
static const int tableDeltas[16] = {
-1, -1, -1, -1,
2, 4, 6, 8,
-1, -1, -1, -1,
2, 4, 6, 8
};
int16 *destPos = dest;
int curai = 7;
byte inputByte = 0;
bool nibbleSwitch = false;
src += 4;
if (isSmush) {
outputWord = READ_LE_INT16(src);
currTablePos = src[2];
src += 3;
}
for (int sample = 0; sample < numSamples; sample++) {
byte nibble;
if (!nibbleSwitch) {
inputByte = *src++;
nibble = inputByte >> 4;
} else
nibble = inputByte & 0xf;
nibbleSwitch = !nibbleSwitch;
currTablePos = CLIP(currTablePos + tableDeltas[nibble & 0xf], 0, 88);
int delta = curai >> 3;
if (nibble & 4) {
delta = delta + curai;
}
if (nibble & 2) {
delta = delta + (curai >> 1);
}
if (nibble & 1) {
delta = delta + (curai >> 2);
}
if (nibble & 8) {
delta = -delta;
}
outputWord = CLIP(outputWord + delta, -0x8000, 0x7fff);
if (currTablePos > 0)
curai = imcTable1[currTablePos];
for (int channel = 0; channel < numChannels; channel++)
WRITE_LE_UINT16(destPos + channel, outputWord);
destPos += numChannels;
}
return;
}
int bits = READ_BE_UINT16(src);
int bitPtr = 0;
src += 2;
for (int channel = 0; channel < numChannels; channel++) {
int16 *destPos = dest + channel;
int currTablePos = sBytes[channel];
int outputWord = sWords[channel];
for (int sample = 0; sample < numSamples; sample++) {
int numBits = imcTable2[currTablePos];
bitPtr += numBits;
int highBit = 1 << (numBits - 1);
int lowBits = highBit - 1;
int val = (bits >> (16 - bitPtr)) & (highBit | lowBits);
if (bitPtr > 7) {
bits = ((bits & 0xff) << 8) | *src++;
bitPtr -= 8;
}
if (val & highBit)
val ^= highBit;
else
highBit = 0;
if (val == lowBits) {
outputWord = ((int16)(bits << bitPtr) & 0xffffff00);
bits = ((bits & 0xff) << 8) | *src++;
outputWord |= ((bits >> (8 - bitPtr)) & 0xff);
bits = ((bits & 0xff) << 8) | *src++;
} else {
int index = (val << (7 - numBits)) | (currTablePos << 6);
int delta = destTable[index];
if (val)
delta += (imcTable1[currTablePos] >> (numBits - 1));
if (highBit)
delta = -delta;
outputWord += delta;
if (outputWord < -0x8000)
outputWord = -0x8000;
else if (outputWord > 0x7fff)
outputWord = 0x7fff;
}
WRITE_LE_UINT16(destPos, outputWord);
destPos += numChannels;
currTablePos += offsets[numBits - 2][val];
if (currTablePos < 0)
currTablePos = 0;
else if (currTablePos > 88)
currTablePos = 88;
}
}
}
} // end of namespace Grim

View File

@@ -0,0 +1,32 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef GRIM_VIMA_H
#define GRIM_VIMA_H
namespace Grim {
void vimaInit(uint16 *destTable);
void decompressVima(const byte *src, int16 *dest, int destLen, uint16 *destTable, bool isSmush);
} // end of namespace Grim
#endif