mirror of
https://github.com/XProger/OpenLara.git
synced 2025-08-19 03:11:22 +02:00
23
src/format.h
23
src/format.h
@@ -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
|
||||
|
@@ -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 :
|
||||
|
24
src/utils.h
24
src/utils.h
@@ -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);
|
||||
|
161
src/video.h
161
src/video.h
@@ -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;
|
||||
|
Reference in New Issue
Block a user