mirror of
https://github.com/XProger/OpenLara.git
synced 2025-08-01 02:40:43 +02:00
#15 add PNG support for Android obb's title and loading screens, add mp3 audio support
This commit is contained in:
BIN
bin/OpenLara.exe
BIN
bin/OpenLara.exe
Binary file not shown.
@@ -1 +1 @@
|
|||||||
- track_XX.ogg, track_XX.ogg, track_XX.wav for *_EN, *_DE, *_FR, *_IT, *_JA, *_RU
|
- track_XX.ogg, track_XX.mp3, track_XX.wav for *_EN, *_DE, *_FR, *_IT, *_JA, *_RU
|
@@ -1,2 +1,2 @@
|
|||||||
- track_XX.ogg, track_XX.ogg, track_XX.wav for *_EN, *_DE, *_FR, *_IT, *_JA, *_RU
|
- track_XX.ogg, track_XX.mp3, track_XX.wav for *_EN, *_DE, *_FR, *_IT, *_JA, *_RU
|
||||||
- MAIN.SFX
|
- MAIN.SFX
|
12
src/core.h
12
src/core.h
@@ -1,6 +1,10 @@
|
|||||||
#ifndef H_CORE
|
#ifndef H_CORE
|
||||||
#define H_CORE
|
#define H_CORE
|
||||||
|
|
||||||
|
#ifndef __EMSCRIPTEN__
|
||||||
|
#define USE_INFLATE
|
||||||
|
#endif
|
||||||
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#ifdef WIN32
|
#ifdef WIN32
|
||||||
#include <windows.h>
|
#include <windows.h>
|
||||||
@@ -137,6 +141,10 @@
|
|||||||
#define glProgramBinary(...)
|
#define glProgramBinary(...)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifdef USE_INFLATE
|
||||||
|
#include "libs/tinf/tinf.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
#include "utils.h"
|
#include "utils.h"
|
||||||
|
|
||||||
enum ControlKey { cLeft, cRight, cUp, cDown, cJump, cWalk, cAction, cWeapon, cLook, cStepLeft, cStepRight, cRoll, cInventory, cMAX };
|
enum ControlKey { cLeft, cRight, cUp, cDown, cJump, cWalk, cAction, cWeapon, cLook, cStepLeft, cStepRight, cRoll, cInventory, cMAX };
|
||||||
@@ -540,6 +548,10 @@ namespace Core {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void init() {
|
void init() {
|
||||||
|
#ifdef USE_INFLATE
|
||||||
|
tinf_init();
|
||||||
|
#endif
|
||||||
|
|
||||||
Input::init();
|
Input::init();
|
||||||
#ifdef ANDROID
|
#ifdef ANDROID
|
||||||
void *libGL = dlopen("libGLESv2.so", RTLD_LAZY);
|
void *libGL = dlopen("libGLESv2.so", RTLD_LAZY);
|
||||||
|
@@ -494,7 +494,7 @@ namespace TR {
|
|||||||
if (Stream::existsContent("DATA/GYM.SAT"))
|
if (Stream::existsContent("DATA/GYM.SAT"))
|
||||||
return VER_TR1_SEGA;
|
return VER_TR1_SEGA;
|
||||||
|
|
||||||
if (Stream::existsContent("data/ASSAULT.TR2") || Stream::existsContent("data/assault.TR2"))
|
if (Stream::existsContent("data/ASSAULT.TR2") || Stream::existsContent("assault.TR2"))
|
||||||
return VER_TR2_PC;
|
return VER_TR2_PC;
|
||||||
if (Stream::existsContent("DATA/ASSAULT.PSX"))
|
if (Stream::existsContent("DATA/ASSAULT.PSX"))
|
||||||
return VER_TR2_PSX;
|
return VER_TR2_PSX;
|
||||||
@@ -630,6 +630,7 @@ namespace TR {
|
|||||||
callback(Sound::openCDAudioMP3("audio/cdaudio.dat", "audio/cdaudio.mp3", track), userData);
|
callback(Sound::openCDAudioMP3("audio/cdaudio.dat", "audio/cdaudio.mp3", track), userData);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
sprintf(title, "track_%02d", track);
|
||||||
if (!checkTrack("", title) && !checkTrack("audio/2/", title) && !checkTrack("audio/", title)) {
|
if (!checkTrack("", title) && !checkTrack("audio/2/", title) && !checkTrack("audio/", title)) {
|
||||||
callback(NULL, userData);
|
callback(NULL, userData);
|
||||||
return;
|
return;
|
||||||
@@ -674,6 +675,7 @@ namespace TR {
|
|||||||
switch (id) {
|
switch (id) {
|
||||||
case LVL_TR1_TITLE :
|
case LVL_TR1_TITLE :
|
||||||
if (Stream::existsContent("DATA/TITLEH.PCX")) return "DATA/TITLEH.PCX";
|
if (Stream::existsContent("DATA/TITLEH.PCX")) return "DATA/TITLEH.PCX";
|
||||||
|
if (Stream::existsContent("TITLEH.png")) return "TITLEH.png";
|
||||||
break;
|
break;
|
||||||
default : ;
|
default : ;
|
||||||
}
|
}
|
||||||
@@ -683,6 +685,7 @@ namespace TR {
|
|||||||
case LVL_TR2_TITLE :
|
case LVL_TR2_TITLE :
|
||||||
if (Stream::existsContent("data/TITLE.PCX")) return "data/TITLE.PCX";
|
if (Stream::existsContent("data/TITLE.PCX")) return "data/TITLE.PCX";
|
||||||
if (Stream::existsContent("pix/title.pcx")) return "pix/title.pcx";
|
if (Stream::existsContent("pix/title.pcx")) return "pix/title.pcx";
|
||||||
|
if (Stream::existsContent("TITLE.png")) return "TITLE.png";
|
||||||
break;
|
break;
|
||||||
default : ;
|
default : ;
|
||||||
}
|
}
|
||||||
|
@@ -3,11 +3,10 @@
|
|||||||
|
|
||||||
#define DECODE_VAG
|
#define DECODE_VAG
|
||||||
#define DECODE_ADPCM
|
#define DECODE_ADPCM
|
||||||
//#define DECODE_MP3
|
|
||||||
#define DECODE_OGG
|
#define DECODE_OGG
|
||||||
|
|
||||||
#ifdef __EMSCRIPTEN__ // TODO: http streaming
|
#ifndef __EMSCRIPTEN
|
||||||
#undef DECODE_MP3
|
#define DECODE_MP3
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include "utils.h"
|
#include "utils.h"
|
||||||
@@ -36,7 +35,7 @@ namespace Sound {
|
|||||||
#define MAX_FDN 16
|
#define MAX_FDN 16
|
||||||
#define MAX_DELAY 1024
|
#define MAX_DELAY 1024
|
||||||
|
|
||||||
static const short FDN[MAX_FDN] = {281,331,373,419,461,503,547,593,641,683,727,769,811,853,907,953};
|
static const int16 FDN[MAX_FDN] = { 281, 331, 373, 419, 461, 503, 547, 593, 641, 683, 727, 769, 811, 853, 907, 953 };
|
||||||
|
|
||||||
struct Delay {
|
struct Delay {
|
||||||
int index;
|
int index;
|
||||||
|
217
src/texture.h
217
src/texture.h
@@ -281,11 +281,224 @@ struct Texture {
|
|||||||
return tex;
|
return tex;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef USE_INFLATE
|
||||||
|
static int pngPaeth(int a, int b, int c) {
|
||||||
|
int p = a + b - c;
|
||||||
|
int pa = abs(p - a);
|
||||||
|
int pb = abs(p - b);
|
||||||
|
int pc = abs(p - c);
|
||||||
|
if (pa <= pb && pa <= pc)
|
||||||
|
return a;
|
||||||
|
if (pb <= pc)
|
||||||
|
return b;
|
||||||
|
return c;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void pngFilter(int id, int BPP, int BPL, const uint8 *src, uint8 *dst, const uint8 *prev) {
|
||||||
|
enum { NONE, SUB, UP, AVRG, PAETH };
|
||||||
|
|
||||||
|
switch (id) {
|
||||||
|
case NONE :
|
||||||
|
memcpy(dst, src, BPL);
|
||||||
|
break;
|
||||||
|
case SUB :
|
||||||
|
memcpy(dst, src, BPP);
|
||||||
|
for (int i = BPP; i < BPL; i++)
|
||||||
|
dst[i] = src[i] + dst[i - BPP];
|
||||||
|
break;
|
||||||
|
case UP :
|
||||||
|
for (int i = 0; i < BPL; i++)
|
||||||
|
dst[i] = src[i] + prev[i];
|
||||||
|
break;
|
||||||
|
case AVRG :
|
||||||
|
for (int i = 0; i < BPP; i++)
|
||||||
|
dst[i] = src[i] + (prev[i] >> 1);
|
||||||
|
for (int i = BPP; i < BPL; i++)
|
||||||
|
dst[i] = src[i] + ((dst[i - BPP] + prev[i]) >> 1);
|
||||||
|
break;
|
||||||
|
case PAETH :
|
||||||
|
for (int i = 0; i < BPP; i++)
|
||||||
|
dst[i] = src[i] + pngPaeth(0, prev[i], 0);
|
||||||
|
for (int i = BPP; i < BPL; i++)
|
||||||
|
dst[i] = src[i] + pngPaeth(dst[i - BPP], prev[i], prev[i - BPP]);
|
||||||
|
break;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static Texture* LoadPNG(Stream &stream) {
|
||||||
|
stream.seek(8);
|
||||||
|
|
||||||
|
uint8 bits, colorType, interlace;
|
||||||
|
int BPP, BPL;
|
||||||
|
uint32 width, height;
|
||||||
|
|
||||||
|
uint8 palette[256 * 3];
|
||||||
|
uint8 trans[256];
|
||||||
|
|
||||||
|
uint8 *cdata = NULL;
|
||||||
|
int offset = 0;
|
||||||
|
|
||||||
|
// read chunks
|
||||||
|
while (stream.pos < stream.size) {
|
||||||
|
uint32 chunkSize, chunkName;
|
||||||
|
chunkSize = swap32(stream.read(chunkSize));
|
||||||
|
stream.read(chunkName);
|
||||||
|
if (chunkName == FOURCC("IHDR")) { // Image Header
|
||||||
|
width = swap32(stream.read(width));
|
||||||
|
height = swap32(stream.read(height));
|
||||||
|
stream.read(bits);
|
||||||
|
stream.read(colorType);
|
||||||
|
stream.seek(2);
|
||||||
|
stream.read(interlace);
|
||||||
|
|
||||||
|
ASSERT(interlace == 0);
|
||||||
|
ASSERT(bits <= 8);
|
||||||
|
|
||||||
|
int components;
|
||||||
|
switch (colorType) {
|
||||||
|
case 2 : components = 3; break;
|
||||||
|
case 4 : components = 2; break;
|
||||||
|
case 6 : components = 4; break;
|
||||||
|
default : components = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
BPP = (bits + 7) / 8 * components;
|
||||||
|
BPL = (width * bits + 7) / 8 * components;
|
||||||
|
|
||||||
|
cdata = new uint8[stream.size];
|
||||||
|
memset(trans, 0xFF, sizeof(trans));
|
||||||
|
} else if (chunkName == FOURCC("PLTE")) { // Palette
|
||||||
|
stream.raw(palette, chunkSize);
|
||||||
|
} else if (chunkName == FOURCC("tRNS")) { // Transparency info
|
||||||
|
stream.raw(trans, chunkSize);
|
||||||
|
} else if (chunkName == FOURCC("IDAT")) { // Compressed image data part
|
||||||
|
ASSERT(cdata);
|
||||||
|
stream.raw(cdata + offset, chunkSize);
|
||||||
|
offset += chunkSize;
|
||||||
|
} else if (chunkName == FOURCC("IEND")) {
|
||||||
|
break;
|
||||||
|
} else
|
||||||
|
stream.seek(chunkSize);
|
||||||
|
stream.seek(4); // skip chunk CRC
|
||||||
|
}
|
||||||
|
|
||||||
|
ASSERT(cdata);
|
||||||
|
|
||||||
|
uint8 *buffer = new uint8[(BPL + 1) * height];
|
||||||
|
|
||||||
|
uint32 cbytes;
|
||||||
|
tinf_uncompress(buffer, &cbytes, cdata + 2, 0);
|
||||||
|
delete[] cdata;
|
||||||
|
|
||||||
|
ASSERT(cbytes > 0);
|
||||||
|
|
||||||
|
// apply line filters to image
|
||||||
|
uint8 *data = new uint8[BPL * height];
|
||||||
|
|
||||||
|
uint8 *prev = new uint8[BPL];
|
||||||
|
memset(prev, 0, BPL);
|
||||||
|
for (uint32 i = 0; i < height; i++) {
|
||||||
|
pngFilter(buffer[(BPL + 1) * i], BPP, BPL, &buffer[(BPL + 1) * i + 1], &data[BPL * i], prev);
|
||||||
|
if (i == 0)
|
||||||
|
delete[] prev;
|
||||||
|
prev = &data[BPL * i];
|
||||||
|
}
|
||||||
|
delete[] buffer;
|
||||||
|
|
||||||
|
|
||||||
|
// convert to 32-bit image
|
||||||
|
uint8 *data32 = new uint8[width * height * 4];
|
||||||
|
|
||||||
|
uint8 *src = data;
|
||||||
|
uint8 *dst = data32;
|
||||||
|
|
||||||
|
switch (colorType) {
|
||||||
|
case 0 : // grayscale
|
||||||
|
for (uint32 i = 0; i < width * height; i += 4, dst += 4, src++) {
|
||||||
|
dst[0] =
|
||||||
|
dst[1] =
|
||||||
|
dst[2] = src[0];
|
||||||
|
dst[3] = trans[0];
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 2 : // RGB
|
||||||
|
for (uint32 i = 0; i < width * height; i++, dst += 4, src += 3) {
|
||||||
|
dst[0] = src[0];
|
||||||
|
dst[1] = src[1];
|
||||||
|
dst[2] = src[2];
|
||||||
|
dst[3] = trans[0];
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 3 : // indexed color with alpha
|
||||||
|
for (uint32 j = 0, palWord = 0; j < height; j++) {
|
||||||
|
for (uint32 i = 0, curBit = 8; i < width; i++) {
|
||||||
|
if (curBit > 7) {
|
||||||
|
curBit -= 8;
|
||||||
|
palWord = *src++;
|
||||||
|
if (i < width - 1)
|
||||||
|
palWord |= *src << 8;
|
||||||
|
}
|
||||||
|
uint16 palIdx = (palWord >> (8 - bits - curBit)) & ~(0xFFFF << bits);
|
||||||
|
curBit += bits;
|
||||||
|
|
||||||
|
dst[0] = palette[palIdx * 3 + 0];
|
||||||
|
dst[1] = palette[palIdx * 3 + 1];
|
||||||
|
dst[2] = palette[palIdx * 3 + 2];
|
||||||
|
dst[3] = trans[palIdx];
|
||||||
|
dst += 4;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 4 : // grayscale with alpha
|
||||||
|
for (uint32 i = 0; i < width * height; i++, dst += 4, src += 2) {
|
||||||
|
dst[0] =
|
||||||
|
dst[1] =
|
||||||
|
dst[2] = src[0];
|
||||||
|
dst[3] = src[1];
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 6 : // RGBA
|
||||||
|
memcpy(dst, src, width * height * 4);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// convert to POT size if NPOT isn't supported
|
||||||
|
uint32 dw = Core::support.texNPOT ? width : nextPow2(width);
|
||||||
|
uint32 dh = Core::support.texNPOT ? height : nextPow2(height);
|
||||||
|
if (dw != width || dh != height) {
|
||||||
|
uint8 *dataPOT = new uint8[dw * dh * 4];
|
||||||
|
uint32 *dst = (uint32*)dataPOT;
|
||||||
|
uint32 *src = (uint32*)data32;
|
||||||
|
|
||||||
|
for (uint32 j = 0; j < dh; j++)
|
||||||
|
for (uint32 i = 0; i < dw; i++)
|
||||||
|
*dst++ = (i < width && j < height) ? *src++ : 0xFF000000;
|
||||||
|
|
||||||
|
width = dw;
|
||||||
|
height = dh;
|
||||||
|
delete[] data32;
|
||||||
|
data32 = dataPOT;
|
||||||
|
}
|
||||||
|
|
||||||
|
Texture *tex = new Texture(width, height, Texture::RGBA, false, data32);
|
||||||
|
|
||||||
|
delete[] data32;
|
||||||
|
delete[] data;
|
||||||
|
|
||||||
|
return tex;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
static Texture* Load(Stream &stream) {
|
static Texture* Load(Stream &stream) {
|
||||||
uint16 magic;
|
uint32 magic;
|
||||||
stream.read(magic);
|
stream.read(magic);
|
||||||
stream.seek(-int(sizeof(magic)));
|
stream.seek(-int(sizeof(magic)));
|
||||||
if (magic == 0x4D42)
|
#ifdef USE_INFLATE
|
||||||
|
if (magic == 0x474E5089)
|
||||||
|
return LoadPNG(stream);
|
||||||
|
#endif
|
||||||
|
if ((magic & 0xFFFF) == 0x4D42)
|
||||||
return LoadBMP(stream);
|
return LoadBMP(stream);
|
||||||
return LoadPCX(stream);
|
return LoadPCX(stream);
|
||||||
}
|
}
|
||||||
|
20
src/utils.h
20
src/utils.h
@@ -97,6 +97,14 @@ inline void swap(T &a, T &b) {
|
|||||||
b = tmp;
|
b = tmp;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline uint16 swap16(uint16 x) {
|
||||||
|
return ((x & 0x00FF) << 8) | ((x & 0xFF00) >> 8);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline uint32 swap32(uint32 x) {
|
||||||
|
return ((x & 0x000000FF) << 24) | ((x & 0x0000FF00) << 8) | ((x & 0x00FF0000) >> 8) | ((x & 0xFF000000) >> 24);
|
||||||
|
}
|
||||||
|
|
||||||
float clampAngle(float a) {
|
float clampAngle(float a) {
|
||||||
return a < -PI ? a + PI2 : (a >= PI ? a - PI2 : a);
|
return a < -PI ? a + PI2 : (a >= PI ? a - PI2 : a);
|
||||||
}
|
}
|
||||||
@@ -1010,9 +1018,11 @@ struct Stream {
|
|||||||
char *name;
|
char *name;
|
||||||
int size, pos;
|
int size, pos;
|
||||||
|
|
||||||
Stream(const void *data, int size) : callback(NULL), userData(NULL), f(NULL), data((char*)data), name(NULL), size(size), pos(0) {}
|
enum Endian { LITTLE_ENDIAN, BIG_ENDIAN } endian;
|
||||||
|
|
||||||
Stream(const char *name, Callback *callback = NULL, void *userData = NULL) : callback(callback), userData(userData), data(NULL), name(NULL), size(-1), pos(0) {
|
Stream(const void *data, int size) : callback(NULL), userData(NULL), f(NULL), data((char*)data), name(NULL), size(size), pos(0), endian(LITTLE_ENDIAN) {}
|
||||||
|
|
||||||
|
Stream(const char *name, Callback *callback = NULL, void *userData = NULL) : callback(callback), userData(userData), data(NULL), name(NULL), size(-1), pos(0), endian(LITTLE_ENDIAN) {
|
||||||
if (contentDir[0] && (!cacheDir[0] || !strstr(name, cacheDir))) {
|
if (contentDir[0] && (!cacheDir[0] || !strstr(name, cacheDir))) {
|
||||||
char path[255];
|
char path[255];
|
||||||
path[0] = 0;
|
path[0] = 0;
|
||||||
@@ -1098,6 +1108,12 @@ struct Stream {
|
|||||||
template <typename T>
|
template <typename T>
|
||||||
inline T& read(T &x) {
|
inline T& read(T &x) {
|
||||||
raw(&x, sizeof(x));
|
raw(&x, sizeof(x));
|
||||||
|
/*
|
||||||
|
if (endian == BIG_ENDIAN) {
|
||||||
|
if (sizeof(T) == 2) x = T(swap16(x));
|
||||||
|
if (sizeof(T) == 4) x = T(swap32(x));
|
||||||
|
}
|
||||||
|
*/
|
||||||
return x;
|
return x;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user