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

#129 TR2 & TR3 FMVs support (Escape-130)

This commit is contained in:
XProger
2018-07-06 05:01:29 +03:00
parent 20503efa6e
commit 21b559500d
3 changed files with 245 additions and 61 deletions

View File

@@ -561,9 +561,10 @@ namespace TR {
if (id == LVL_TR2_VENICE || id == LVL_TR2_CUT_2 || id == LVL_TR2_PLATFORM || id == LVL_TR2_CUT_3 || id == LVL_TR2_UNWATER ||
id == LVL_TR2_KEEL || id == LVL_TR2_LIVING || id == LVL_TR2_DECK || id == LVL_TR2_CATACOMB || id == LVL_TR2_ICECAVE ||
id == LVL_TR2_CUT_4 || id == LVL_TR2_XIAN || id == LVL_TR2_HOUSE) {
strcpy(dst, LEVEL_INFO[id].name);
String::toLower(dst);
sprintf(dst, "DATA/%s.TR2", dst);
char buf[64];
strcpy(buf, LEVEL_INFO[id].name);
String::toLower(buf);
sprintf(dst, "DATA/%s.TR2", buf);
} else if (id == LVL_TR2_TITLE) {
sprintf(dst, "DATA/%s.tr2", LEVEL_INFO[id].name);
} else if (id == LVL_TR2_EMPRTOMB) {
@@ -912,6 +913,18 @@ namespace TR {
case LVL_TR2_HOUSE :
CHECK_FILE("fmv/END.RPL");
return "video/2/END.RPL";
case LVL_TR3_TITLE :
CHECK_FILE("fmv/Intr_Eng.rpl");
return "video/3/Intr_Eng.rpl";
case LVL_TR3_SHORE :
CHECK_FILE("fmv/Sail_Eng.rpl");
return "video/3/Sail_Eng.rpl";
case LVL_TR3_ANTARC :
CHECK_FILE("fmv/Crsh_Eng.rpl");
return "video/3/Crsh_Eng.rpl";
case LVL_TR3_STPAUL :
CHECK_FILE("fmv/Endgame.rpl");
return "video/3/Endgame.rpl";
// TR3
default : return NULL;
}

View File

@@ -314,33 +314,30 @@ namespace Sound {
int16 getSample(uint8 n, State &state) {
static int indexLUT[] = {
-1, -1, -1, -1, 2, 4, 6, 8
-1, -1, -1, -1, 2, 4, 6, 8,
};
static int stepLUT[] = {
7, 8, 9, 10, 11, 12, 13, 14,
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,
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,
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,
7132, 7845, 8630, 9493, 10442, 11487, 12635, 13899,
15289, 16818, 18500, 20350, 22385, 24623, 27086, 29794,
32767
};
int step = stepLUT[state.idx];
int idx = n & 7;
state.idx += indexLUT[n & 7];
state.idx = clamp(state.idx, 0, 88);
state.idx = clamp(state.idx + indexLUT[idx], 0, 88);
int diff = step >> 3;
if (n & 1) diff += step >> 2;
if (n & 2) diff += step >> 1;
if (n & 4) diff += step;
int diff = (2 * idx + 1) * step >> 3;
if (n & 8) {
state.amp -= diff;
@@ -359,8 +356,8 @@ namespace Sound {
uint8 n;
stream->read(n);
int a = getSample(n & 0x0F, state[0]);
int b = getSample(n >> 4, state[1 % channels]);
int a = getSample(n >> 4, state[0]);
int b = getSample(n & 0x0F, state[1 % channels]);
Frame frame;
if (channels == 2) {
@@ -600,7 +597,7 @@ namespace Sound {
} else if (type == FOURCC("data")) {
if (waveFmt.format == 1) decoder = new PCM(stream, waveFmt.channels, waveFmt.samplesPerSec, size, waveFmt.sampleBits);
#ifdef DECODE_ADPCM
if (waveFmt.format == 2) decoder = new ADPCM(stream, waveFmt.channels, size, waveFmt.block, waveFmt.samplesPerSec);
if (waveFmt.format == 2) decoder = new ADPCM(stream, waveFmt.channels, waveFmt.samplesPerSec, size, waveFmt.block);
#endif
break;
} else

View File

@@ -7,6 +7,35 @@
struct Video {
union Color32 {
uint32 value;
struct { uint8 r, g, b, a; };
void SetRGB15(uint16 v) {
r = (v & 0x7C00) >> 7;
g = (v & 0x03E0) >> 2;
b = (v & 0x001F) << 3;
a = 255;
}
void SetYCbCr(int32 Y, int32 Cb, int32 Cr) {
Y = max(0, 1191 * (Y - 16));
Cb -= 128;
Cr -= 128;
r = clamp((Y + 1836 * Cr) >> 10, 0, 255);
g = clamp((Y - 547 * Cr - 218 * Cb) >> 10, 0, 255);
b = clamp((Y + 2165 * Cb) >> 10, 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;
}
};
struct Decoder : Sound::Decoder {
int width, height, fps;
@@ -25,7 +54,7 @@ struct Video {
Sound::Decoder *audioDecoder;
uint32 *prevFrame, *nextFrame;
uint8 *prevFrame, *nextFrame, *lumaFrame;
struct Chunk {
int offset;
@@ -47,7 +76,7 @@ struct Video {
MacroBlock *blocks;
} codebook[3];
Escape(Stream *stream) : Decoder(stream), audioDecoder(NULL), chunks(NULL) {
Escape(Stream *stream) : Decoder(stream), audioDecoder(NULL), prevFrame(NULL), nextFrame(NULL), lumaFrame(NULL), chunks(NULL) {
for (int i = 0; i < 4; i++)
skipLine();
@@ -78,10 +107,24 @@ struct Video {
chunks[i].audioSize = readValue();
}
prevFrame = new uint32[width * height];
nextFrame = new uint32[width * height];
memset(prevFrame, 0, width * height * sizeof(uint32));
memset(nextFrame, 0, width * height * sizeof(uint32));
switch (vfmt) {
case 124 :
prevFrame = new uint8[width * height * 4];
nextFrame = new uint8[width * height * 4];
memset(prevFrame, 0, width * height * sizeof(uint32));
memset(nextFrame, 0, width * height * sizeof(uint32));
break;
case 130 :
prevFrame = new uint8[width * height * 3 / 2];
nextFrame = new uint8[width * height * 3 / 2];
lumaFrame = new uint8[width * height / 4];
memset(prevFrame, 0, width * height);
memset(prevFrame + width * height, 16, width * height / 2);
break;
default :
LOG("! unsupported Escape codec version (%d)\n", vfmt);
ASSERT(false);
}
codebook[0].blocks =
codebook[1].blocks =
@@ -110,6 +153,7 @@ struct Video {
delete[] codebook[2].blocks;
delete[] prevFrame;
delete[] nextFrame;
delete[] lumaFrame;
}
void skipLine() {
@@ -133,6 +177,28 @@ struct Video {
return 0;
}
int getSkip124(BitStream &bs) {
int value;
if ((value = bs.read(1)) != 1 ||
(value += bs.read(3)) != 8 ||
(value += bs.read(7)) != 135)
return value;
return value + bs.read(12);
}
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;
return -1;
}
void copySuperBlock(uint32 *dst, int dstWidth, uint32 *src, int srcWidth) {
for (int i = 0; i < 8; i++) {
memcpy(dst, src, 8 * sizeof(uint32));
@@ -141,24 +207,6 @@ struct Video {
}
}
int getSkipCount(BitStream &bs) {
int value;
value = bs.read(1);
if (!value)
return value;
value += bs.read(3);
if (value != (1 + ((1 << 3) - 1)))
return value;
value += bs.read(7);
if (value != (1 + ((1 << 3) - 1)) + ((1 << 7) - 1))
return value;
return value + bs.read(12);
}
void decodeMacroBlock(BitStream &bs, MacroBlock &mb, int &cbIndex, int sbIndex) {
int value = bs.read(1);
if (value) {
@@ -184,20 +232,6 @@ struct Video {
dst[9] = mb.pixels[3];
}
union Color32 {
uint32 value;
struct { uint8 r, g, b, a; };
Color32() {}
Color32(uint16 v) {
r = (v & 0x7C00) >> 7;
g = (v & 0x03E0) >> 2;
b = (v & 0x001F) << 3;
a = 255;
}
};
bool decode(uint8 *frame) {
if (curVideoChunk >= chunksCount)
return false;
@@ -258,8 +292,9 @@ struct Video {
for (uint32 j = 0; j < cb.size; j++) {
uint8 mask = bs.read(4);
Color32 cA = Color32(uint16(bs.read(15)));
Color32 cB = Color32(uint16(bs.read(15)));
Color32 cA, cB;
cA.SetRGB15(bs.read(15));
cB.SetRGB15(bs.read(15));
if (cA.value != cB.value && (mask == 6 || mask == 9) && // check for 0101 or 1010 mask
abs(int(cA.r) - int(cB.r)) <= 8 &&
@@ -292,13 +327,13 @@ struct Video {
for (int sbIndex = 0; sbIndex < sbCount; sbIndex++) {
int sbLine = width / 8;
int sbOffset = ((sbIndex / sbLine) * width + (sbIndex % sbLine)) * 8;
uint32 *src = prevFrame + sbOffset;
uint32 *dst = nextFrame + sbOffset;
uint32 *src = (uint32*)prevFrame + sbOffset;
uint32 *dst = (uint32*)nextFrame + sbOffset;
uint16 multiMask = 0;
if (skip == -1)
skip = getSkipCount(bs);
skip = getSkip124(bs);
if (skip) {
copySuperBlock(dst, width, src, width);
@@ -345,13 +380,152 @@ struct Video {
}
bool decode130(uint8 *frame) {
static const uint8 offsetLUT[] = {
2, 4, 10, 20
};
static const int8 signLUT[64][4] = {
{ 0, 0, 0, 0 }, { -1, 1, 0, 0 }, { 1, -1, 0, 0 }, { -1, 0, 1, 0 },
{ -1, 1, 1, 0 }, { 0, -1, 1, 0 }, { 1, -1, 1, 0 }, { -1, -1, 1, 0 },
{ 1, 0, -1, 0 }, { 0, 1, -1, 0 }, { 1, 1, -1, 0 }, { -1, 1, -1, 0 },
{ 1, -1, -1, 0 }, { -1, 0, 0, 1 }, { -1, 1, 0, 1 }, { 0, -1, 0, 1 },
{ 0, 0, 0, 0 }, { 1, -1, 0, 1 }, { -1, -1, 0, 1 }, { -1, 0, 1, 1 },
{ -1, 1, 1, 1 }, { 0, -1, 1, 1 }, { 1, -1, 1, 1 }, { -1, -1, 1, 1 },
{ 0, 0, -1, 1 }, { 1, 0, -1, 1 }, { -1, 0, -1, 1 }, { 0, 1, -1, 1 },
{ 1, 1, -1, 1 }, { -1, 1, -1, 1 }, { 0, -1, -1, 1 }, { 1, -1, -1, 1 },
{ 0, 0, 0, 0 }, { -1, -1, -1, 1 }, { 1, 0, 0, -1 }, { 0, 1, 0, -1 },
{ 1, 1, 0, -1 }, { -1, 1, 0, -1 }, { 1, -1, 0, -1 }, { 0, 0, 1, -1 },
{ 1, 0, 1, -1 }, { -1, 0, 1, -1 }, { 0, 1, 1, -1 }, { 1, 1, 1, -1 },
{ -1, 1, 1, -1 }, { 0, -1, 1, -1 }, { 1, -1, 1, -1 }, { -1, -1, 1, -1 },
{ 0, 0, 0, 0 }, { 1, 0, -1, -1 }, { 0, 1, -1, -1 }, { 1, 1, -1, -1 },
{ -1, 1, -1, -1 }, { 1, -1, -1, -1 }, { 0, 0, 0, 0 }, { 0, 0, 0, 0 },
{ 0, 0, 0, 0 }, { 0, 0, 0, 0 }, { 0, 0, 0, 0 }, { 0, 0, 0, 0 },
{ 0, 0, 0, 0 }, { 0, 0, 0, 0 }, { 0, 0, 0, 0 }, { 0, 0, 0, 0 },
};
static const int8 lumaLUT[] = {
-4, -3, -2, -1, 1, 2, 3, 4
};
static const int8 chromaLUT[2][8] = {
{ 1, 1, 0, -1, -1, -1, 0, 1 },
{ 0, 1, 1, 1, 0, -1, -1, -1 }
};
static const uint8 chromaValueLUT[] = {
20, 28, 36, 44, 52, 60, 68, 76,
84, 92, 100, 106, 112, 116, 120, 124,
128, 132, 136, 140, 144, 150, 156, 164,
172, 180, 188, 196, 204, 212, 220, 228
};
Chunk &chunk = chunks[curVideoChunk++];
uint8 *data = new uint8[chunk.videoSize];
stream->raw(data, chunk.videoSize);
BitStream bs(data, chunk.videoSize);
bs.data += 16; // skip 16 bytes
uint8 *lumaPtr = lumaFrame;
int skip = -1;
int bCount = width * height / 4;
uint32 luma = 0, Y[4] = { 0 }, U = 16, V = 16;
uint8 *oY = prevFrame, *oU = oY + width * height, *oV = oU + width * height / 4;
uint8 *nY = nextFrame, *nU = nY + width * height, *nV = nU + width * height / 4;
for (int bIndex = 0; bIndex < bCount; bIndex++) {
if (skip == -1)
skip = getSkip130(bs);
if (skip) {
Y[0] = oY[0];
Y[1] = oY[1];
Y[2] = oY[width];
Y[3] = oY[width + 1];
U = oU[0];
V = oV[0];
luma = *lumaPtr;
} else {
if (bs.read(1)) {
uint32 sign = bs.read(6);
uint32 diff = bs.read(2);
luma = bs.read(5) * 2;
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);
for (int i = 0; i < 4; i++)
Y[i] = luma;
}
if (bs.read(1)) {
if (bs.read(1)) {
U = bs.read(5);
V = bs.read(5);
} else {
uint32 idx = bs.read(3);
U = (U + chromaLUT[0][idx]) & 31;
V = (V + chromaLUT[1][idx]) & 31;
}
}
}
*lumaPtr++ = luma;
nY[0] = Y[0];
nY[1] = Y[1];
nY[width] = Y[2];
nY[width + 1] = Y[3];
nU[0] = U;
nV[0] = V;
nY += 2; nU++; nV++;
oY += 2; oU++; oV++;
if (!(((bIndex + 1) * 2) % width)) {
nY += width;
oY += width;
}
skip--;
}
delete[] data;
nY = nextFrame;
nU = nY + width * height;
nV = nU + width * height / 4;
Color32 *p = (Color32*)frame;
for (int y = 0; y < height; y++) {
for (int x = 0; x < width; x++) {
int Y = nY[y * width + x] << 2;
int U = chromaValueLUT[nU[x / 2]] - 128;
int V = chromaValueLUT[nV[x / 2]] - 128;
(p++)->SetYUV(Y, U, V);
}
if (y & 1) {
nU += width / 2;
nV += width / 2;
}
}
swap(prevFrame, nextFrame);
return true;
}
virtual int decode(Sound::Frame *frames, int count) {
if (!audioDecoder) return 0;
if (abs(curAudioChunk - curVideoChunk) > 1) { // sync with video chunk
if (bps != 4 && abs(curAudioChunk - curVideoChunk) > 1) { // sync with video chunk, doesn't work for IMA
curAudioChunk = curVideoChunk;
curAudioPos = 0;
}