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:
@@ -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;
|
||||
}
|
||||
|
25
src/sound.h
25
src/sound.h
@@ -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
|
||||
|
262
src/video.h
262
src/video.h
@@ -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;
|
||||
}
|
||||
|
Reference in New Issue
Block a user