Added a -dump-textures option (config key DumpTextures) that writes texture BMP files, one for each known format (12 in all currently)

This commit is contained in:
Bart Trzynadlowski 2022-02-01 23:15:06 +00:00
parent 646b46fd4b
commit 32933ef9b0
3 changed files with 245 additions and 9 deletions

View file

@ -1087,9 +1087,37 @@ CReal3D::~CReal3D(void)
}
else
printf("unable to dump %s\n", "texram");
Util::WriteSurfaceToBMP<Util::A1RGB5>("textures.bmp", reinterpret_cast<uint8_t *>(textureRAM), 2048, 2048, false);
#endif
// Dump textures if requested
if (m_config["DumpTextures"].ValueAsDefault<bool>(false))
{
Util::WriteSurfaceToBMP<Util::T1RGB5>("textures_t1rgb5.bmp", reinterpret_cast<uint8_t*>(textureRAM), 2048, 2048, false);
printf("Wrote textures as T1RGB5 to 'textures_t1rgb5.bmp'\n");
Util::WriteSurfaceToBMP<Util::A4L4Low>("textures_a4l4_lo.bmp", reinterpret_cast<uint8_t*>(textureRAM), 2048, 2048, false);
printf("Wrote textures as A4L4 (low) to 'textures_a4l4_lo.bmp'\n");
Util::WriteSurfaceToBMP<Util::L4A4Low>("textures_l4a4_lo.bmp", reinterpret_cast<uint8_t*>(textureRAM), 2048, 2048, false);
printf("Wrote textures as L4A4 (low) to 'textures_l4a4_lo.bmp'\n");
Util::WriteSurfaceToBMP<Util::A4L4High>("textures_a4l4_hi.bmp", reinterpret_cast<uint8_t*>(textureRAM), 2048, 2048, false);
printf("Wrote textures as A4L4 (high) to 'textures_a4l4_hi.bmp'\n");
Util::WriteSurfaceToBMP<Util::L4A4High>("textures_l4a4_hi.bmp", reinterpret_cast<uint8_t*>(textureRAM), 2048, 2048, false);
printf("Wrote textures as L4A4 (high) to 'textures_l4a4_hi.bmp'\n");
Util::WriteSurfaceToBMP<Util::L8Low>("textures_l8_lo.bmp", reinterpret_cast<uint8_t*>(textureRAM), 2048, 2048, false);
printf("Wrote textures as L8 (low) to 'textures_l8_lo.bmp'\n");
Util::WriteSurfaceToBMP<Util::L8High>("textures_l8_hi.bmp", reinterpret_cast<uint8_t*>(textureRAM), 2048, 2048, false);
printf("Wrote textures as L8 (high) to 'textures_l8_hi.bmp'\n");
Util::WriteSurfaceToBMP<Util::RGBA4>("textures_rgba4.bmp", reinterpret_cast<uint8_t*>(textureRAM), 2048, 2048, false);
printf("Wrote textures as RGBA4 to 'textures_rgba4.bmp'\n");
Util::WriteSurfaceToBMP<Util::L4Channel0>("textures_l4_0.bmp", reinterpret_cast<uint8_t*>(textureRAM), 2048, 2048, false);
printf("Wrote textures as L4 (channel 0) to 'textures_l4_0.bmp'\n");
Util::WriteSurfaceToBMP<Util::L4Channel1>("textures_l4_1.bmp", reinterpret_cast<uint8_t*>(textureRAM), 2048, 2048, false);
printf("Wrote textures as L4 (channel 1) to 'textures_l4_1.bmp'\n");
Util::WriteSurfaceToBMP<Util::L4Channel2>("textures_l4_2.bmp", reinterpret_cast<uint8_t*>(textureRAM), 2048, 2048, false);
printf("Wrote textures as L4 (channel 2) to 'textures_l4_2.bmp'\n");
Util::WriteSurfaceToBMP<Util::L4Channel3>("textures_l4_3.bmp", reinterpret_cast<uint8_t*>(textureRAM), 2048, 2048, false);
printf("Wrote textures as L4 (channel 3) to 'textures_l4_3.bmp'\n");
}
Render3D = NULL;
if (memoryPool != NULL)
{

View file

@ -1444,6 +1444,7 @@ static Util::Config::Node DefaultConfig()
config.Set("SDLConstForceThreshold", "30");
#endif
config.Set("Outputs", "none");
config.Set("DumpTextures", false);
return config;
}
@ -1529,8 +1530,9 @@ static void Help(void)
#endif
puts(" -print-inputs Prints current input configuration");
puts("");
#ifdef SUPERMODEL_DEBUGGER
puts("Debug Options:");
puts(" -dump-textures Write textures to bitmap image files on exit");
#ifdef SUPERMODEL_DEBUGGER
puts(" -disable-debugger Completely disable debugger functionality");
puts(" -enter-debugger Enter debugger at start of emulation");
puts("");
@ -1626,7 +1628,7 @@ static ParsedCommandLine ParseCommandLine(int argc, char **argv)
#endif
{ "-no-force-feedback", { "ForceFeedback", false } },
{ "-force-feedback", { "ForceFeedback", true } },
{ "-dump-textures", { "DumpTextures", true } },
};
for (int i = 1; i < argc; i++)
{

View file

@ -31,6 +31,7 @@ namespace Util
}
};
// BITMAPV4HEADER
struct BMPInfoHeader
{
uint32_t size;
@ -44,18 +45,52 @@ namespace Util
int32_t vertical_resolution;
uint32_t num_palette_colors;
uint32_t num_important_colors;
uint32_t red_mask;
uint32_t green_mask;
uint32_t blue_mask;
uint32_t alpha_mask;
uint32_t color_space;
uint32_t endpoints_red_x;
uint32_t endpoints_red_y;
uint32_t endpoints_red_z;
uint32_t endpoints_green_x;
uint32_t endpoints_green_y;
uint32_t endpoints_green_z;
uint32_t endpoints_blue_x;
uint32_t endpoints_blue_y;
uint32_t endpoints_blue_z;
uint32_t gamma_red;
uint32_t gamma_green;
uint32_t gamma_blue;
BMPInfoHeader(int32_t _width, int32_t _height)
: size(sizeof(BMPInfoHeader)),
width(_width),
height(_height),
num_planes(1),
bits_per_pixel(24),
compression_method(0),
bits_per_pixel(32),
compression_method(3), // BI_BITFIELDS
bitmap_size(_width*_height*3),
horizontal_resolution(2835), // 72 dpi
vertical_resolution(2835),
num_palette_colors(0),
num_important_colors(0)
num_important_colors(0),
red_mask(0x00ff0000),
green_mask(0x0000ff00),
blue_mask(0x000000ff),
alpha_mask(0xff000000),
color_space(1), // LCS_DEVICE_RGB
endpoints_red_x(0),
endpoints_red_y(0),
endpoints_red_z(0),
endpoints_green_x(0),
endpoints_green_y(0),
endpoints_green_z(0),
endpoints_blue_x(0),
endpoints_blue_y(0),
endpoints_blue_z(0),
gamma_red(0),
gamma_green(0),
gamma_blue(0)
{}
};
@ -93,7 +128,8 @@ namespace Util
}
};
struct A1RGB5
// Texture format 0: TRRR RRGG GGGB BBBB, T = contour bit
struct T1RGB5
{
static const unsigned bytes_per_pixel = 2;
static inline uint8_t GetRed(const uint8_t *pixel)
@ -110,10 +146,146 @@ namespace Util
}
static inline uint8_t GetAlpha(const uint8_t *pixel)
{
return uint8_t((255.0f / 1.0f) * float((*reinterpret_cast<const uint16_t *>(pixel) >> 15) & 0x1));
bool t = (*reinterpret_cast<const uint16_t*>(pixel) >> 15) & 0x1;
return t ? uint8_t(0x00) : uint8_t(0xff); // T-bit indicates transparency
}
};
// Texture format 1: xxxx xxxx AAAA LLLL
struct A4L4Low
{
static const unsigned bytes_per_pixel = 2;
static inline uint8_t GetRed(const uint8_t* pixel)
{
return uint8_t((255.0f / 15.0f) * float((*reinterpret_cast<const uint16_t*>(pixel) >> 0) & 0xf));
}
static inline uint8_t GetGreen(const uint8_t* pixel)
{
return uint8_t((255.0f / 15.0f) * float((*reinterpret_cast<const uint16_t*>(pixel) >> 0) & 0xf));
}
static inline uint8_t GetBlue(const uint8_t* pixel)
{
return uint8_t((255.0f / 15.0f) * float((*reinterpret_cast<const uint16_t*>(pixel) >> 0) & 0xf));
}
static inline uint8_t GetAlpha(const uint8_t* pixel)
{
return uint8_t((255.0f / 15.0f) * float((*reinterpret_cast<const uint16_t*>(pixel) >> 4) & 0xf));
}
};
// Texture format 2: xxxx xxxx LLLL AAAA
struct L4A4Low
{
static const unsigned bytes_per_pixel = 2;
static inline uint8_t GetRed(const uint8_t* pixel)
{
return uint8_t((255.0f / 15.0f) * float((*reinterpret_cast<const uint16_t*>(pixel) >> 4) & 0xf));
}
static inline uint8_t GetGreen(const uint8_t* pixel)
{
return uint8_t((255.0f / 15.0f) * float((*reinterpret_cast<const uint16_t*>(pixel) >> 4) & 0xf));
}
static inline uint8_t GetBlue(const uint8_t* pixel)
{
return uint8_t((255.0f / 15.0f) * float((*reinterpret_cast<const uint16_t*>(pixel) >> 4) & 0xf));
}
static inline uint8_t GetAlpha(const uint8_t* pixel)
{
return uint8_t((255.0f / 15.0f) * float((*reinterpret_cast<const uint16_t*>(pixel) >> 0) & 0xf));
}
};
// Texture format 3: AAAA LLLL xxxx xxxx
struct A4L4High
{
static const unsigned bytes_per_pixel = 2;
static inline uint8_t GetRed(const uint8_t* pixel)
{
return uint8_t((255.0f / 15.0f) * float((*reinterpret_cast<const uint16_t*>(pixel) >> 8) & 0xf));
}
static inline uint8_t GetGreen(const uint8_t* pixel)
{
return uint8_t((255.0f / 15.0f) * float((*reinterpret_cast<const uint16_t*>(pixel) >> 8) & 0xf));
}
static inline uint8_t GetBlue(const uint8_t* pixel)
{
return uint8_t((255.0f / 15.0f) * float((*reinterpret_cast<const uint16_t*>(pixel) >> 8) & 0xf));
}
static inline uint8_t GetAlpha(const uint8_t* pixel)
{
return uint8_t((255.0f / 15.0f) * float((*reinterpret_cast<const uint16_t*>(pixel) >> 12) & 0xf));
}
};
// Texture format 4: LLLL AAAA xxxx xxxx
struct L4A4High
{
static const unsigned bytes_per_pixel = 2;
static inline uint8_t GetRed(const uint8_t* pixel)
{
return uint8_t((255.0f / 15.0f) * float((*reinterpret_cast<const uint16_t*>(pixel) >> 12) & 0xf));
}
static inline uint8_t GetGreen(const uint8_t* pixel)
{
return uint8_t((255.0f / 15.0f) * float((*reinterpret_cast<const uint16_t*>(pixel) >> 12) & 0xf));
}
static inline uint8_t GetBlue(const uint8_t* pixel)
{
return uint8_t((255.0f / 15.0f) * float((*reinterpret_cast<const uint16_t*>(pixel) >> 12) & 0xf));
}
static inline uint8_t GetAlpha(const uint8_t* pixel)
{
return uint8_t((255.0f / 15.0f) * float((*reinterpret_cast<const uint16_t*>(pixel) >> 8) & 0xf));
}
};
// Texture format 5: xxxx xxxx LLLL LLLL, where L=0xff is transparent and L!=0xff is opaque
struct L8Low
{
static const unsigned bytes_per_pixel = 2;
static inline uint8_t GetRed(const uint8_t* pixel)
{
return uint8_t((*reinterpret_cast<const uint16_t*>(pixel) >> 0) & 0xff);
}
static inline uint8_t GetGreen(const uint8_t* pixel)
{
return uint8_t((*reinterpret_cast<const uint16_t*>(pixel) >> 0) & 0xff);
}
static inline uint8_t GetBlue(const uint8_t* pixel)
{
return uint8_t((*reinterpret_cast<const uint16_t*>(pixel) >> 0) & 0xff);
}
static inline uint8_t GetAlpha(const uint8_t* pixel)
{
uint8_t l = uint8_t((*reinterpret_cast<const uint16_t*>(pixel) >> 0) & 0xff);
return l == 0xff ? 0 : 0xff;
}
};
// Texture format 6: LLLL LLLL xxxx xxxx, where L=0xff is transparent and L!=0xff is opaque
struct L8High
{
static const unsigned bytes_per_pixel = 2;
static inline uint8_t GetRed(const uint8_t* pixel)
{
return uint8_t((*reinterpret_cast<const uint16_t*>(pixel) >> 8) & 0xff);
}
static inline uint8_t GetGreen(const uint8_t* pixel)
{
return uint8_t((*reinterpret_cast<const uint16_t*>(pixel) >> 8) & 0xff);
}
static inline uint8_t GetBlue(const uint8_t* pixel)
{
return uint8_t((*reinterpret_cast<const uint16_t*>(pixel) >> 8) & 0xff);
}
static inline uint8_t GetAlpha(const uint8_t* pixel)
{
uint8_t l = uint8_t((*reinterpret_cast<const uint16_t*>(pixel) >> 8) & 0xff);
return l == 0xff ? 0 : 0xff;
}
};
// Texture format 7: RRRR GGGG BBBB AAAA
struct RGBA4
{
static const unsigned bytes_per_pixel = 2;
@ -135,11 +307,43 @@ namespace Util
}
};
// Texture format 8: xxxx xxxx xxxx LLLL
// Texture format 9: xxxx xxxx LLLL xxxx
// Texture format 10: xxxx LLLL xxxx xxxx
// Texture format 11: LLLL xxxx xxxx xxxx
template <int Channel>
struct L4
{
static const unsigned bytes_per_pixel = 2;
static inline uint8_t GetRed(const uint8_t* pixel)
{
return uint8_t((255.0f / 15.0f) * float((*reinterpret_cast<const uint16_t*>(pixel) >> (Channel * 4)) & 0xf));
}
static inline uint8_t GetGreen(const uint8_t* pixel)
{
return uint8_t((255.0f / 15.0f) * float((*reinterpret_cast<const uint16_t*>(pixel) >> (Channel * 4)) & 0xf));
}
static inline uint8_t GetBlue(const uint8_t* pixel)
{
return uint8_t((255.0f / 15.0f) * float((*reinterpret_cast<const uint16_t*>(pixel) >> (Channel * 4)) & 0xf));
}
static inline uint8_t GetAlpha(const uint8_t* pixel)
{
uint8_t l = (*reinterpret_cast<const uint16_t*>(pixel) >> (Channel * 4)) & 0xf;
return l == 0x0f ? 0x00 : 0xff;
}
};
using L4Channel0 = struct L4<0>;
using L4Channel1 = struct L4<1>;
using L4Channel2 = struct L4<2>;
using L4Channel3 = struct L4<3>;
template <class SurfaceFormat>
static bool WriteSurfaceToBMP(const std::string &file_name, const uint8_t *pixels, int32_t width, int32_t height, bool flip_vertical)
{
using namespace detail;
size_t file_size = sizeof(FileHeader) + width*height*3;
size_t file_size = sizeof(FileHeader) + width*height*4;
std::shared_ptr<uint8_t> file(new uint8_t[file_size], std::default_delete<uint8_t[]>());
FileHeader *header = new (file.get()) FileHeader(width, height);
uint8_t *bmp = file.get() + sizeof(*header);
@ -153,6 +357,7 @@ namespace Util
*bmp++ = SurfaceFormat::GetBlue(src);
*bmp++ = SurfaceFormat::GetGreen(src);
*bmp++ = SurfaceFormat::GetRed(src);
*bmp++ = SurfaceFormat::GetAlpha(src);
src += SurfaceFormat::bytes_per_pixel;
}
}
@ -167,6 +372,7 @@ namespace Util
*bmp++ = SurfaceFormat::GetBlue(src);
*bmp++ = SurfaceFormat::GetGreen(src);
*bmp++ = SurfaceFormat::GetRed(src);
*bmp++ = SurfaceFormat::GetAlpha(src);
src += SurfaceFormat::bytes_per_pixel;
}
}