From eb501ccb65777a2cadf0912ed2b2ddae140f4ba8 Mon Sep 17 00:00:00 2001 From: XProger Date: Thu, 14 Dec 2017 02:25:44 +0300 Subject: [PATCH] #15 RNC texture format support (TR1 PSX title images) --- src/gameflow.h | 63 +++++++++-------------- src/inventory.h | 6 +-- src/texture.h | 131 +++++++++++++++++++++++++++++++++++++++++++++--- src/utils.h | 41 +++++++++++++++ 4 files changed, 193 insertions(+), 48 deletions(-) diff --git a/src/gameflow.h b/src/gameflow.h index 6dc69b9..cec2c95 100644 --- a/src/gameflow.h +++ b/src/gameflow.h @@ -671,51 +671,36 @@ namespace TR { const char* getGameScreen(Version version, LevelID id) { if (useEasyStart) { - if (version == VER_TR1_PC) - 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 : ; - } + switch (id) { + case LVL_TR1_TITLE : + if (Stream::existsContent("TITLEH.png")) return "TITLEH.png"; + if (Stream::existsContent("DATA/TITLEH.PCX")) return "DATA/TITLEH.PCX"; + if (Stream::existsContent("DELDATA/AMERTIT.RAW")) return "DELDATA/AMERTIT.RAW"; + break; - if (version == VER_TR2_PC) - switch (id) { - 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 : ; - } + case LVL_TR2_TITLE : + if (Stream::existsContent("TITLE.png")) return "TITLE.png"; + if (Stream::existsContent("data/TITLE.PCX")) return "data/TITLE.PCX"; + if (Stream::existsContent("pix/title.pcx")) return "pix/title.pcx"; + break; - if (version == VER_TR3_PC) - switch (id) { - case LVL_TR3_TITLE : - if (Stream::existsContent("pix/TITLEUK.BMP")) return "pix/TITLEUK.BMP"; - break; - default : ; - } + case LVL_TR3_TITLE : + if (Stream::existsContent("pix/TITLEUK.BMP")) return "pix/TITLEUK.BMP"; + break; + + default : ; + } } else { - if (version & VER_TR1) - switch (id) { - case LVL_TR1_TITLE : return "level/1/TITLEH.PCX"; - default : ; - } + switch (id) { + case LVL_TR1_TITLE : return "level/1/TITLEH.PCX"; - if (version & VER_TR2) - switch (id) { - case LVL_TR2_TITLE : return "level/2/TITLE.PCX"; - default : ; - } + case LVL_TR2_TITLE : return "level/2/TITLE.PCX"; - if (version & VER_TR3) - switch (id) { - case LVL_TR3_TITLE : return "level/3/TITLEUK.BMP"; - default : ; - } + case LVL_TR3_TITLE : return "level/3/TITLEUK.BMP"; + + default : ; + } } return NULL; diff --git a/src/inventory.h b/src/inventory.h index 00303e2..0e1533d 100644 --- a/src/inventory.h +++ b/src/inventory.h @@ -1021,11 +1021,11 @@ struct Inventory { } else { game->setShader(Core::passFilter, Shader::DEFAULT, false, false); - float aspectSrc = float(640.0f) / float(480.0f); + float aspectSrc = float(background[0]->origWidth) / float(background[0]->origHeight); float aspectDst = float(Core::width) / float(Core::height); float aspectImg = aspectDst / aspectSrc; - float ax = 640.0f / float(background[0]->width); - float ay = 480.0f / float(background[0]->height); + float ax = background[0]->origWidth / float(background[0]->width); + float ay = background[0]->origHeight / float(background[0]->height); Core::active.shader->setParam(uParam, vec4(ax * aspectImg, -ay, (0.5f - aspectImg * 0.5f) * ax, ay)); } diff --git a/src/texture.h b/src/texture.h index 346fd3a..bb8eda9 100644 --- a/src/texture.h +++ b/src/texture.h @@ -7,7 +7,7 @@ struct Texture { enum Format : uint32 { LUMINANCE, RGBA, RGB16, RGBA16, RGBA_FLOAT, RGBA_HALF, DEPTH, DEPTH_STENCIL, SHADOW, MAX }; GLuint ID; - int width, height; + int width, height, origWidth, origHeight; Format format; bool cube; bool filter; @@ -17,8 +17,8 @@ struct Texture { width = nextPow2(width); height = nextPow2(height); } - this->width = width; - this->height = height; + this->width = origWidth = width; + this->height = origHeight = height; glGenTextures(1, &ID); bind(0); @@ -236,6 +236,8 @@ struct Texture { } Texture *tex = new Texture(dw, dh, Texture::RGBA, false, data); + tex->origWidth = pcx.width; + tex->origHeight = pcx.height; delete[] buffer; return tex; @@ -275,6 +277,8 @@ struct Texture { } Texture *tex = new Texture(dw, dh, Texture::RGBA, false, data32); + tex->origWidth = width; + tex->origHeight = height; delete[] data24; delete[] data32; @@ -475,13 +479,13 @@ struct Texture { 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); + Texture *tex = new Texture(dw, dh, Texture::RGBA, false, data32); + tex->origWidth = width; + tex->origHeight = height; delete[] data32; delete[] data; @@ -490,6 +494,119 @@ struct Texture { } #endif + static void rncGetOffset(BitStream &bs, uint16 &offset) { + offset = 0; + if (bs.readBit()) { + offset = bs.readBit(); + + if (bs.readBit()) { + offset = ((offset << 1) | bs.readBit()) | 4; + + if (!bs.readBit()) + offset = (offset << 1) | bs.readBit(); + } else if (!offset) + offset = bs.readBit() + 2; + } + offset = ((offset << 8) | bs.readByte()) + 1; + } + + static Texture* LoadRNC(Stream &stream) { // https://github.com/lab313ru/rnc_propack_source + stream.seek(4); // skip MAGIC + uint32 size = swap32(stream.read(size)); + uint32 csize = swap32(stream.read(csize)); + stream.seek(4); // skip CRC + uint8 chunks, fkey; + stream.read(fkey); + stream.read(chunks); + + uint16 key = fkey; + + uint8 *data = new uint8[size]; + uint8 *cdata = new uint8[csize]; + + stream.raw(cdata, csize); + + BitStream bs(cdata, csize); + uint8 *dst = data; + uint8 *end = data + size; + + uint32 length = 0; + uint16 offset = 0; + + bs.readBits(2); + while (dst < end) { + if (!bs.readBit()) { + *dst++ = bs.readByte(); + } else { + if (bs.readBit()) { + if (bs.readBit()) { + if (bs.readBit()) { + length = bs.readByte() + 8; + if (length == 8) { + bs.readBit(); + continue; + } + } else + length = 3; + + rncGetOffset(bs, offset); + } else { + length = 2; + offset = bs.readByte() + 1; + } + + while (length--) + *dst++ = dst[-offset]; + } else { + length = bs.readBit() + 4; + if (bs.readBit()) + length = ((length - 1) << 1) + bs.readBit(); + + if (length != 9) { + rncGetOffset(bs, offset); + + while (length--) + *dst++ = dst[-offset]; + } else { + length = (bs.readBits(4) << 2) + 12; + while (length--) + *dst++ = bs.readByte(); + } + } + } + } + delete[] cdata; + + int width = 384; + int height = 256; + int dw = Core::support.texNPOT ? width : nextPow2(width); + int dh = Core::support.texNPOT ? height : nextPow2(height); + + uint32 *data32 = new uint32[dw * dh]; + { + uint32 *dst = data32; + uint16 *src = (uint16*)data; + + for (int j = 0; j < dh; j++) + for (int i = 0; i < dw; i++) { + if (i < width && j < height) { + uint16 c = *src++; + *dst++ = ((c & 0x001F) << 3) | ((c & 0x03E0) << 6) | (((c & 0x7C00) << 9)) | 0xFF000000; + } else + *dst++ = 0xFF000000; + } + + delete[] data; + } + Texture *tex = new Texture(dw, dh, Texture::RGBA, false, data32); + tex->origWidth = width; + tex->origHeight = height; + + delete[] data32; + + return tex; + } + static Texture* Load(Stream &stream) { uint32 magic; stream.read(magic); @@ -498,6 +615,8 @@ struct Texture { if (magic == 0x474E5089) return LoadPNG(stream); #endif + if (magic == FOURCC("RNC\002")) + return LoadRNC(stream); if ((magic & 0xFFFF) == 0x4D42) return LoadBMP(stream); return LoadPCX(stream); diff --git a/src/utils.h b/src/utils.h index 0a045fe..386be55 100644 --- a/src/utils.h +++ b/src/utils.h @@ -1128,6 +1128,47 @@ struct Stream { } }; + +struct BitStream { + uint8 *data; + uint8 *end; + uint8 index; + uint8 value; + + BitStream(uint8 *data, int size) : data(data), end(data + size), index(0), value(0) {} + + uint8 readBits(int count) { + uint32 bits = 0; + + while (count--) { + if (!index) { + ASSERT(data < end); + value = *data++; + index = 8; + } + + bits <<= 1; + + if (value & 0x80) + bits |= 1; + + value <<= 1; + index--; + } + + return bits; + } + + uint8 readBit() { + return readBits(1); + } + + uint8 readByte() { + return *data++; + } +}; + + namespace String { void toLower(char *str) {