1
0
mirror of https://github.com/XProger/OpenLara.git synced 2025-08-14 00:54:05 +02:00

#129 PSX FMVs support

This commit is contained in:
XProger
2018-07-14 04:52:08 +03:00
parent 73607410ca
commit 8f03934857
5 changed files with 888 additions and 78 deletions

View File

@@ -872,67 +872,107 @@ namespace TR {
switch (id) {
// TR1
case LVL_TR1_TITLE :
CHECK_FILE("FMV/CAFE.FMV");
CHECK_FILE("FMV/CAFE.RPL");
CHECK_FILE("video/1/CAFE.FMV");
return "video/1/CAFE.RPL";
case LVL_TR1_GYM :
CHECK_FILE("FMV/MANSION.FMV");
CHECK_FILE("FMV/MANSION.RPL");
CHECK_FILE("video/1/MANSION.FMV");
return "video/1/MANSION.RPL";
case LVL_TR1_1 :
CHECK_FILE("FMV/SNOW.FMV");
CHECK_FILE("FMV/SNOW.RPL");
CHECK_FILE("video/1/SNOW.FMV");
return "video/1/SNOW.RPL";
case LVL_TR1_4 :
CHECK_FILE("FMV/LIFT.FMV");
CHECK_FILE("FMV/LIFT.RPL");
CHECK_FILE("video/1/LIFT.FMV");
return "video/1/LIFT.RPL";
case LVL_TR1_8A :
CHECK_FILE("FMV/VISION.FMV");
CHECK_FILE("FMV/VISION.RPL");
CHECK_FILE("video/1/VISION.FMV");
return "video/1/VISION.RPL";
case LVL_TR1_10A :
CHECK_FILE("FMV/CANYON.FMV");
CHECK_FILE("FMV/CANYON.RPL");
CHECK_FILE("video/1/CANYON.FMV");
return "video/1/CANYON.RPL";
case LVL_TR1_10B :
CHECK_FILE("FMV/PIRAMID.FMV");
CHECK_FILE("FMV/PIRAMID.RPL");
CHECK_FILE("video/1/PIRAMID.FMV");
return "video/1/PIRAMID.RPL";
case LVL_TR1_CUT_4 :
CHECK_FILE("FMV/PRISON.FMV");
CHECK_FILE("FMV/PRISON.RPL");
CHECK_FILE("video/1/PRISON.FMV");
return "video/1/PRISON.RPL";
case LVL_TR1_EGYPT :
CHECK_FILE("FMV/END.FMV");
CHECK_FILE("FMV/END.RPL");
CHECK_FILE("video/1/END.FMV");
return "video/1/END.RPL";
// TR2
case LVL_TR2_TITLE :
CHECK_FILE("FMV/ANCIENT.FMV");
CHECK_FILE("fmv/ANCIENT.RPL");
CHECK_FILE("video/2/ANCIENT.FMV");
return "video/2/ANCIENT.RPL";
case LVL_TR2_WALL :
CHECK_FILE("FMV/MODERN.FMV");
CHECK_FILE("fmv/MODERN.RPL");
CHECK_FILE("video/2/MODERN.FMV");
return "video/2/MODERN.RPL";
case LVL_TR2_RIG :
CHECK_FILE("FMV/LANDING.FMV");
CHECK_FILE("fmv/LANDING.RPL");
CHECK_FILE("video/2/LANDING.FMV");
return "video/2/LANDING.RPL";
case LVL_TR2_UNWATER :
CHECK_FILE("FMV/MS.FMV");
CHECK_FILE("fmv/MS.RPL");
CHECK_FILE("video/2/MS.FMV");
return "video/2/MS.RPL";
case LVL_TR2_SKIDOO :
CHECK_FILE("FMV/CRASH.FMV");
CHECK_FILE("fmv/CRASH.RPL");
CHECK_FILE("video/2/CRASH.FMV");
return "video/2/CRASH.RPL";
case LVL_TR2_EMPRTOMB :
CHECK_FILE("FMV/JEEP.FMV");
CHECK_FILE("fmv/JEEP.RPL");
CHECK_FILE("video/2/JEEP.FMV");
return "video/2/JEEP.RPL";
case LVL_TR2_HOUSE :
CHECK_FILE("FMV/END.FMV");
CHECK_FILE("fmv/END.RPL");
CHECK_FILE("video/2/END.FMV");
return "video/2/END.RPL";
// TR3
case LVL_TR3_TITLE :
CHECK_FILE("FMV/INTRO.FMV");
CHECK_FILE("fmv/Intr_Eng.rpl");
CHECK_FILE("video/3/INTRO.FMV");
return "video/3/Intr_Eng.rpl";
case LVL_TR3_SHORE :
CHECK_FILE("FMV/LAGOON.FMV");
CHECK_FILE("fmv/Sail_Eng.rpl");
CHECK_FILE("video/3/LAGOON.FMV");
return "video/3/Sail_Eng.rpl";
case LVL_TR3_ANTARC :
CHECK_FILE("FMV/HUEY.FMV");
CHECK_FILE("fmv/Crsh_Eng.rpl");
CHECK_FILE("video/3/HUEY.FMV");
return "video/3/Crsh_Eng.rpl";
case LVL_TR3_STPAUL :
CHECK_FILE("FMV/END.FMV");
CHECK_FILE("fmv/Endgame.rpl");
CHECK_FILE("video/3/END.FMV");
return "video/3/Endgame.rpl";
// TR3
default : return NULL;
}
}

View File

