mirror of
https://github.com/XProger/OpenLara.git
synced 2025-01-17 21:09:00 +01:00
#15 add PNG support for Android obb's title and loading screens, add mp3 audio support
This commit is contained in:
parent
8219740b9f
commit
a1141c4939
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
|
12
src/core.h
12
src/core.h
@ -1,6 +1,10 @@
|
||||
#ifndef H_CORE
|
||||
#define H_CORE
|
||||
|
||||
#ifndef __EMSCRIPTEN__
|
||||
#define USE_INFLATE
|
||||
#endif
|
||||
|
||||
#include <stdio.h>
|
||||
#ifdef WIN32
|
||||
#include <windows.h>
|
||||
@ -137,6 +141,10 @@
|
||||
#define glProgramBinary(...)
|
||||
#endif
|
||||
|
||||
#ifdef USE_INFLATE
|
||||
#include "libs/tinf/tinf.h"
|
||||
#endif
|
||||
|
||||
#include "utils.h"
|
||||
|
||||
enum ControlKey { cLeft, cRight, cUp, cDown, cJump, cWalk, cAction, cWeapon, cLook, cStepLeft, cStepRight, cRoll, cInventory, cMAX };
|
||||
@ -540,6 +548,10 @@ namespace Core {
|
||||
}
|
||||
|
||||
void init() {
|
||||
#ifdef USE_INFLATE
|
||||
tinf_init();
|
||||
#endif
|
||||
|
||||
Input::init();
|
||||
#ifdef ANDROID
|
||||
void *libGL = dlopen("libGLESv2.so", RTLD_LAZY);
|
||||
|
@ -494,7 +494,7 @@ namespace TR {
|
||||
if (Stream::existsContent("DATA/GYM.SAT"))
|
||||
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;
|
||||
if (Stream::existsContent("DATA/ASSAULT.PSX"))
|
||||
return VER_TR2_PSX;
|
||||
@ -630,6 +630,7 @@ namespace TR {
|
||||
callback(Sound::openCDAudioMP3("audio/cdaudio.dat", "audio/cdaudio.mp3", track), userData);
|
||||
return;
|
||||
}
|
||||
sprintf(title, "track_%02d", track);
|
||||
if (!checkTrack("", title) && !checkTrack("audio/2/", title) && !checkTrack("audio/", title)) {
|
||||
callback(NULL, userData);
|
||||
return;
|
||||
@ -674,6 +675,7 @@ namespace TR {
|
||||
switch (id) {
|
||||
case LVL_TR1_TITLE :
|
||||
if (Stream::existsContent("DATA/TITLEH.PCX")) return "DATA/TITLEH.PCX";
|
||||
if (Stream::existsContent("TITLEH.png")) return "TITLEH.png";
|
||||
break;
|
||||
default : ;
|
||||
}
|
||||
@ -683,6 +685,7 @@ namespace TR {
|
||||
case LVL_TR2_TITLE :
|
||||
if (Stream::existsContent("data/TITLE.PCX")) return "data/TITLE.PCX";
|
||||
if (Stream::existsContent("pix/title.pcx")) return "pix/title.pcx";
|
||||
if (Stream::existsContent("TITLE.png")) return "TITLE.png";
|
||||
break;
|
||||
default : ;
|
||||
}
|
||||
|
@ -3,11 +3,10 @@
|
||||
|
||||
#define DECODE_VAG
|
||||
#define DECODE_ADPCM
|
||||
//#define DECODE_MP3
|
||||
#define DECODE_OGG
|
||||
|
||||
#ifdef __EMSCRIPTEN__ // TODO: http streaming
|
||||
#undef DECODE_MP3
|
||||
#ifndef __EMSCRIPTEN
|
||||
#define DECODE_MP3
|
||||
#endif
|
||||
|
||||
#include "utils.h"
|
||||
@ -36,7 +35,7 @@ namespace Sound {
|
||||
#define MAX_FDN 16
|
||||
#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 {
|
||||
int index;
|
||||
|
217
src/texture.h
217
src/texture.h
@ -281,11 +281,224 @@ struct Texture {
|
||||
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) {
|
||||
uint16 magic;
|
||||
uint32 magic;
|
||||
stream.read(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 LoadPCX(stream);
|
||||
}
|
||||
|
20
src/utils.h
20
src/utils.h
@ -97,6 +97,14 @@ inline void swap(T &a, T &b) {
|
||||
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) {
|
||||
return a < -PI ? a + PI2 : (a >= PI ? a - PI2 : a);
|
||||
}
|
||||
@ -1010,9 +1018,11 @@ struct Stream {
|
||||
char *name;
|
||||
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))) {
|
||||
char path[255];
|
||||
path[0] = 0;
|
||||
@ -1098,6 +1108,12 @@ struct Stream {
|
||||
template <typename T>
|
||||
inline T& read(T &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;
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user