1
0
mirror of https://github.com/XProger/OpenLara.git synced 2025-08-19 03:11:22 +02:00

#138 gameflow for sega saturn version, #129 CPK audio decoder

This commit is contained in:
XProger
2018-11-13 06:06:55 +03:00
parent cec3af9080
commit 710bf99290
4 changed files with 225 additions and 42 deletions

View File

@@ -2499,16 +2499,6 @@ namespace TR {
} inv;
} extra;
struct TPAL {
uint8 data[3];
};
uint32 tpalCount;
TPAL *tpal;
int32 spalCount;
uint16 *spal;
uint32 tsubCount;
uint8 *tsub;
@@ -2971,8 +2961,6 @@ namespace TR {
delete[] soundOffsets;
delete[] soundSize;
delete[] spal;
delete[] tpal;
delete[] tsub;
}
@@ -3032,17 +3020,12 @@ namespace TR {
break;
case CHUNK("ROOMTPAL") : {
ASSERTV(stream.readBE32() == 0x00000003);
tpalCount = stream.readBE32();
tpal = new TPAL[tpalCount];
stream.raw(tpal, sizeof(TPAL) * tpalCount);
stream.seek(stream.readBE32() * 3);
break;
}
case CHUNK("ROOMSPAL") : {
ASSERTV(stream.readLE32() == 0x02000000);
spalCount = stream.readBE32();
spal = new uint16[spalCount];
for (int i = 0; i < spalCount; i++)
spal[i] = stream.readBE16();
stream.seek(stream.readBE32() * 2);
break;
}
case CHUNK("ROOMDATA") :
@@ -3411,8 +3394,6 @@ namespace TR {
case CHUNK("ROOMEND ") :
ASSERTV(stream.readBE32() == 0x00000000);
ASSERTV(stream.readBE32() == 0x00000000);
LOG("tpalCount = %d\n", tpalCount);
LOG("spalCount = %d\n", spalCount);
prepare();
break;
// SAD

View File

@@ -290,8 +290,8 @@ namespace TR {
// TITLE
case 585648 : // PSX JAP
case 508614 : version = VER_TR1_PSX;
//case 320412 : // PC JAP
case 5148 : // SAT
case 320412 : // PC JAP
case 334874 :
case 316138 :
case 316460 : return LVL_TR1_TITLE;
@@ -305,7 +305,7 @@ namespace TR {
// LEVEL1
case 1667568 : // PSX JAP
case 1448896 : version = VER_TR1_PSX;
case 497656 :
case 497656 : // SAT
case 2540906 : // PC JAP
case 2533312 :
case 2533634 : return LVL_TR1_1;
@@ -313,96 +313,114 @@ namespace TR {
case 2873406 : isDemoLevel = true; return LVL_TR1_2;
case 1766352 : // PSX JAP
case 1535734 : version = VER_TR1_PSX;
case 532250 : // SAT
case 2880722 : // PC JAP
case 2873128 :
case 2873450 : return LVL_TR1_2;
// LEVEL3A
case 1876896 : // PSX JAP
case 1630560 : version = VER_TR1_PSX;
case 547782 : // SAT
case 2942002 : // PC JAP
case 2934408 :
case 2934730 : return LVL_TR1_3A;
// LEVEL3B
case 1510414 : // PSX JAP
case 1506614 : version = VER_TR1_PSX;
case 310960 : // SAT
case 2745530 : // PC JAP
case 2737936 :
case 2738258 : return LVL_TR1_3B;
// CUT1
case 722402 : version = VER_TR1_PSX;
case 142116 : // SAT
case 599840 : return LVL_TR1_CUT_1;
// LEVEL4
case 1624130 : // PSX JAP
case 1621970 : version = VER_TR1_PSX;
case 440612 : // SAT
case 3038144 : // PC JAP
case 3030550 :
case 3030872 : return LVL_TR1_4;
// LEVEL5
case 1588102 : // PSX JAP
case 1585942 : version = VER_TR1_PSX;
case 389996 : // SAT
case 2725812 : // PC JAP
case 2718218 :
case 2718540 : return LVL_TR1_5;
// LEVEL6
case 1710624 : // PSX JAP
case 1708464 : version = VER_TR1_PSX;
case 573506 : // SAT
case 3147184 : // PC JAP
case 3139590 :
case 3074376 : return LVL_TR1_6;
// LEVEL7A
case 1698824 : // PSX JAP
case 1696664 : version = VER_TR1_PSX;
case 581416 : // SAT
case 2824884 : // PC JAP
case 2817290 :
case 2817612 : return LVL_TR1_7A;
// LEVEL7B
case 1735434 : // PSX JAP
case 1733274 : version = VER_TR1_PSX;
case 596416 : // SAT
case 3603912 : // PC JAP
case 3388774 :
case 3389096 : return LVL_TR1_7B;
// CUT2
case 542960 : version = VER_TR1_PSX;
case 70860 : // SAT
case 354320 : return LVL_TR1_CUT_2;
// LEVEL8A
case 1565494 : // PSX JAP
case 1563356 : version = VER_TR1_PSX;
case 592188 : // SAT
case 2887836 : // PC JAP
case 2880242 :
case 2880564 : return LVL_TR1_8A;
// LEVEL8B
case 1567790 : // PSX JAP
case 1565630 : version = VER_TR1_PSX;
case 599928 : // SAT
case 2894028 : // PC JAP
case 2886434 :
case 2886756 : return LVL_TR1_8B;
// LEVEL8C
case 1621520 : // PSX JAP
case 1619360 : version = VER_TR1_PSX;
case 536950 : // SAT
case 3072066 : // PC JAP
case 3105128 :
case 3105450 : return LVL_TR1_8C;
// LEVEL10A
case 1680146 : // PSX JAP
case 1678018 : version = VER_TR1_PSX;
case 569856 : // SAT
case 3270372 : // PC JAP
case 3223816 :
case 3224138 : return LVL_TR1_10A;
// CUT3
case 636660 : version = VER_TR1_PSX;
case 210134 : // SAT
case 512104 : return LVL_TR1_CUT_3;
// LEVEL10B
case 1688908 : // PSX JAP
case 1686748 : version = VER_TR1_PSX;
case 525646 : // SAT
case 3101614 : // PC JAP
case 3094342 :
case 3094020 : return LVL_TR1_10B;
// CUT4
case 940398 : version = VER_TR1_PSX;
case 167188 : // SAT
case 879582 : return LVL_TR1_CUT_4;
// LEVEL10C
case 1816438 : // PSX JAP
case 1814278 : version = VER_TR1_PSX;
case 418170 : // SAT
case 3533814 : // PC JAP
case 3531702 :
case 3532024 : return LVL_TR1_10C;
@@ -1095,6 +1113,7 @@ namespace TR {
if (version & VER_TR1) {
CHECK_FILE("FMV/CORELOGO.FMV");
CHECK_FILE("FMV/CORE.RPL");
CHECK_FILE("FMV/CORELOGO.CPK");
CHECK_FILE("video/1/CORELOGO.FMV");
CHECK_FILE("video/1/CORE.RPL");
}
@@ -1119,59 +1138,77 @@ namespace TR {
const char* getGameVideo(LevelID id) {
switch (id) {
// TR1
case LVL_TR1_TITLE :
case LVL_TR1_TITLE :
CHECK_FILE("FMV/CAFE.FMV");
CHECK_FILE("FMV/CAFE.RPL");
CHECK_FILE("FMV/CAFE.CPK");
CHECK_FILE("video/1/CAFE.FMV");
CHECK_FILE("video/1/CAFE.RPL");
CHECK_FILE("video/1/CAFE.CPK");
break;
case LVL_TR1_GYM :
case LVL_TR1_GYM :
CHECK_FILE("FMV/MANSION.FMV");
CHECK_FILE("FMV/MANSION.RPL");
CHECK_FILE("FMV/MANSION.CPK");
CHECK_FILE("video/1/MANSION.FMV");
CHECK_FILE("video/1/MANSION.RPL");
CHECK_FILE("video/1/MANSION.CPK");
break;
case LVL_TR1_1 :
case LVL_TR1_1 :
CHECK_FILE("FMV/SNOW.FMV");
CHECK_FILE("FMV/SNOW.RPL");
CHECK_FILE("FMV/SNOW.CPK");
CHECK_FILE("video/1/SNOW.FMV");
CHECK_FILE("video/1/SNOW.RPL");
CHECK_FILE("video/1/SNOW.CPK");
break;
case LVL_TR1_4 :
case LVL_TR1_4 :
CHECK_FILE("FMV/LIFT.FMV");
CHECK_FILE("FMV/LIFT.RPL");
CHECK_FILE("FMV/LIFT.CPK");
CHECK_FILE("video/1/LIFT.FMV");
CHECK_FILE("video/1/LIFT.RPL");
CHECK_FILE("video/1/LIFT.CPK");
break;
case LVL_TR1_8A :
case LVL_TR1_8A :
CHECK_FILE("FMV/VISION.FMV");
CHECK_FILE("FMV/VISION.RPL");
CHECK_FILE("FMV/VISION.CPK");
CHECK_FILE("video/1/VISION.FMV");
CHECK_FILE("video/1/VISION.RPL");
CHECK_FILE("video/1/VISION.CPK");
break;
case LVL_TR1_10A :
case LVL_TR1_10A :
CHECK_FILE("FMV/CANYON.FMV");
CHECK_FILE("FMV/CANYON.RPL");
CHECK_FILE("FMV/CANYON.CPK");
CHECK_FILE("video/1/CANYON.FMV");
CHECK_FILE("video/1/CANYON.RPL");
CHECK_FILE("video/1/CANYON.CPK");
break;
case LVL_TR1_10B :
case LVL_TR1_10B :
CHECK_FILE("FMV/PYRAMID.FMV");
CHECK_FILE("FMV/PYRAMID.RPL");
CHECK_FILE("FMV/PYRAMID.CPK");
CHECK_FILE("video/1/PYRAMID.FMV");
CHECK_FILE("video/1/PYRAMID.RPL");
CHECK_FILE("video/1/PYRAMID.CPK");
break;
case LVL_TR1_CUT_4 :
case LVL_TR1_CUT_4 :
CHECK_FILE("FMV/PRISON.FMV");
CHECK_FILE("FMV/PRISON.RPL");
CHECK_FILE("FMV/PRISON.CPK");
CHECK_FILE("video/1/PRISON.FMV");
CHECK_FILE("video/1/PRISON.RPL");
CHECK_FILE("video/1/PRISON.CPK");
break;
case LVL_TR1_EGYPT :
case LVL_TR1_EGYPT :
CHECK_FILE("FMV/END.FMV");
CHECK_FILE("FMV/END.RPL");
CHECK_FILE("FMV/END.CPK");
CHECK_FILE("video/1/END.FMV");
CHECK_FILE("video/1/END.RPL");
CHECK_FILE("video/1/END.CPK");
break;
// TR2
case LVL_TR2_TITLE :

View File

@@ -1650,17 +1650,21 @@ struct Array {
clear();
}
void reserve(int capacity) {
this->capacity = capacity;
if (items)
items = (T*)realloc(items, capacity * sizeof(T));
else
items = (T*)malloc(capacity * sizeof(T));
}
int push(const T &item) {
if (!items)
items = (T*)malloc(capacity * sizeof(T));
if (length == capacity) {
capacity += capacity + capacity / 2;
if (items)
items = (T*)realloc(items, capacity * sizeof(T));
else
items = (T*)malloc(capacity * sizeof(T));
}
if (length == capacity)
reserve(capacity + capacity / 2);
items[length] = item;
return length++;
}
@@ -1681,6 +1685,12 @@ struct Array {
items[i] = items[i + 1];
}
void resize(int length) {
if (capacity < length)
reserve(length);
this->length = length;
}
void clear() {
length = 0;
free(items);

View File

@@ -1131,6 +1131,156 @@ struct Video {
}
};
// based on https://wiki.multimedia.cx/index.php/Sega_FILM
struct Cinepak : Decoder {
struct Chunk {
int offset;
int size;
uint32 info[2];
} *chunks;
int chunksCount;
int audioChunkIndex;
int audioChunkPos;
Array<Sound::Frame> audioChunkFrames;
int videoChunkIndex;
int videoChunkPos;
Array<uint8> videoChunkData;
Cinepak(Stream *stream) : Decoder(stream), chunks(NULL), audioChunkIndex(-1), audioChunkPos(0), videoChunkIndex(-1), videoChunkPos(0) {
ASSERTV(stream->readLE32() == FOURCC("FILM"));
int sampleOffset = stream->readBE32();
stream->seek(4); // skip version 1.06
stream->seek(4); // skip reserved
ASSERTV(stream->readLE32() == FOURCC("FDSC"));
ASSERTV(stream->readBE32() == 32);
ASSERTV(stream->readLE32() == FOURCC("cvid"));
height = stream->readBE32();
width = stream->readBE32();
ASSERTV(stream->read() == 24);
channels = stream->read();
ASSERT(channels == 2);
ASSERTV(stream->read() == 16);
ASSERTV(stream->read() == 0);
freq = stream->readBE16();
ASSERT(freq == 22254);
stream->seek(6);
ASSERTV(stream->readLE32() == FOURCC("STAB"));
stream->seek(4); // skip STAB length
fps = stream->readBE32() / 2;
chunksCount = stream->readBE32();
chunks = new Chunk[chunksCount];
for (int i = 0; i < chunksCount; i++) {
Chunk &c = chunks[i];
c.offset = stream->readBE32() + sampleOffset;
c.size = stream->readBE32();
c.info[0] = stream->readBE32();
c.info[1] = stream->readBE32();
}
}
virtual ~Cinepak() {
delete[] chunks;
}
virtual bool decodeVideo(Color32 *pixels) {
if (audioChunkIndex >= chunksCount)
return false;
/*
// TODO: sega cinepak film decoder
// get next audio chunk
if (videoChunkPos >= videoChunkData.length) {
videoChunkPos = 0;
while (++videoChunkIndex < chunksCount) {
if (chunks[videoChunkIndex].info[0] != 0xFFFFFFFF || chunks[videoChunkIndex].info[1] != 1)
break;
}
if (videoChunkIndex >= chunksCount)
return true;
const Chunk &chunk = chunks[videoChunkIndex];
{
OS_LOCK(Sound::lock);
stream->setPos(chunk.offset);
videoChunkData.resize(chunk.size);
stream->raw(videoChunkData.items, videoChunkData.length);
}
}
// TODO: decode
Stream data(NULL, videoChunkData.items + videoChunkPos, videoChunkData.length - videoChunkPos);
union FrameHeader {
struct { uint32 flags:8, size:24; };
uint32 value;
} hdr;
hdr.value = data.readBE32();
ASSERT(hdr.size <= videoChunkData.length - videoChunkPos);
videoChunkPos += hdr.size;
*/
for (int y = 0; y < height; y++)
for (int x = 0; x < width; x++) {
Color32 c;
c.r = c.g = c.b = x ^ y;
c.a = 255;
pixels[y * width + x] = c;
}
return true;
}
virtual int decode(Sound::Frame *frames, int count) {
if (audioChunkIndex >= chunksCount) {
memset(frames, 0, count);
return count;
}
// get next audio chunk
if (audioChunkPos >= audioChunkFrames.length) {
audioChunkPos = 0;
while (++audioChunkIndex < chunksCount) {
if (chunks[audioChunkIndex].info[0] == 0xFFFFFFFF)
break;
}
if (audioChunkIndex >= chunksCount) {
memset(frames, 0, count);
return count;
}
const Chunk &chunk = chunks[audioChunkIndex];
audioChunkFrames.resize(chunk.size / sizeof(Sound::Frame));
stream->setPos(chunk.offset);
// read LEFT channel samples
for (int i = 0; i < audioChunkFrames.length; i++)
audioChunkFrames[i].L = stream->readBE16();
// read RIGHT channel samples
for (int i = 0; i < audioChunkFrames.length; i++)
audioChunkFrames[i].R = stream->readBE16();
}
for (int i = 0; i < count; i += 2) {
frames[i + 0] = audioChunkFrames[audioChunkPos];
frames[i + 1] = audioChunkFrames[audioChunkPos++];
if (audioChunkPos >= audioChunkFrames.length)
return i + 2;
}
return count;
}
};
Decoder *decoder;
Texture *frameTex[2];
Color32 *frameData;
@@ -1144,11 +1294,15 @@ struct Video {
if (!stream) return;
uint32 magic;
stream->read(magic);
uint32 magic = stream->readLE32();
stream->seek(-4);
if (magic == 0x6F4D5241)
float pitch = 1.0f;
if (magic == FOURCC("FILM")) {
decoder = new Cinepak(stream);
pitch = decoder->freq / 22050.0f; // 22254 / 22050 = 1.00925
} else if (magic == FOURCC("ARMo"))
decoder = new Escape(stream);
else
decoder = new STR(stream);
@@ -1160,6 +1314,7 @@ struct Video {
frameTex[i] = new Texture(decoder->width, decoder->height, FMT_RGBA, 0, frameData);
sample = Sound::play(decoder);
sample->pitch = pitch;
step = 1.0f / decoder->fps;
stepTimer = step;