@@ -1,9 +1,10 @@
#ifndef H_SOUND
#define H_SOUND
#define DECODE_VAG
#define DECODE_ADPCM
#define DECODE_IMA
#define DECODE_VAG
#define DECODE_XA
#define DECODE_OGG
@@ -28,12 +29,25 @@
namespace Sound {
static const int8 SPU_POS[] = { 0, 60, 115, 98, 122 };
static const int8 SPU_NEG[] = { 0, 0, -52, -55, -60 };
static const int16 SPU_ZIG_ZAG[7][30] = {
{ 0, 0, 0, 0, 0, 0, -0x0002, +0x000A, -0x0022, +0x0041, -0x0054, +0x0034, +0x0009, -0x010A, +0x0400, -0x0A78, +0x234C, +0x6794, -0x1780, +0x0BCD, -0x0623, +0x0350, -0x016D, +0x006B, +0x000A, -0x0010, +0x0011, -0x0008, +0x0003, -0x0001 },
{ 0, 0, 0, 0, -0x0002, 0, +0x0003, -0x0013, +0x003C, -0x004B, +0x00A2, -0x00E3, +0x0132, -0x0043, -0x0267, +0x0C9D, +0x74BB, -0x11B4, +0x09B8, -0x05BF, +0x0372, -0x01A8, +0x00A6, -0x001B, +0x0005, +0x0006, -0x0008, +0x0003, -0x0001, 0 },
{ 0, 0, 0, -0x0001, +0x0003, -0x0002, -0x0005, +0x001F, -0x004A, +0x00B3, -0x0192, +0x02B1, -0x039E, +0x04F8, -0x05A6, +0x7939, -0x05A6, +0x04F8, -0x039E, +0x02B1, -0x0192, +0x00B3, -0x004A, +0x001F, -0x0005, -0x0002, +0x0003, -0x0001, 0, 0 },
{ 0, 0, -0x0001, +0x0003, -0x0008, +0x0006, +0x0005, -0x001B, +0x00A6, -0x01A8, +0x0372, -0x05BF, +0x09B8, -0x11B4, +0x74BB, +0x0C9D, -0x0267, -0x0043, +0x0132, -0x00E3, +0x00A2, -0x004B, +0x003C, -0x0013, +0x0003, 0, -0x0002, 0, 0, 0 },
{ 0, -0x0001, +0x0003, -0x0008, +0x0011, -0x0010, +0x000A, +0x006B, -0x016D, +0x0350, -0x0623, +0x0BCD, -0x1780, +0x6794, +0x234C, -0x0A78, +0x0400, -0x010A, +0x0009, +0x0034, -0x0054, +0x0041, -0x0022, +0x000A, -0x0001, 0, +0x0001, 0, 0, 0 },
{ 0, +0x0002, -0x0008, +0x0010, -0x0023, +0x002B, +0x001A, -0x00EB, +0x027B, -0x0548, +0x0AFA, -0x16FA, +0x53E0, +0x3C07, -0x1249, +0x080E, -0x0347, +0x015B, -0x0044, -0x0017, +0x0046, -0x0023, +0x0011, -0x0005, 0, 0, 0, 0, 0, 0 },
{ 0, -0x0005, +0x0011, -0x0023, +0x0046, -0x0017, -0x0044, +0x015B, -0x0347, +0x080E, -0x1249, +0x3C07, +0x53E0, -0x16FA, +0x0AFA, -0x0548, +0x027B, -0x00EB, +0x001A, +0x002B, -0x0023, +0x0010, -0x0008, +0x0002, 0, 0, 0, 0, 0, 0 },
};
struct Frame {
short L, R;
int16 L, R;
};
struct FrameHI {
int L, R;
int32 L, R;
};
namespace Filter {
@@ -384,10 +398,7 @@ namespace Sound {
VAG(Stream *stream) : Decoder(stream, 1, 11025), s1(0), s2(0), bufferSize(0) {}
void predicate(short value) {
int inc[] = { 0, 60, 115, 98, 122 };
int dec[] = { 0, 0, -52, -55, -60 };
int s = (s1 * inc[pred] + s2 * dec[pred]) >> 6;
int s = (s1 * SPU_POS[pred] + s2 * SPU_NEG[pred]) >> 6;
s = clamp((value >> shift) + s, -32768, 32767);
s2 = s1;
s1 = s;
@@ -447,6 +458,126 @@ namespace Sound {
};
#endif
#ifdef DECODE_XA
// http://problemkaputt.de/psx-spx.htm#cdromxaaudioadpcmcompression
struct XA : Decoder {
uint8 pred, shift, flags;
int s1, s2;
Frame buffer[18 * 112];
int pos;
struct Group {
uint8 params[16];
uint8 data[112];
} groups[18];
Frame prevFrames[2];
Frame lerpFrames[32];
uint32 lerpPos;
XA(Stream *stream) : Decoder(stream, 1, 11025), s1(0), s2(0), pos(COUNT(buffer)), lerpPos(0) {
memset(prevFrames, 0, sizeof(prevFrames));
memset(lerpFrames, 0, sizeof(lerpFrames));
}
void decode28(Group &group, int block, int channel) {
int16 *dst = channel ? &buffer[pos].R : &buffer[pos].L;
int16 &old = channel ? prevFrames[0].R : prevFrames[0].L;
int16 &older = channel ? prevFrames[1].R : prevFrames[1].L;
int shift = 12 - (group.params[4 + block * 2 + channel] & 0x0F);
int filter = (group.params[4 + block * 2 + channel] & 0x30) >> 4;
int f0 = SPU_POS[filter];
int f1 = SPU_NEG[filter];
for (int i = 0; i < 28; i++) {
int t = (group.data[block + i * 4] >> (channel * 4)) & 0x0F;
if (t & 8)
t -= 16;
int s = (t << shift) + ((old * f0 + older * f1 + 32) / 64);
s = clamp(s, -32768, 32767);
older = old;
old = s;
dst[0] = s;
dst += 2; // skip second channel
}
}
void processBlock() {
if (stream->pos >= stream->size)
return;
stream->seek(24); // skip sync header
stream->raw(groups, sizeof(groups));
pos = 0;
for (int i = 0; i < COUNT(groups); i++)
for (int j = 0; j < 4; j++) {
decode28(groups[i], j, 0);
decode28(groups[i], j, 1);
pos += 28;
}
pos = 0;
}
void ZigZagOut(Frame &frame, uint8 p, const int16 *LUT) {
FrameHI sum;
sum.L = sum.R = 0;
for (uint8 i = 1; i < 30; i++) {
Frame &f = lerpFrames[uint8(p - i) & 0x1F];
sum.L += f.L * LUT[i];
sum.R += f.R * LUT[i];
}
frame.L = clamp(sum.L >> 15, -32767, 32767);
frame.R = clamp(sum.R >> 15, -32767, 32767);
}
virtual int decode(Frame *frames, int count) {
if (pos >= COUNT(buffer))
processBlock();
ASSERT((int(COUNT(buffer)) - pos) % 6 == 0)
ASSERT(count % 7 == 0)
count = min(count, (int(COUNT(buffer)) - pos) / 6 * 7);
int i = 0;
while (i < count) {
ASSERT(pos < COUNT(buffer));
lerpFrames[lerpPos++ & 0x1F] = buffer[pos++];
lerpFrames[lerpPos++ & 0x1F] = buffer[pos++];
lerpFrames[lerpPos++ & 0x1F] = buffer[pos++];
lerpFrames[lerpPos++ & 0x1F] = buffer[pos++];
lerpFrames[lerpPos++ & 0x1F] = buffer[pos++];
lerpFrames[lerpPos++ & 0x1F] = buffer[pos++];
ZigZagOut(frames[i++], lerpPos, SPU_ZIG_ZAG[0]);
ZigZagOut(frames[i++], lerpPos, SPU_ZIG_ZAG[1]);
ZigZagOut(frames[i++], lerpPos, SPU_ZIG_ZAG[2]);
ZigZagOut(frames[i++], lerpPos, SPU_ZIG_ZAG[3]);
ZigZagOut(frames[i++], lerpPos, SPU_ZIG_ZAG[4]);
ZigZagOut(frames[i++], lerpPos, SPU_ZIG_ZAG[5]);
ZigZagOut(frames[i++], lerpPos, SPU_ZIG_ZAG[6]);
}
ASSERT(i == count);
return count;
}
virtual void replay() {
stream->setPos(0);
s1 = s2 = 0;
}
};
#endif
#ifdef DECODE_MP3
struct MP3 : Decoder {
mp3_decoder_t mp3;

View File

@@ -474,16 +474,16 @@ struct Texture : GAPI::Texture {
static void rncGetOffset(BitStream &bs, uint16 &offset) {
offset = 0;
if (bs.readBit()) {
offset = bs.readBit();
if (bs.readBitBE()) {
offset = bs.readBitBE();
if (bs.readBit()) {
offset = ((offset << 1) | bs.readBit()) | 4;
if (bs.readBitBE()) {
offset = ((offset << 1) | bs.readBitBE()) | 4;
if (!bs.readBit())
offset = (offset << 1) | bs.readBit();
if (!bs.readBitBE())
offset = (offset << 1) | bs.readBitBE();
} else if (!offset)
offset = bs.readBit() + 2;
offset = bs.readBitBE() + 2;
}
offset = ((offset << 8) | bs.readByte()) + 1;
}
@@ -514,17 +514,17 @@ struct Texture : GAPI::Texture {
uint32 length = 0;
uint16 offset = 0;
bs.readBits(2);
bs.readBE(2);
while (bs.data < bs.end && dst < end) {
if (!bs.readBit()) {
if (!bs.readBitBE()) {
*dst++ = bs.readByte();
} else {
if (bs.readBit()) {
if (bs.readBit()) {
if (bs.readBit()) {
if (bs.readBitBE()) {
if (bs.readBitBE()) {
if (bs.readBitBE()) {
length = bs.readByte() + 8;
if (length == 8) {
bs.readBit();
bs.readBitBE();
continue;
}
} else
@@ -541,9 +541,9 @@ struct Texture : GAPI::Texture {
dst++;
}
} else {
length = bs.readBit() + 4;
if (bs.readBit())
length = ((length - 1) << 1) + bs.readBit();
length = bs.readBitBE() + 4;
if (bs.readBitBE())
length = ((length - 1) << 1) + bs.readBitBE();
if (length != 9) {
rncGetOffset(bs, offset);
@@ -553,7 +553,7 @@ struct Texture : GAPI::Texture {
dst++;
}
} else {
length = (bs.readBits(4) << 2) + 12;
length = (bs.readBE(4) << 2) + 12;
while (length--)
*dst++ = bs.readByte();
}

View File

@@ -1438,38 +1438,61 @@ void osRWUnlockWrite(void *obj) {
#endif
static const uint32 BIT_MASK[] = {
0x00000000,
0x00000001, 0x00000003, 0x00000007, 0x0000000F,
0x0000001F, 0x0000003F, 0x0000007F, 0x000000FF,
0x000001FF, 0x000003FF, 0x000007FF, 0x00000FFF,
0x00001FFF, 0x00003FFF, 0x00007FFF, 0x0000FFFF,
0x0001FFFF, 0x0003FFFF, 0x0007FFFF, 0x000FFFFF,
0x001FFFFF, 0x003FFFFF, 0x007FFFFF, 0x00FFFFFF,
0x01FFFFFF, 0x03FFFFFF, 0x07FFFFFF, 0x0FFFFFFF,
0x1FFFFFFF, 0x3FFFFFFF, 0x7FFFFFFF, 0xFFFFFFFF,
};
struct BitStream {
uint8 *data;
uint8 *end;
uint8 index;
uint8 value;
uint8 *data;
uint8 *end;
uint16 index;
uint16 value;
BitStream(uint8 *data, int size) : data(data), end(data + size), index(0), value(0) {}
uint32 read(int count) {
uint32 bits = 0;
inline uint32 readBit() {
uint32 bit;
int m = count - 1;
if (!index--) {
value = *data++;
index = 7;
}
bit = value & 1;
value >>= 1;
return bit;
}
uint32 read(int count) {
uint32 bits = 0, mask = 1;
if (index == 0) {
if (count > 7) {
count -= 8;
mask <<= 8;
bits = *data++;
}
}
while (count--) {
if (!index) {
ASSERT(data < end);
value = *data++;
index = 8;
}
if (value & 1)
bits |= (1 << (m - count));
value >>= 1;
index--;
if (readBit())
bits += mask;
mask <<= 1;
}
return bits;
}
uint8 readBits(int count) {
uint8 readBE(int count) {
uint32 bits = 0;
while (count--) {
@@ -1491,14 +1514,61 @@ struct BitStream {
return bits;
}
uint8 readBit() {
return readBits(1);
uint8 readBitBE() {
return readBE(1);
}
uint8 readByte() {
ASSERT(data < end);
return *data++;
}
uint32 readWord(bool littleEndian) {
uint8 a, b;
if (littleEndian) {
a = data[0];
b = data[1];
} else {
b = data[0];
a = data[1];
}
data += 2;
return a + (b << 8);
}
uint32 readU(int count) {
if (!index) {
value = readWord(true);
index = 16;
}
uint32 bits;
if (count <= index) {
bits = (value >> (index - count)) & BIT_MASK[count];
index -= count;
} else {
bits = value & BIT_MASK[index];
count -= index;
index = 0;
while (count >= 16) {
bits = (bits << 16) | readWord(true);
count -= 16;
}
if (count > 0) {
value = readWord(true);
index = 16 - count;
bits = (bits << count) | (value >> index);
}
}
return bits;
}
void skip(int count) {
readU(count);
}
};

View File

@@ -5,6 +5,164 @@
#include "texture.h"
#include "sound.h"
struct AC_ENTRY {
uint8 code;
uint8 skip;
uint8 ac;
uint8 length;
};
// ISO 13818-2 table B-14
static const AC_ENTRY STR_AC[] = {
// signBit = (8 + shift) - length
// AC_LUT_1 (shift = 1)
{ 0b11000000 , 1 , 1 , 4 },
{ 0b10000000 , 0 , 2 , 5 },
{ 0b10100000 , 2 , 1 , 5 },
{ 0b01010000 , 0 , 3 , 6 },
{ 0b01100000 , 4 , 1 , 6 },
{ 0b01110000 , 3 , 1 , 6 },
{ 0b00100000 , 7 , 1 , 7 },
{ 0b00101000 , 6 , 1 , 7 },
{ 0b00110000 , 1 , 2 , 7 },
{ 0b00111000 , 5 , 1 , 7 },
{ 0b00010000 , 2 , 2 , 8 },
{ 0b00010100 , 9 , 1 , 8 },
{ 0b00011000 , 0 , 4 , 8 },
{ 0b00011100 , 8 , 1 , 8 },
{ 0b01000000 , 13 , 1 , 9 },
{ 0b01000010 , 0 , 6 , 9 },
{ 0b01000100 , 12 , 1 , 9 },
{ 0b01000110 , 11 , 1 , 9 },
{ 0b01001000 , 3 , 2 , 9 },
{ 0b01001010 , 1 , 3 , 9 },
{ 0b01001100 , 0 , 5 , 9 },
{ 0b01001110 , 10 , 1 , 9 },
// AC_LUT_6 (shift = 6)
{ 0b10000000 , 16 , 1 , 11 },
{ 0b10010000 , 5 , 2 , 11 },
{ 0b10100000 , 0 , 7 , 11 },
{ 0b10110000 , 2 , 3 , 11 },
{ 0b11000000 , 1 , 4 , 11 },
{ 0b11010000 , 15 , 1 , 11 },
{ 0b11100000 , 14 , 1 , 11 },
{ 0b11110000 , 4 , 2 , 11 },
{ 0b01000000 , 0 , 11 , 13 },
{ 0b01000100 , 8 , 2 , 13 },
{ 0b01001000 , 4 , 3 , 13 },
{ 0b01001100 , 0 , 10 , 13 },
{ 0b01010000 , 2 , 4 , 13 },
{ 0b01010100 , 7 , 2 , 13 },
{ 0b01011000 , 21 , 1 , 13 },
{ 0b01011100 , 20 , 1 , 13 },
{ 0b01100000 , 0 , 9 , 13 },
{ 0b01100100 , 19 , 1 , 13 },
{ 0b01101000 , 18 , 1 , 13 },
{ 0b01101100 , 1 , 5 , 13 },
{ 0b01110000 , 3 , 3 , 13 },
{ 0b01110100 , 0 , 8 , 13 },
{ 0b01111000 , 6 , 2 , 13 },
{ 0b01111100 , 17 , 1 , 13 },
{ 0b00100000 , 10 , 2 , 14 },
{ 0b00100010 , 9 , 2 , 14 },
{ 0b00100100 , 5 , 3 , 14 },
{ 0b00100110 , 3 , 4 , 14 },
{ 0b00101000 , 2 , 5 , 14 },
{ 0b00101010 , 1 , 7 , 14 },
{ 0b00101100 , 1 , 6 , 14 },
{ 0b00101110 , 0 , 15 , 14 },
{ 0b00110000 , 0 , 14 , 14 },
{ 0b00110010 , 0 , 13 , 14 },
{ 0b00110100 , 0 , 12 , 14 },
{ 0b00110110 , 26 , 1 , 14 },
{ 0b00111000 , 25 , 1 , 14 },
{ 0b00111010 , 24 , 1 , 14 },
{ 0b00111100 , 23 , 1 , 14 },
{ 0b00111110 , 22 , 1 , 14 },
// AC_LUT_9 (shift = 9)
{ 0b10000000 , 0 , 31 , 15 },
{ 0b10001000 , 0 , 30 , 15 },
{ 0b10010000 , 0 , 29 , 15 },
{ 0b10011000 , 0 , 28 , 15 },
{ 0b10100000 , 0 , 27 , 15 },
{ 0b10101000 , 0 , 26 , 15 },
{ 0b10110000 , 0 , 25 , 15 },
{ 0b10111000 , 0 , 24 , 15 },
{ 0b11000000 , 0 , 23 , 15 },
{ 0b11001000 , 0 , 22 , 15 },
{ 0b11010000 , 0 , 21 , 15 },
{ 0b11011000 , 0 , 20 , 15 },
{ 0b11100000 , 0 , 19 , 15 },
{ 0b11101000 , 0 , 18 , 15 },
{ 0b11110000 , 0 , 17 , 15 },
{ 0b11111000 , 0 , 16 , 15 },
{ 0b01000000 , 0 , 40 , 16 },
{ 0b01000100 , 0 , 39 , 16 },
{ 0b01001000 , 0 , 38 , 16 },
{ 0b01001100 , 0 , 37 , 16 },
{ 0b01010000 , 0 , 36 , 16 },
{ 0b01010100 , 0 , 35 , 16 },
{ 0b01011000 , 0 , 34 , 16 },
{ 0b01011100 , 0 , 33 , 16 },
{ 0b01100000 , 0 , 32 , 16 },
{ 0b01100100 , 1 , 14 , 16 },
{ 0b01101000 , 1 , 13 , 16 },
{ 0b01101100 , 1 , 12 , 16 },
{ 0b01110000 , 1 , 11 , 16 },
{ 0b01110100 , 1 , 10 , 16 },
{ 0b01111000 , 1 , 9 , 16 },
{ 0b01111100 , 1 , 8 , 16 },
{ 0b00100000 , 1 , 18 , 17 },
{ 0b00100010 , 1 , 17 , 17 },
{ 0b00100100 , 1 , 16 , 17 },
{ 0b00100110 , 1 , 15 , 17 },
{ 0b00101000 , 6 , 3 , 17 },
{ 0b00101010 , 16 , 2 , 17 },
{ 0b00101100 , 15 , 2 , 17 },
{ 0b00101110 , 14 , 2 , 17 },
{ 0b00110000 , 13 , 2 , 17 },
{ 0b00110010 , 12 , 2 , 17 },
{ 0b00110100 , 11 , 2 , 17 },
{ 0b00110110 , 31 , 1 , 17 },
{ 0b00111000 , 30 , 1 , 17 },
{ 0b00111010 , 29 , 1 , 17 },
{ 0b00111100 , 28 , 1 , 17 },
{ 0b00111110 , 27 , 1 , 17 },
};
static const uint8 STR_ZIG_ZAG[] = {
0, 1, 8, 16, 9, 2, 3, 10,
17, 24, 32, 25, 18, 11, 4, 5,
12, 19, 26, 33, 40, 48, 41, 34,
27, 20, 13, 6, 7, 14, 21, 28,
35, 42, 49, 56, 57, 50, 43, 36,
29, 22, 15, 23, 30, 37, 44, 51,
58, 59, 52, 45, 38, 31, 39, 46,
53, 60, 61, 54, 47, 55, 62, 63,
};
static const uint8 STR_QUANTIZATION[] = {
2, 16, 19, 22, 26, 27, 29, 34,
16, 16, 22, 24, 27, 29, 34, 37,
19, 22, 26, 27, 29, 34, 34, 38,
22, 22, 26, 27, 29, 34, 37, 40,
22, 26, 27, 29, 32, 35, 40, 48,
26, 27, 29, 32, 35, 40, 48, 58,
26, 27, 29, 34, 38, 46, 56, 69,
27, 29, 35, 38, 46, 56, 69, 83
};
static const float STR_IDCT[] = {
0.354f, 0.354f, 0.354f, 0.354f, 0.354f, 0.354f, 0.354f, 0.354f,
0.490f, 0.416f, 0.278f, 0.098f, -0.098f, -0.278f, -0.416f, -0.490f,
0.462f, 0.191f, -0.191f, -0.462f, -0.462f, -0.191f, 0.191f, 0.462f,
0.416f, -0.098f, -0.490f, -0.278f, 0.278f, 0.490f, 0.098f, -0.416f,
0.354f, -0.354f, -0.354f, 0.354f, 0.354f, -0.354f, -0.354f, 0.354f,
0.278f, -0.490f, 0.098f, 0.416f, -0.416f, -0.098f, 0.490f, -0.278f,
0.191f, -0.462f, 0.462f, -0.191f, -0.191f, 0.462f, -0.462f, 0.191f,
0.098f, -0.278f, 0.416f, -0.490f, 0.490f, -0.416f, 0.278f, -0.098f,
};
struct Video {
union Color32 {
@@ -28,12 +186,51 @@ struct Video {
a = 255;
}
void SetYCbCrPSX(int32 Y, int32 Cb, int32 Cr) {
int32 R = ((91893 * Cr) >> 16);
int32 G = ((-(22525 * Cb) - (46812 * Cr)) >> 16);
int32 B = ((116224 * Cb) >> 16);
Y += 128;
r = clamp(Y + R, 0, 255);
g = clamp(Y + G, 0, 255);
b = clamp(Y + B, 0, 255);
a = 255;
}
void SetYUV(int32 Y, int32 U, int32 V) {
r = clamp(Y + (74698 * V >> 16), 0, 255);
g = clamp(Y - ((25863 * U + 38049 * V) >> 16), 0, 255);
b = clamp(Y + (133174 * U >> 16), 0, 255);
a = 255;
}
static void YCbCr_420_PSX(int32 Y0, int32 Y1, int32 Y2, int32 Y3, int32 Cb, int32 Cr, Color32 &C0, Color32 &C1, Color32 &C2, Color32 &C3) {
int32 R = ((91893 * Cr) >> 16) + 128;
int32 G = ((-(22525 * Cb) - (46812 * Cr)) >> 16) + 128;
int32 B = ((116224 * Cb) >> 16) + 128;
C0.r = clamp(Y0 + R, 0, 255);
C0.g = clamp(Y0 + G, 0, 255);
C0.b = clamp(Y0 + B, 0, 255);
C0.a = 255;
C1.r = clamp(Y1 + R, 0, 255);
C1.g = clamp(Y1 + G, 0, 255);
C1.b = clamp(Y1 + B, 0, 255);
C1.a = 255;
C2.r = clamp(Y2 + R, 0, 255);
C2.g = clamp(Y2 + G, 0, 255);
C2.b = clamp(Y2 + B, 0, 255);
C2.a = 255;
C3.r = clamp(Y3 + R, 0, 255);
C3.g = clamp(Y3 + G, 0, 255);
C3.b = clamp(Y3 + B, 0, 255);
C3.a = 255;
}
};
struct Decoder : Sound::Decoder {
@@ -41,10 +238,10 @@ struct Video {
Decoder(Stream *stream) : Sound::Decoder(stream, 2, 0) {}
virtual ~Decoder() { /* delete stream; */ }
virtual bool decode(uint8 *frame) { return false; }
virtual bool decodeVideo(Color32 *pixels) { return false; }
};
// based on ffmpeg code https://github.com/FFmpeg/FFmpeg/blob/master/libavcodec/escape124.c
// based on ffmpeg https://github.com/FFmpeg/FFmpeg/blob/master/libavcodec/ implementation of escape codecs
struct Escape : Decoder {
int vfmt, bpp;
int sfmt, rate, channels, bps;
@@ -135,11 +332,12 @@ struct Video {
if (sfmt == 1)
audioDecoder = new Sound::PCM(stream, channels, rate, 0, bps); // TR2
else if (sfmt == 101)
else if (sfmt == 101) {
if (bps == 8)
audioDecoder = new Sound::PCM(stream, channels, rate, 0, bps); // TR1
else
audioDecoder = new Sound::IMA(stream, channels, rate); // TR3
}
}
virtual ~Escape() {
@@ -180,7 +378,7 @@ struct Video {
int getSkip124(BitStream &bs) {
int value;
if ((value = bs.read(1)) != 1 ||
if ((value = bs.readBit()) != 1 ||
(value += bs.read(3)) != 8 ||
(value += bs.read(7)) != 135)
return value;
@@ -191,10 +389,10 @@ struct Video {
int getSkip130(BitStream &bs) {
int value;
if (value = bs.read(1)) return 0;
if (value = bs.read(3)) return value;
if (value = bs.read(8)) return value + 7;
if (value = bs.read(15)) return value + 262;
if ((value = bs.readBit())) return 0;
if ((value = bs.read(3))) return value;
if ((value = bs.read(8))) return value + 7;
if ((value = bs.read(15))) return value + 262;
return -1;
}
@@ -208,10 +406,10 @@ struct Video {
}
void decodeMacroBlock(BitStream &bs, MacroBlock &mb, int &cbIndex, int sbIndex) {
int value = bs.read(1);
int value = bs.readBit();
if (value) {
static const int8 trans[3][2] = { {2, 1}, {0, 2}, {1, 0} };
value = bs.read(1);
value = bs.readBit();
cbIndex = trans[cbIndex][value];
}
@@ -232,7 +430,7 @@ struct Video {
dst[9] = mb.pixels[3];
}
bool decode(uint8 *frame) {
virtual bool decodeVideo(Color32 *pixels) {
if (curVideoChunk >= chunksCount)
return false;
@@ -245,15 +443,15 @@ struct Video {
stream->setPos(chunks[curVideoChunk].offset + curVideoPos);
switch (vfmt) {
case 124 : return decode124(frame);
case 130 : return decode130(frame);
case 124 : return decode124(pixels);
case 130 : return decode130(pixels);
default : ASSERT(false);
}
return false;
}
bool decode124(uint8 *frame) {
bool decode124(Color32 *pixels) {
uint32 flags, size;
stream->read(flags);
stream->read(size);
@@ -340,7 +538,7 @@ struct Video {
} else {
copySuperBlock(sb.pixels, 8, src, width);
while (!bs.read(1)) {
while (!bs.readBit()) {
decodeMacroBlock(bs, mb, cbIndex, sbIndex);
uint16 mask = bs.read(16);
multiMask |= mask;
@@ -349,7 +547,7 @@ struct Video {
insertMacroBlock(sb, mb, i);
}
if (!bs.read(1)) {
if (!bs.readBit()) {
uint16 invMask = bs.read(4);
for (int i = 0; i < 4; i++)
multiMask ^= ((invMask & (1 << i)) ? 0x0F : bs.read(4)) << (i * 4);
@@ -360,7 +558,7 @@ struct Video {
}
} else
if (flags & (1 << 16))
while (!bs.read(1)) {
while (!bs.readBit()) {
decodeMacroBlock(bs, mb, cbIndex, sbIndex);
insertMacroBlock(sb, mb, bs.read(4));
}
@@ -373,13 +571,13 @@ struct Video {
delete[] data;
memcpy(frame, nextFrame, width * height * 4);
memcpy(pixels, nextFrame, width * height * 4);
swap(prevFrame, nextFrame);
return true;
}
bool decode130(uint8 *frame) {
bool decode130(Color32 *pixels) {
static const uint8 offsetLUT[] = {
2, 4, 10, 20
@@ -449,7 +647,7 @@ struct Video {
V = oV[0];
luma = *lumaPtr;
} else {
if (bs.read(1)) {
if (bs.readBit()) {
uint32 sign = bs.read(6);
uint32 diff = bs.read(2);
@@ -458,15 +656,15 @@ struct Video {
for (int i = 0; i < 4; i++)
Y[i] = clamp(luma + offsetLUT[diff] * signLUT[sign][i], 0U, 63U);
} else if (bs.read(1)) {
luma = bs.read(1) ? bs.read(6) : ((luma + lumaLUT[bs.read(3)]) & 63);
} else if (bs.readBit()) {
luma = bs.readBit() ? bs.read(6) : ((luma + lumaLUT[bs.read(3)]) & 63);
for (int i = 0; i < 4; i++)
Y[i] = luma;
}
if (bs.read(1)) {
if (bs.read(1)) {
if (bs.readBit()) {
if (bs.readBit()) {
U = bs.read(5);
V = bs.read(5);
} else {
@@ -502,7 +700,7 @@ struct Video {
nU = nY + width * height;
nV = nU + width * height / 4;
Color32 *p = (Color32*)frame;
Color32 *p = pixels;
for (int y = 0; y < height; y++) {
for (int x = 0; x < width; x++) {
int Y = nY[y * width + x] << 2;
@@ -564,9 +762,366 @@ struct Video {
}
};
// based on https://raw.githubusercontent.com/m35/jpsxdec/readme/jpsxdec/PlayStation1_STR_format.txt
struct STR : Decoder {
enum {
MAGIC_STR = 0x80010160,
SECTOR_SIZE = 2352,
VIDEO_CHUNK_SIZE = 2016,
VIDEO_MAX_CHUNKS = 16,
VIDEO_MAX_FRAMES = 4,
AUDIO_CHUNK_SIZE = (16 + 112) * 18, // XA ADPCM data block size
AUDIO_MAX_FRAMES = 10000,
BLOCK_EOD = 0xFE00,
};
struct SyncHeader {
uint32 sync[3];
uint8 mins, secs, block, mode;
uint8 interleaved;
uint8 channel;
struct {
uint8 isEnd:1, isVideo:1, isAudio:1, isData:1, trigger:1, form:1, realtime:1, eof:1;
} submode;
struct {
uint8 stereo:1, :1, rate:1, :1, bps:1, :3;
} coding;
uint32 dup;
};
struct Sector {
uint32 magic;
uint16 chunkIndex;
uint16 chunksCount;
uint32 frameIndex;
uint32 chunkSize;
uint16 width, height;
uint16 blocks;
uint16 unk1;
uint16 qscale;
uint16 version;
uint32 unk2;
};
struct VideoFrame {
uint8 data[VIDEO_CHUNK_SIZE * VIDEO_MAX_CHUNKS];
int size;
uint16 width;
uint16 height;
uint32 qscale;
};
struct AudioFrame {
int pos;
int size;
};
uint8 AC_LUT_1[256];
uint8 AC_LUT_6[256];
uint8 AC_LUT_9[256];
VideoFrame vFrames[VIDEO_MAX_FRAMES];
AudioFrame aFrames[AUDIO_MAX_FRAMES];
int vFrameIndex;
int aFrameIndex;
Sound::Decoder *audioDecoder;
struct {
uint8 code;
uint8 length;
} vlc[176];
int curAudioPos;
int curAudioFrame;
STR(Stream *stream) : Decoder(stream), vFrameIndex(-1), aFrameIndex(-1), audioDecoder(NULL) {
curAudioFrame = 0;
if (stream->pos >= stream->size) {
LOG("Can't load STR format \"%s\"\n", stream->name);
ASSERT(false);
return;
}
memset(AC_LUT_1, 255, sizeof(AC_LUT_1));
memset(AC_LUT_6, 255, sizeof(AC_LUT_6));
memset(AC_LUT_9, 255, sizeof(AC_LUT_9));
buildLUT(AC_LUT_1, 0, 22, 1);
buildLUT(AC_LUT_6, 22, 62, 6);
buildLUT(AC_LUT_9, 62, 110, 9);
int pos = stream->pos;
nextFrame();
VideoFrame &frame = vFrames[vFrameIndex];
width = (frame.width + 15) / 16 * 16;
height = (frame.height + 15) / 16 * 16;
fps = 150 / (frame.size / VIDEO_CHUNK_SIZE);
fps = (fps < 20) ? 15 : 30;
//fps = int(fps * 44100 / 37800);
stream->setPos(pos);
vFrameIndex = aFrameIndex = -1;
memset(aFrames, 0, sizeof(aFrames));
audioDecoder = new Sound::XA(stream);
}
virtual ~STR() {
if (audioDecoder) {
audioDecoder->stream = NULL;
delete audioDecoder;
}
}
void buildLUT(uint8 *LUT, int start, int end, int shift) {
for (int i = start; i < end; i++) {
const AC_ENTRY &e = STR_AC[i];
uint8 trash = (1 << (8 + shift - e.length + 1));
// fill the value and all possible endings
while (trash--)
LUT[e.code | trash] = i;
}
}
bool nextFrame() {
uint8 data[SECTOR_SIZE];
VideoFrame *vFrame = vFrames + vFrameIndex;
while (stream->pos < stream->size) {
if (stream->raw(data, sizeof(data)) != sizeof(data)) {
ASSERT(false);
return false;
}
SyncHeader *syncHeader = (SyncHeader*)data;
if (syncHeader->sync[0] != 0xFFFFFF00 || syncHeader->sync[1] != 0xFFFFFFFF || syncHeader->sync[2] != 0x00FFFFFF) {
ASSERT(false);
return false;
}
if (syncHeader->submode.isVideo || syncHeader->submode.isData) {
Sector *sector = (Sector*)(data + sizeof(SyncHeader));
if (sector->magic == MAGIC_STR) {
if (sector->chunkIndex == 0) {
vFrameIndex = (vFrameIndex + 1) % VIDEO_MAX_FRAMES;
vFrame = vFrames + vFrameIndex;
vFrame->size = 0;
vFrame->width = sector->width;
vFrame->height = sector->height;
vFrame->qscale = sector->qscale;
}
ASSERT(vFrame->size + VIDEO_CHUNK_SIZE < sizeof(vFrame->data));
memcpy(vFrame->data + vFrame->size, data + sizeof(SyncHeader) + sizeof(Sector), VIDEO_CHUNK_SIZE);
vFrame->size += VIDEO_CHUNK_SIZE;
if (sector->chunkIndex == sector->chunksCount - 1) {
//LOG("frame %d: %dx%d %d\n", sector->frameIndex, frame->width, frame->height, frame->size);
return true;
}
}
} else if (syncHeader->submode.isAudio) {
channels = syncHeader->coding.stereo ? 2 : 1;
freq = syncHeader->coding.rate ? 37800 : 18900;
aFrameIndex = (aFrameIndex + 1) % AUDIO_MAX_FRAMES;
AudioFrame *aFrame = aFrames + aFrameIndex;
aFrame->pos = stream->pos - sizeof(data);
aFrame->size = AUDIO_CHUNK_SIZE;
};
}
return false;
}
// http://jpsxdec.blogspot.com/2011/06/decoding-mpeg-like-bitstreams.html
bool readCode(BitStream &bs, int16 &skip, int16 &ac) {
if (bs.readU(1)) {
if (bs.readU(1)) {
skip = 0;
ac = bs.readU(1) ? -1 : 1;
return true;
}
return false; // end of block
}
int nz = 1;
while (!bs.readU(1))
nz++;
if (nz == 5) { // escape code == 0b1000001
uint16 esc = bs.readU(16);
skip = esc >> 10;
ac = esc & 0x3FF;
if (ac & 0x200)
ac -= 0x400;
return true;
}
uint8 *table, shift;
if (nz < 6) {
table = AC_LUT_1;
shift = 1;
} else if (nz < 9) {
table = AC_LUT_6;
shift = 6;
} else {
table = AC_LUT_9;
shift = 9;
}
BitStream state = bs;
uint32 code = (1 << 7) | state.readU(7);
code >>= nz - shift;
ASSERT(table);
int index = table[code];
ASSERT(index != 255);
const AC_ENTRY &e = STR_AC[index];
bs.skip(e.length - nz - 1);
skip = e.skip;
ac = (code & (1 << (8 + shift - e.length))) ? -e.ac : e.ac;
return true;
}
void IDCT(int16 *b) {
float t[64];
for (int x = 0; x < 8; x++)
for (int y = 0; y < 8; y++)
t[x + y * 8] = b[x + 0 * 8] * STR_IDCT[0 * 8 + y]
+ b[x + 1 * 8] * STR_IDCT[1 * 8 + y]
+ b[x + 2 * 8] * STR_IDCT[2 * 8 + y]
+ b[x + 3 * 8] * STR_IDCT[3 * 8 + y]
+ b[x + 4 * 8] * STR_IDCT[4 * 8 + y]
+ b[x + 5 * 8] * STR_IDCT[5 * 8 + y]
+ b[x + 6 * 8] * STR_IDCT[6 * 8 + y]
+ b[x + 7 * 8] * STR_IDCT[7 * 8 + y];
for (int x = 0; x < 8; x++)
for (int y = 0; y < 8; y++) {
int i = y * 8;
b[x + i] = int16(
t[0 + i] * STR_IDCT[x + 0 * 8]
+ t[1 + i] * STR_IDCT[x + 1 * 8]
+ t[2 + i] * STR_IDCT[x + 2 * 8]
+ t[3 + i] * STR_IDCT[x + 3 * 8]
+ t[4 + i] * STR_IDCT[x + 4 * 8]
+ t[5 + i] * STR_IDCT[x + 5 * 8]
+ t[6 + i] * STR_IDCT[x + 6 * 8]
+ t[7 + i] * STR_IDCT[x + 7 * 8]);
}
}
virtual bool decodeVideo(Color32 *pixels) {
if (!nextFrame())
return false;
VideoFrame *frame = vFrames + vFrameIndex;
BitStream bs(frame->data + 8, frame->size - 8); // make bitstream without frame header
int16 block[6][64]; // Cr, Cb, YTL, YTR, YBL, YBR
for (int bX = 0; bX < width / 16; bX++)
for (int bY = 0; bY < height / 16; bY++) {
memset(block, 0, sizeof(block));
for (int i = 0; i < 6; i++) {
bool nonZero = false;
int16 *channel = block[i];
channel[0] = bs.readU(10);
if (channel[0]) {
if (channel[0] & 0x200)
channel[0] -= 0x400;
channel[0] = channel[0] * STR_QUANTIZATION[0]; // DC
nonZero = true;
}
int16 skip, ac;
int index = 0;
while (readCode(bs, skip, ac)) {
index += 1 + skip;
ASSERT(index < 64);
int zIndex = STR_ZIG_ZAG[index];
channel[zIndex] = (ac * STR_QUANTIZATION[zIndex] * frame->qscale + 4) >> 3;
nonZero = true;
}
if (nonZero)
IDCT(channel);
}
Color32 *blockPixels = pixels + (width * bY * 16 + bX * 16);
for (uint32 i = 0; i < 8 * 8; i++) {
int x = (i % 8) * 2;
int y = (i / 8) * 2;
int j = (x & 7) + (y & 7) * 8;
Color32 *c = blockPixels + (width * y + x);
int16 *b = block[(x < 8) ? ((y < 8) ? 2 : 4) : ((y < 8) ? 3 : 5)];
Color32::YCbCr_420_PSX(b[j], b[j + 1], b[j + 8], b[j + 8 + 1], block[1][i], block[0][i], c[0], c[1], c[width], c[width + 1]);
}
}
return true;
}
virtual int decode(Sound::Frame *frames, int count) {
if (!audioDecoder) return 0;
int oldPos = stream->pos;
Sound::XA *xa = (Sound::XA*)audioDecoder;
int i = 0;
while (i < count) {
if (xa->pos >= COUNT(xa->buffer)) {
if (aFrames[curAudioFrame].size == 0)
curAudioFrame = (curAudioFrame + 1) % AUDIO_MAX_FRAMES;
if (aFrames[curAudioFrame].size == 0) {
stream->setPos(oldPos);
nextFrame();
oldPos = stream->pos;
curAudioFrame = aFrameIndex;
}
}
stream->setPos(aFrames[curAudioFrame].pos);
i += audioDecoder->decode(&frames[i], count - i);
if (xa->pos >= COUNT(xa->buffer))
aFrames[curAudioFrame].size = 0;
}
stream->setPos(oldPos);
return count;
}
};
Decoder *decoder;
Texture *frameTex[2];
uint8 *frameData;
Color32 *frameData;
float time, step;
bool isPlaying;
bool needUpdate;
@@ -577,9 +1132,17 @@ struct Video {
if (!stream) return;
decoder = new Escape(stream);
frameData = new uint8[decoder->width * decoder->height * 4];
memset(frameData, 0, decoder->width * decoder->height * 4);
uint32 magic;
stream->read(magic);
stream->seek(-4);
if (magic == 0x6F4D5241)
decoder = new Escape(stream);
else
decoder = new STR(stream);
frameData = new Color32[decoder->width * decoder->height];
memset(frameData, 0, decoder->width * decoder->height * sizeof(Color32));
for (int i = 0; i < 2; i++)
frameTex[i] = new Texture(decoder->width, decoder->height, FMT_RGBA, 0, frameData);
@@ -607,8 +1170,14 @@ struct Video {
if (time < step)
return;
time -= step;
isPlaying = needUpdate = decoder->decode(frameData);
#ifdef VIDEO_TEST
int t = Core::getTime();
while (decoder->decodeVideo(frameData)) {}
LOG("time: %d\n", Core::getTime() - t);
isPlaying = false;
#else
isPlaying = needUpdate = decoder->decodeVideo(frameData);
#endif
}
void render() { // just update GPU texture if it's necessary