mirror of https://github.com/AxioDL/metaforce.git
initial TXTR support
This commit is contained in:
parent
dea341d27b
commit
29103838da
|
@ -1,5 +1,6 @@
|
||||||
include_directories(${HECL_INCLUDE_DIR} ${NOD_LIB_INCLUDE_DIR})
|
include_directories(${HECL_INCLUDE_DIR} ${NOD_LIB_INCLUDE_DIR} ${LIBPNG_INCLUDE_DIR}
|
||||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11")
|
${SQUISH_INCLUDE_DIR})
|
||||||
|
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -Wno-multichar")
|
||||||
|
|
||||||
# Magic ingredient
|
# Magic ingredient
|
||||||
find_package(atdna REQUIRED)
|
find_package(atdna REQUIRED)
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
add_library(DNACommon
|
add_library(DNACommon
|
||||||
STRG.hpp
|
STRG.hpp STRG.cpp
|
||||||
STRG.cpp
|
TXTR.hpp TXTR.cpp
|
||||||
DNACommon.hpp
|
DNACommon.hpp DNACommon.cpp)
|
||||||
DNACommon.cpp)
|
|
||||||
|
|
|
@ -0,0 +1,444 @@
|
||||||
|
#include <png.h>
|
||||||
|
#include <colourblockGCN.h>
|
||||||
|
#include "TXTR.hpp"
|
||||||
|
|
||||||
|
namespace Retro
|
||||||
|
{
|
||||||
|
|
||||||
|
static LogVisor::LogModule Log("libpng");
|
||||||
|
|
||||||
|
/* GX uses this upsampling technique to prevent banding on downsampled texture formats */
|
||||||
|
static inline uint8_t Convert3To8(uint8_t v)
|
||||||
|
{
|
||||||
|
/* Swizzle bits: 00000123 -> 12312312 */
|
||||||
|
return (v << 5) | (v << 2) | (v >> 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline uint8_t Convert4To8(uint8_t v)
|
||||||
|
{
|
||||||
|
/* Swizzle bits: 00001234 -> 12341234 */
|
||||||
|
return (v << 4) | v;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline uint8_t Convert5To8(uint8_t v)
|
||||||
|
{
|
||||||
|
/* Swizzle bits: 00012345 -> 12345123 */
|
||||||
|
return (v << 3) | (v >> 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline uint8_t Convert6To8(uint8_t v)
|
||||||
|
{
|
||||||
|
/* Swizzle bits: 00123456 -> 12345612 */
|
||||||
|
return (v << 2) | (v >> 4);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline uint8_t Lookup4BPP(const uint8_t* texels, int width, int x, int y)
|
||||||
|
{
|
||||||
|
int bwidth = (width + 7) / 8;
|
||||||
|
int bx = x / 8;
|
||||||
|
int by = y / 8;
|
||||||
|
int rx = x % 8;
|
||||||
|
int ry = y % 8;
|
||||||
|
int bidx = by * bwidth + bx;
|
||||||
|
const uint8_t* btexels = &texels[32*bidx];
|
||||||
|
return btexels[ry*4+rx/2] << (rx%2*4) & 0xf;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline uint8_t Lookup8BPP(const uint8_t* texels, int width, int x, int y)
|
||||||
|
{
|
||||||
|
int bwidth = (width + 7) / 8;
|
||||||
|
int bx = x / 8;
|
||||||
|
int by = y / 4;
|
||||||
|
int rx = x % 8;
|
||||||
|
int ry = y % 4;
|
||||||
|
int bidx = by * bwidth + bx;
|
||||||
|
const uint8_t* btexels = &texels[32*bidx];
|
||||||
|
return btexels[ry*8+rx];
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline uint16_t Lookup16BPP(const uint8_t* texels, int width, int x, int y)
|
||||||
|
{
|
||||||
|
int bwidth = (width + 3) / 4;
|
||||||
|
int bx = x / 4;
|
||||||
|
int by = y / 4;
|
||||||
|
int rx = x % 4;
|
||||||
|
int ry = y % 4;
|
||||||
|
int bidx = by * bwidth + bx;
|
||||||
|
const uint16_t* btexels = (uint16_t*)&texels[32*bidx];
|
||||||
|
return btexels[ry*4+rx];
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void LookupRGBA8(const uint8_t* texels, int width, int x, int y,
|
||||||
|
uint8_t* r, uint8_t* g, uint8_t* b, uint8_t* a)
|
||||||
|
{
|
||||||
|
int bwidth = (width + 3) / 4;
|
||||||
|
int bx = x / 4;
|
||||||
|
int by = y / 4;
|
||||||
|
int rx = x % 4;
|
||||||
|
int ry = y % 4;
|
||||||
|
int bidx = (by * bwidth + bx) * 2;
|
||||||
|
const uint16_t* artexels = (uint16_t*)&texels[32*bidx];
|
||||||
|
const uint16_t* gbtexels = (uint16_t*)&texels[32*(bidx+1)];
|
||||||
|
uint16_t ar = HECL::SBig(artexels[ry*4+rx]);
|
||||||
|
*a = ar >> 8 & 0xff;
|
||||||
|
*r = ar & 0xff;
|
||||||
|
uint16_t gb = HECL::SBig(gbtexels[ry*4+rx]);
|
||||||
|
*g = gb >> 8 & 0xff;
|
||||||
|
*b = gb & 0xff;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void DecodeI4(png_structrp png, png_infop info,
|
||||||
|
const uint8_t* texels, int width, int height)
|
||||||
|
{
|
||||||
|
png_set_IHDR(png, info, width, height, 4,
|
||||||
|
PNG_COLOR_TYPE_GRAY, PNG_INTERLACE_NONE,
|
||||||
|
PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);
|
||||||
|
png_write_info(png, info);
|
||||||
|
std::unique_ptr<uint8_t[]> buf(new uint8_t[width]);
|
||||||
|
memset(buf.get(), 0, width);
|
||||||
|
for (int y=0 ; y<height ; ++y)
|
||||||
|
{
|
||||||
|
for (int x=0 ; x<width ; ++x)
|
||||||
|
buf[x/2] |= Lookup4BPP(texels, width, x, y) << x%2*4;
|
||||||
|
png_write_row(png, buf.get());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void DecodeI8(png_structrp png, png_infop info,
|
||||||
|
const uint8_t* texels, int width, int height)
|
||||||
|
{
|
||||||
|
png_set_IHDR(png, info, width, height, 8,
|
||||||
|
PNG_COLOR_TYPE_GRAY, PNG_INTERLACE_NONE,
|
||||||
|
PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);
|
||||||
|
png_write_info(png, info);
|
||||||
|
std::unique_ptr<uint8_t[]> buf(new uint8_t[width]);
|
||||||
|
for (int y=0 ; y<height ; ++y)
|
||||||
|
{
|
||||||
|
for (int x=0 ; x<width ; ++x)
|
||||||
|
buf[x] = Lookup8BPP(texels, width, x, y);
|
||||||
|
png_write_row(png, buf.get());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void DecodeIA4(png_structrp png, png_infop info,
|
||||||
|
const uint8_t* texels, int width, int height)
|
||||||
|
{
|
||||||
|
png_set_IHDR(png, info, width, height, 4,
|
||||||
|
PNG_COLOR_TYPE_GRAY_ALPHA, PNG_INTERLACE_NONE,
|
||||||
|
PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);
|
||||||
|
png_write_info(png, info);
|
||||||
|
std::unique_ptr<uint8_t[]> buf(new uint8_t[width]);
|
||||||
|
for (int y=0 ; y<height ; ++y)
|
||||||
|
{
|
||||||
|
for (int x=0 ; x<width ; ++x)
|
||||||
|
buf[x] = Lookup8BPP(texels, width, x, y);
|
||||||
|
png_write_row(png, buf.get());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void DecodeIA8(png_structrp png, png_infop info,
|
||||||
|
const uint8_t* texels, int width, int height)
|
||||||
|
{
|
||||||
|
png_set_IHDR(png, info, width, height, 8,
|
||||||
|
PNG_COLOR_TYPE_GRAY_ALPHA, PNG_INTERLACE_NONE,
|
||||||
|
PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);
|
||||||
|
png_write_info(png, info);
|
||||||
|
std::unique_ptr<uint16_t[]> buf(new uint16_t[width]);
|
||||||
|
for (int y=0 ; y<height ; ++y)
|
||||||
|
{
|
||||||
|
for (int x=0 ; x<width ; ++x)
|
||||||
|
buf[x] = Lookup16BPP(texels, width, x, y);
|
||||||
|
png_write_row(png, (png_bytep)buf.get());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static const uint8_t* DecodePalette(png_structrp png, png_infop info,
|
||||||
|
int numEntries, const uint8_t* data)
|
||||||
|
{
|
||||||
|
uint32_t format = HECL::SBig(*(uint32_t*)data);
|
||||||
|
data += 8;
|
||||||
|
png_color cEntries[256];
|
||||||
|
png_byte aEntries[256];
|
||||||
|
switch (format)
|
||||||
|
{
|
||||||
|
case 0:
|
||||||
|
{
|
||||||
|
/* IA8 */
|
||||||
|
for (int e=0 ; e<numEntries ; ++e)
|
||||||
|
{
|
||||||
|
cEntries[e].red = data[e*2];
|
||||||
|
cEntries[e].green = data[e*2];
|
||||||
|
cEntries[e].blue = data[e*2];
|
||||||
|
aEntries[e] = data[e*2+1];
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 1:
|
||||||
|
{
|
||||||
|
/* RGB565 */
|
||||||
|
const uint16_t* data16 = (uint16_t*)data;
|
||||||
|
for (int e=0 ; e<numEntries ; ++e)
|
||||||
|
{
|
||||||
|
uint16_t texel = HECL::SBig(data16[e]);
|
||||||
|
cEntries[e].red = Convert5To8(texel >> 11 & 0x1f);
|
||||||
|
cEntries[e].green = Convert6To8(texel >> 5 & 0x3f);
|
||||||
|
cEntries[e].blue = Convert5To8(texel & 0x1f);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 2:
|
||||||
|
{
|
||||||
|
/* RGB5A3 */
|
||||||
|
const uint16_t* data16 = (uint16_t*)data;
|
||||||
|
for (int e=0 ; e<numEntries ; ++e)
|
||||||
|
{
|
||||||
|
uint16_t texel = HECL::SBig(data16[e]);
|
||||||
|
if (texel & 0x8000)
|
||||||
|
{
|
||||||
|
cEntries[e].red = Convert5To8(texel >> 10 & 0x1f);
|
||||||
|
cEntries[e].green = Convert5To8(texel >> 5 & 0x1f);
|
||||||
|
cEntries[e].blue = Convert5To8(texel & 0x1f);
|
||||||
|
aEntries[e] = 0xff;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
cEntries[e].red = Convert4To8(texel >> 8 & 0xf);
|
||||||
|
cEntries[e].green = Convert4To8(texel >> 4 & 0xf);
|
||||||
|
cEntries[e].blue = Convert4To8(texel & 0xf);
|
||||||
|
aEntries[e] = Convert3To8(texel >> 12 & 0x7);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
png_set_PLTE(png, info, cEntries, numEntries);
|
||||||
|
if (format == 0 || format == 2)
|
||||||
|
png_set_tRNS(png, info, aEntries, numEntries, nullptr);
|
||||||
|
data += numEntries * 2;
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void DecodeC4(png_structrp png, png_infop info,
|
||||||
|
const uint8_t* data, int width, int height)
|
||||||
|
{
|
||||||
|
png_set_IHDR(png, info, width, height, 4,
|
||||||
|
PNG_COLOR_TYPE_PALETTE, PNG_INTERLACE_NONE,
|
||||||
|
PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);
|
||||||
|
const uint8_t* texels = DecodePalette(png, info, 16, data);
|
||||||
|
png_write_info(png, info);
|
||||||
|
std::unique_ptr<uint8_t[]> buf(new uint8_t[width]);
|
||||||
|
memset(buf.get(), 0, width);
|
||||||
|
for (int y=0 ; y<height ; ++y)
|
||||||
|
{
|
||||||
|
for (int x=0 ; x<width ; ++x)
|
||||||
|
buf[x/2] |= Lookup4BPP(texels, width, x, y) << x%2*4;
|
||||||
|
png_write_row(png, buf.get());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void DecodeC8(png_structrp png, png_infop info,
|
||||||
|
const uint8_t* data, int width, int height)
|
||||||
|
{
|
||||||
|
png_set_IHDR(png, info, width, height, 4,
|
||||||
|
PNG_COLOR_TYPE_PALETTE, PNG_INTERLACE_NONE,
|
||||||
|
PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);
|
||||||
|
const uint8_t* texels = DecodePalette(png, info, 256, data);
|
||||||
|
png_write_info(png, info);
|
||||||
|
std::unique_ptr<uint8_t[]> buf(new uint8_t[width]);
|
||||||
|
for (int y=0 ; y<height ; ++y)
|
||||||
|
{
|
||||||
|
for (int x=0 ; x<width ; ++x)
|
||||||
|
buf[x] = Lookup8BPP(texels, width, x, y);
|
||||||
|
png_write_row(png, buf.get());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void DecodeRGB565(png_structrp png, png_infop info,
|
||||||
|
const uint8_t* texels, int width, int height)
|
||||||
|
{
|
||||||
|
png_set_IHDR(png, info, width, height, 8,
|
||||||
|
PNG_COLOR_TYPE_RGB, PNG_INTERLACE_NONE,
|
||||||
|
PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);
|
||||||
|
png_write_info(png, info);
|
||||||
|
std::unique_ptr<uint8_t[]> buf(new uint8_t[width*3]);
|
||||||
|
for (int y=0 ; y<height ; ++y)
|
||||||
|
{
|
||||||
|
for (int x=0 ; x<width ; ++x)
|
||||||
|
{
|
||||||
|
uint16_t texel = HECL::SBig(Lookup16BPP(texels, width, x, y));
|
||||||
|
buf[x*3] = Convert5To8(texel >> 11 & 0x1f);
|
||||||
|
buf[x*3+1] = Convert6To8(texel >> 5 & 0x3f);
|
||||||
|
buf[x*3+2] = Convert5To8(texel & 0x1f);
|
||||||
|
}
|
||||||
|
png_write_row(png, buf.get());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void DecodeRGB5A3(png_structrp png, png_infop info,
|
||||||
|
const uint8_t* texels, int width, int height)
|
||||||
|
{
|
||||||
|
png_set_IHDR(png, info, width, height, 8,
|
||||||
|
PNG_COLOR_TYPE_RGB_ALPHA, PNG_INTERLACE_NONE,
|
||||||
|
PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);
|
||||||
|
png_write_info(png, info);
|
||||||
|
std::unique_ptr<uint8_t[]> buf(new uint8_t[width*4]);
|
||||||
|
for (int y=0 ; y<height ; ++y)
|
||||||
|
{
|
||||||
|
for (int x=0 ; x<width ; ++x)
|
||||||
|
{
|
||||||
|
uint16_t texel = HECL::SBig(Lookup16BPP(texels, width, x, y));
|
||||||
|
if (texel & 0x8000)
|
||||||
|
{
|
||||||
|
buf[x*4] = Convert5To8(texel >> 10 & 0x1f);
|
||||||
|
buf[x*4+1] = Convert5To8(texel >> 5 & 0x1f);
|
||||||
|
buf[x*4+2] = Convert5To8(texel & 0x1f);
|
||||||
|
buf[x*4+3] = 0xff;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
buf[x*4] = Convert4To8(texel >> 8 & 0xf);
|
||||||
|
buf[x*4+1] = Convert4To8(texel >> 4 & 0xf);
|
||||||
|
buf[x*4+2] = Convert4To8(texel & 0xf);
|
||||||
|
buf[x*4+3] = Convert3To8(texel >> 12 & 0x7);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
png_write_row(png, buf.get());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void DecodeRGBA8(png_structrp png, png_infop info,
|
||||||
|
const uint8_t* texels, int width, int height)
|
||||||
|
{
|
||||||
|
png_set_IHDR(png, info, width, height, 8,
|
||||||
|
PNG_COLOR_TYPE_RGB_ALPHA, PNG_INTERLACE_NONE,
|
||||||
|
PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);
|
||||||
|
png_write_info(png, info);
|
||||||
|
std::unique_ptr<uint8_t[]> buf(new uint8_t[width*4]);
|
||||||
|
for (int y=0 ; y<height ; ++y)
|
||||||
|
{
|
||||||
|
for (int x=0 ; x<width ; ++x)
|
||||||
|
LookupRGBA8(texels, width, x, y, &buf[x*4], &buf[x*4+1], &buf[x*4+2], &buf[x*4+3]);
|
||||||
|
png_write_row(png, buf.get());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct DXTBlock
|
||||||
|
{
|
||||||
|
uint16_t color1;
|
||||||
|
uint16_t color2;
|
||||||
|
uint8_t lines[4];
|
||||||
|
};
|
||||||
|
|
||||||
|
static void DecodeCMPR(png_structrp png, png_infop info,
|
||||||
|
const uint8_t* texels, int width, int height)
|
||||||
|
{
|
||||||
|
png_set_IHDR(png, info, width, height, 8,
|
||||||
|
PNG_COLOR_TYPE_RGB_ALPHA, PNG_INTERLACE_NONE,
|
||||||
|
PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);
|
||||||
|
png_write_info(png, info);
|
||||||
|
|
||||||
|
/* Decode 8 rows at a time */
|
||||||
|
std::unique_ptr<uint32_t[]> buf(new uint32_t[width*8]);
|
||||||
|
uint32_t* bTargets[4] = {
|
||||||
|
buf.get(),
|
||||||
|
buf.get() + 4,
|
||||||
|
buf.get() + 4 * width,
|
||||||
|
buf.get() + 4 * width + 4
|
||||||
|
};
|
||||||
|
int bwidth = (width + 7) / 8;
|
||||||
|
for (int y=height/8-1 ; y>=0 ; --y)
|
||||||
|
{
|
||||||
|
const DXTBlock* blks = (DXTBlock*)(texels + 32 * bwidth * y);
|
||||||
|
for (int x=0 ; x<width ; x+=8)
|
||||||
|
{
|
||||||
|
uint32_t blkOut[4][4][4];
|
||||||
|
squish::DecompressColourGCN((uint8_t*)blkOut[0][0], blks++);
|
||||||
|
squish::DecompressColourGCN((uint8_t*)blkOut[1][0], blks++);
|
||||||
|
squish::DecompressColourGCN((uint8_t*)blkOut[2][0], blks++);
|
||||||
|
squish::DecompressColourGCN((uint8_t*)blkOut[3][0], blks++);
|
||||||
|
|
||||||
|
for (int bt=0 ; bt<4 ; ++bt)
|
||||||
|
for (int by=0 ; by<4 ; ++by)
|
||||||
|
memcpy(bTargets[bt] + x + width * by, blkOut[bt][by], 16);
|
||||||
|
}
|
||||||
|
for (int r=7 ; r>=0 ; --r)
|
||||||
|
png_write_row(png, (png_bytep)(bTargets[0] + width * r));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void PNGErr(png_structp png, png_const_charp msg)
|
||||||
|
{
|
||||||
|
Log.report(LogVisor::Error, msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void PNGWarn(png_structp png, png_const_charp msg)
|
||||||
|
{
|
||||||
|
Log.report(LogVisor::Warning, msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool TXTR::Extract(PAKEntryReadStream& rs, const HECL::ProjectPath& outPath)
|
||||||
|
{
|
||||||
|
rs.setEndian(Athena::BigEndian);
|
||||||
|
uint32_t format = rs.readUint32();
|
||||||
|
uint16_t width = rs.readUint16();
|
||||||
|
uint16_t height = rs.readUint16();
|
||||||
|
uint32_t numMips = rs.readUint32();
|
||||||
|
|
||||||
|
FILE* fp = HECL::Fopen(outPath.getAbsolutePath().c_str(), _S("wb"));
|
||||||
|
if (!fp)
|
||||||
|
{
|
||||||
|
Log.report(LogVisor::Error,
|
||||||
|
_S("Unable to open '%s' for writing"),
|
||||||
|
outPath.getAbsolutePath().c_str());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
png_structp png = png_create_write_struct(PNG_LIBPNG_VER_STRING, nullptr, PNGErr, PNGWarn);
|
||||||
|
png_init_io(png, fp);
|
||||||
|
png_infop info = png_create_info_struct(png);
|
||||||
|
|
||||||
|
switch (format)
|
||||||
|
{
|
||||||
|
case 0:
|
||||||
|
DecodeI4(png, info, rs.data() + 12, width, height);
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
DecodeI8(png, info, rs.data() + 12, width, height);
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
DecodeIA4(png, info, rs.data() + 12, width, height);
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
|
DecodeIA8(png, info, rs.data() + 12, width, height);
|
||||||
|
break;
|
||||||
|
case 4:
|
||||||
|
DecodeC4(png, info, rs.data() + 12, width, height);
|
||||||
|
break;
|
||||||
|
case 5:
|
||||||
|
DecodeC8(png, info, rs.data() + 12, width, height);
|
||||||
|
break;
|
||||||
|
case 7:
|
||||||
|
DecodeRGB565(png, info, rs.data() + 12, width, height);
|
||||||
|
break;
|
||||||
|
case 8:
|
||||||
|
DecodeRGB5A3(png, info, rs.data() + 12, width, height);
|
||||||
|
break;
|
||||||
|
case 9:
|
||||||
|
DecodeRGBA8(png, info, rs.data() + 12, width, height);
|
||||||
|
break;
|
||||||
|
case 10:
|
||||||
|
DecodeCMPR(png, info, rs.data() + 12, width, height);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
png_write_end(png, info);
|
||||||
|
png_write_flush(png);
|
||||||
|
png_destroy_write_struct(&png, &info);
|
||||||
|
fclose(fp);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool TXTR::Cook(const HECL::ProjectPath& inPath, const HECL::ProjectPath& outPath)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,17 @@
|
||||||
|
#ifndef __DNACOMMON_TXTR_HPP__
|
||||||
|
#define __DNACOMMON_TXTR_HPP__
|
||||||
|
|
||||||
|
#include "DNACommon.hpp"
|
||||||
|
|
||||||
|
namespace Retro
|
||||||
|
{
|
||||||
|
|
||||||
|
struct TXTR
|
||||||
|
{
|
||||||
|
static bool Extract(PAKEntryReadStream& rs, const HECL::ProjectPath& outPath);
|
||||||
|
static bool Cook(const HECL::ProjectPath& inPath, const HECL::ProjectPath& outPath);
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // __DNACOMMON_TXTR_HPP__
|
|
@ -3,8 +3,7 @@ make_dnalist(liblist
|
||||||
MLVL
|
MLVL
|
||||||
STRG)
|
STRG)
|
||||||
add_library(DNAMP1
|
add_library(DNAMP1
|
||||||
DNAMP1.hpp
|
DNAMP1.hpp DNAMP1.cpp
|
||||||
DNAMP1.cpp
|
|
||||||
${liblist}
|
${liblist}
|
||||||
PAK.cpp
|
PAK.cpp
|
||||||
STRG.cpp)
|
STRG.cpp)
|
||||||
|
|
|
@ -4,6 +4,7 @@
|
||||||
#include "DNAMP1.hpp"
|
#include "DNAMP1.hpp"
|
||||||
#include "STRG.hpp"
|
#include "STRG.hpp"
|
||||||
#include "MLVL.hpp"
|
#include "MLVL.hpp"
|
||||||
|
#include "../DNACommon/TXTR.hpp"
|
||||||
|
|
||||||
namespace Retro
|
namespace Retro
|
||||||
{
|
{
|
||||||
|
@ -18,9 +19,9 @@ PAKBridge::PAKBridge(HECL::Database::Project& project, const NOD::DiscBase::IPar
|
||||||
m_pak.read(rs);
|
m_pak.read(rs);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string PAKBridge::getLevelString() const
|
HECL::SystemString PAKBridge::getLevelString() const
|
||||||
{
|
{
|
||||||
std::string retval;
|
HECL::SystemString retval;
|
||||||
for (const PAK::Entry& entry : m_pak.m_entries)
|
for (const PAK::Entry& entry : m_pak.m_entries)
|
||||||
{
|
{
|
||||||
if (entry.type == Retro::MLVL)
|
if (entry.type == Retro::MLVL)
|
||||||
|
@ -36,7 +37,7 @@ std::string PAKBridge::getLevelString() const
|
||||||
mlvlName.read(rs);
|
mlvlName.read(rs);
|
||||||
if (retval.size())
|
if (retval.size())
|
||||||
retval += _S(", ");
|
retval += _S(", ");
|
||||||
retval += mlvlName.getUTF8(ENGL, 0);
|
retval += mlvlName.getSystemString(ENGL, 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -45,8 +46,13 @@ std::string PAKBridge::getLevelString() const
|
||||||
|
|
||||||
ResExtractor PAKBridge::LookupExtractor(const PAK::Entry& entry)
|
ResExtractor PAKBridge::LookupExtractor(const PAK::Entry& entry)
|
||||||
{
|
{
|
||||||
if (entry.type == Retro::STRG)
|
switch (entry.type.toUint32())
|
||||||
|
{
|
||||||
|
case SBIG('STRG'):
|
||||||
return {STRG::Extract<STRG>, ".as"};
|
return {STRG::Extract<STRG>, ".as"};
|
||||||
|
case SBIG('TXTR'):
|
||||||
|
return {TXTR::Extract, ".png"};
|
||||||
|
}
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -21,7 +21,7 @@ class PAKBridge
|
||||||
public:
|
public:
|
||||||
PAKBridge(HECL::Database::Project& project, const NOD::DiscBase::IPartition::Node& node);
|
PAKBridge(HECL::Database::Project& project, const NOD::DiscBase::IPartition::Node& node);
|
||||||
const std::string& getName() const {return m_node.getName();}
|
const std::string& getName() const {return m_node.getName();}
|
||||||
std::string getLevelString() const;
|
HECL::SystemString getLevelString() const;
|
||||||
bool extractResources(const HECL::ProjectPath& dirOut,
|
bool extractResources(const HECL::ProjectPath& dirOut,
|
||||||
const HECL::ProjectPath& cookedOut,
|
const HECL::ProjectPath& cookedOut,
|
||||||
bool force);
|
bool force);
|
||||||
|
|
|
@ -2,7 +2,6 @@ make_dnalist(liblist
|
||||||
MLVL
|
MLVL
|
||||||
STRG)
|
STRG)
|
||||||
add_library(DNAMP2
|
add_library(DNAMP2
|
||||||
DNAMP2.hpp
|
DNAMP2.hpp DNAMP2.cpp
|
||||||
DNAMP2.cpp
|
|
||||||
${liblist}
|
${liblist}
|
||||||
STRG.cpp)
|
STRG.cpp)
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
#include "DNAMP2.hpp"
|
#include "DNAMP2.hpp"
|
||||||
#include "STRG.hpp"
|
#include "STRG.hpp"
|
||||||
#include "MLVL.hpp"
|
#include "MLVL.hpp"
|
||||||
|
#include "../DNACommon/TXTR.hpp"
|
||||||
|
|
||||||
namespace Retro
|
namespace Retro
|
||||||
{
|
{
|
||||||
|
@ -17,9 +18,9 @@ PAKBridge::PAKBridge(HECL::Database::Project& project, const NOD::DiscBase::IPar
|
||||||
m_pak.read(rs);
|
m_pak.read(rs);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string PAKBridge::getLevelString() const
|
HECL::SystemString PAKBridge::getLevelString() const
|
||||||
{
|
{
|
||||||
std::string retval;
|
HECL::SystemString retval;
|
||||||
for (const DNAMP1::PAK::Entry& entry : m_pak.m_entries)
|
for (const DNAMP1::PAK::Entry& entry : m_pak.m_entries)
|
||||||
{
|
{
|
||||||
if (entry.type == Retro::MLVL)
|
if (entry.type == Retro::MLVL)
|
||||||
|
@ -35,7 +36,7 @@ std::string PAKBridge::getLevelString() const
|
||||||
mlvlName.read(rs);
|
mlvlName.read(rs);
|
||||||
if (retval.size())
|
if (retval.size())
|
||||||
retval += _S(", ");
|
retval += _S(", ");
|
||||||
retval += mlvlName.getUTF8(ENGL, 0);
|
retval += mlvlName.getSystemString(ENGL, 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -44,8 +45,13 @@ std::string PAKBridge::getLevelString() const
|
||||||
|
|
||||||
ResExtractor PAKBridge::LookupExtractor(const DNAMP1::PAK::Entry& entry)
|
ResExtractor PAKBridge::LookupExtractor(const DNAMP1::PAK::Entry& entry)
|
||||||
{
|
{
|
||||||
if (entry.type == Retro::STRG)
|
switch (entry.type.toUint32())
|
||||||
|
{
|
||||||
|
case SBIG('STRG'):
|
||||||
return {STRG::Extract<STRG>, ".as"};
|
return {STRG::Extract<STRG>, ".as"};
|
||||||
|
case SBIG('TXTR'):
|
||||||
|
return {TXTR::Extract, ".png"};
|
||||||
|
}
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -21,7 +21,7 @@ class PAKBridge
|
||||||
public:
|
public:
|
||||||
PAKBridge(HECL::Database::Project& project, const NOD::DiscBase::IPartition::Node& node);
|
PAKBridge(HECL::Database::Project& project, const NOD::DiscBase::IPartition::Node& node);
|
||||||
const std::string& getName() const {return m_node.getName();}
|
const std::string& getName() const {return m_node.getName();}
|
||||||
std::string getLevelString() const;
|
HECL::SystemString getLevelString() const;
|
||||||
bool extractResources(const HECL::ProjectPath& dirOut,
|
bool extractResources(const HECL::ProjectPath& dirOut,
|
||||||
const HECL::ProjectPath& cookedOut,
|
const HECL::ProjectPath& cookedOut,
|
||||||
bool force);
|
bool force);
|
||||||
|
|
|
@ -3,8 +3,7 @@ make_dnalist(liblist
|
||||||
MLVL
|
MLVL
|
||||||
STRG)
|
STRG)
|
||||||
add_library(DNAMP3
|
add_library(DNAMP3
|
||||||
DNAMP3.hpp
|
DNAMP3.hpp DNAMP3.cpp
|
||||||
DNAMP3.cpp
|
|
||||||
${liblist}
|
${liblist}
|
||||||
PAK.cpp
|
PAK.cpp
|
||||||
STRG.cpp)
|
STRG.cpp)
|
||||||
|
|
|
@ -4,6 +4,7 @@
|
||||||
#include "DNAMP3.hpp"
|
#include "DNAMP3.hpp"
|
||||||
#include "STRG.hpp"
|
#include "STRG.hpp"
|
||||||
#include "MLVL.hpp"
|
#include "MLVL.hpp"
|
||||||
|
#include "../DNACommon/TXTR.hpp"
|
||||||
|
|
||||||
namespace Retro
|
namespace Retro
|
||||||
{
|
{
|
||||||
|
@ -19,9 +20,9 @@ PAKBridge::PAKBridge(HECL::Database::Project& project, const NOD::DiscBase::IPar
|
||||||
m_pak.read(rs);
|
m_pak.read(rs);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string PAKBridge::getLevelString() const
|
HECL::SystemString PAKBridge::getLevelString() const
|
||||||
{
|
{
|
||||||
std::set<std::string, CaseInsensitiveCompare> uniq;
|
std::set<HECL::SystemString, CaseInsensitiveCompare> uniq;
|
||||||
for (const PAK::Entry& entry : m_pak.m_entries)
|
for (const PAK::Entry& entry : m_pak.m_entries)
|
||||||
{
|
{
|
||||||
if (entry.type == Retro::MLVL)
|
if (entry.type == Retro::MLVL)
|
||||||
|
@ -35,13 +36,13 @@ std::string PAKBridge::getLevelString() const
|
||||||
PAKEntryReadStream rs = nameEnt->beginReadStream(m_node);
|
PAKEntryReadStream rs = nameEnt->beginReadStream(m_node);
|
||||||
STRG mlvlName;
|
STRG mlvlName;
|
||||||
mlvlName.read(rs);
|
mlvlName.read(rs);
|
||||||
uniq.insert(mlvlName.getUTF8(ENGL, 0));
|
uniq.insert(mlvlName.getSystemString(ENGL, 0));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
std::string retval;
|
HECL::SystemString retval;
|
||||||
bool comma = false;
|
bool comma = false;
|
||||||
for (const std::string& str : uniq)
|
for (const HECL::SystemString& str : uniq)
|
||||||
{
|
{
|
||||||
if (comma)
|
if (comma)
|
||||||
retval += _S(", ");
|
retval += _S(", ");
|
||||||
|
@ -53,8 +54,13 @@ std::string PAKBridge::getLevelString() const
|
||||||
|
|
||||||
ResExtractor PAKBridge::LookupExtractor(const PAK::Entry& entry)
|
ResExtractor PAKBridge::LookupExtractor(const PAK::Entry& entry)
|
||||||
{
|
{
|
||||||
if (entry.type == Retro::STRG)
|
switch (entry.type.toUint32())
|
||||||
|
{
|
||||||
|
case SBIG('STRG'):
|
||||||
return {STRG::Extract, ".as"};
|
return {STRG::Extract, ".as"};
|
||||||
|
case SBIG('TXTR'):
|
||||||
|
return {TXTR::Extract, ".png"};
|
||||||
|
}
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -21,7 +21,7 @@ class PAKBridge
|
||||||
public:
|
public:
|
||||||
PAKBridge(HECL::Database::Project& project, const NOD::DiscBase::IPartition::Node& node);
|
PAKBridge(HECL::Database::Project& project, const NOD::DiscBase::IPartition::Node& node);
|
||||||
const std::string& getName() const {return m_node.getName();}
|
const std::string& getName() const {return m_node.getName();}
|
||||||
std::string getLevelString() const;
|
HECL::SystemString getLevelString() const;
|
||||||
bool extractResources(const HECL::ProjectPath& dirOut,
|
bool extractResources(const HECL::ProjectPath& dirOut,
|
||||||
const HECL::ProjectPath& cookedOut,
|
const HECL::ProjectPath& cookedOut,
|
||||||
bool force);
|
bool force);
|
||||||
|
|
Loading…
Reference in New Issue