From 32933ef9b0388e0f91f7e2a070a22d30287d41a9 Mon Sep 17 00:00:00 2001 From: Bart Trzynadlowski Date: Tue, 1 Feb 2022 23:15:06 +0000 Subject: [PATCH] Added a -dump-textures option (config key DumpTextures) that writes texture BMP files, one for each known format (12 in all currently) --- Src/Model3/Real3D.cpp | 30 +++++- Src/OSD/SDL/Main.cpp | 6 +- Src/Util/BMPFile.h | 218 ++++++++++++++++++++++++++++++++++++++++-- 3 files changed, 245 insertions(+), 9 deletions(-) diff --git a/Src/Model3/Real3D.cpp b/Src/Model3/Real3D.cpp index 125a54a..7fcffa5 100644 --- a/Src/Model3/Real3D.cpp +++ b/Src/Model3/Real3D.cpp @@ -1087,9 +1087,37 @@ CReal3D::~CReal3D(void) } else printf("unable to dump %s\n", "texram"); - Util::WriteSurfaceToBMP("textures.bmp", reinterpret_cast(textureRAM), 2048, 2048, false); #endif + // Dump textures if requested + if (m_config["DumpTextures"].ValueAsDefault(false)) + { + Util::WriteSurfaceToBMP("textures_t1rgb5.bmp", reinterpret_cast(textureRAM), 2048, 2048, false); + printf("Wrote textures as T1RGB5 to 'textures_t1rgb5.bmp'\n"); + Util::WriteSurfaceToBMP("textures_a4l4_lo.bmp", reinterpret_cast(textureRAM), 2048, 2048, false); + printf("Wrote textures as A4L4 (low) to 'textures_a4l4_lo.bmp'\n"); + Util::WriteSurfaceToBMP("textures_l4a4_lo.bmp", reinterpret_cast(textureRAM), 2048, 2048, false); + printf("Wrote textures as L4A4 (low) to 'textures_l4a4_lo.bmp'\n"); + Util::WriteSurfaceToBMP("textures_a4l4_hi.bmp", reinterpret_cast(textureRAM), 2048, 2048, false); + printf("Wrote textures as A4L4 (high) to 'textures_a4l4_hi.bmp'\n"); + Util::WriteSurfaceToBMP("textures_l4a4_hi.bmp", reinterpret_cast(textureRAM), 2048, 2048, false); + printf("Wrote textures as L4A4 (high) to 'textures_l4a4_hi.bmp'\n"); + Util::WriteSurfaceToBMP("textures_l8_lo.bmp", reinterpret_cast(textureRAM), 2048, 2048, false); + printf("Wrote textures as L8 (low) to 'textures_l8_lo.bmp'\n"); + Util::WriteSurfaceToBMP("textures_l8_hi.bmp", reinterpret_cast(textureRAM), 2048, 2048, false); + printf("Wrote textures as L8 (high) to 'textures_l8_hi.bmp'\n"); + Util::WriteSurfaceToBMP("textures_rgba4.bmp", reinterpret_cast(textureRAM), 2048, 2048, false); + printf("Wrote textures as RGBA4 to 'textures_rgba4.bmp'\n"); + Util::WriteSurfaceToBMP("textures_l4_0.bmp", reinterpret_cast(textureRAM), 2048, 2048, false); + printf("Wrote textures as L4 (channel 0) to 'textures_l4_0.bmp'\n"); + Util::WriteSurfaceToBMP("textures_l4_1.bmp", reinterpret_cast(textureRAM), 2048, 2048, false); + printf("Wrote textures as L4 (channel 1) to 'textures_l4_1.bmp'\n"); + Util::WriteSurfaceToBMP("textures_l4_2.bmp", reinterpret_cast(textureRAM), 2048, 2048, false); + printf("Wrote textures as L4 (channel 2) to 'textures_l4_2.bmp'\n"); + Util::WriteSurfaceToBMP("textures_l4_3.bmp", reinterpret_cast(textureRAM), 2048, 2048, false); + printf("Wrote textures as L4 (channel 3) to 'textures_l4_3.bmp'\n"); + } + Render3D = NULL; if (memoryPool != NULL) { diff --git a/Src/OSD/SDL/Main.cpp b/Src/OSD/SDL/Main.cpp index 18aca0b..2e01010 100644 --- a/Src/OSD/SDL/Main.cpp +++ b/Src/OSD/SDL/Main.cpp @@ -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++) { diff --git a/Src/Util/BMPFile.h b/Src/Util/BMPFile.h index aa1dc80..9bf0c4f 100644 --- a/Src/Util/BMPFile.h +++ b/Src/Util/BMPFile.h @@ -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(pixel) >> 15) & 0x1)); + bool t = (*reinterpret_cast(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(pixel) >> 0) & 0xf)); + } + static inline uint8_t GetGreen(const uint8_t* pixel) + { + return uint8_t((255.0f / 15.0f) * float((*reinterpret_cast(pixel) >> 0) & 0xf)); + } + static inline uint8_t GetBlue(const uint8_t* pixel) + { + return uint8_t((255.0f / 15.0f) * float((*reinterpret_cast(pixel) >> 0) & 0xf)); + } + static inline uint8_t GetAlpha(const uint8_t* pixel) + { + return uint8_t((255.0f / 15.0f) * float((*reinterpret_cast(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(pixel) >> 4) & 0xf)); + } + static inline uint8_t GetGreen(const uint8_t* pixel) + { + return uint8_t((255.0f / 15.0f) * float((*reinterpret_cast(pixel) >> 4) & 0xf)); + } + static inline uint8_t GetBlue(const uint8_t* pixel) + { + return uint8_t((255.0f / 15.0f) * float((*reinterpret_cast(pixel) >> 4) & 0xf)); + } + static inline uint8_t GetAlpha(const uint8_t* pixel) + { + return uint8_t((255.0f / 15.0f) * float((*reinterpret_cast(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(pixel) >> 8) & 0xf)); + } + static inline uint8_t GetGreen(const uint8_t* pixel) + { + return uint8_t((255.0f / 15.0f) * float((*reinterpret_cast(pixel) >> 8) & 0xf)); + } + static inline uint8_t GetBlue(const uint8_t* pixel) + { + return uint8_t((255.0f / 15.0f) * float((*reinterpret_cast(pixel) >> 8) & 0xf)); + } + static inline uint8_t GetAlpha(const uint8_t* pixel) + { + return uint8_t((255.0f / 15.0f) * float((*reinterpret_cast(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(pixel) >> 12) & 0xf)); + } + static inline uint8_t GetGreen(const uint8_t* pixel) + { + return uint8_t((255.0f / 15.0f) * float((*reinterpret_cast(pixel) >> 12) & 0xf)); + } + static inline uint8_t GetBlue(const uint8_t* pixel) + { + return uint8_t((255.0f / 15.0f) * float((*reinterpret_cast(pixel) >> 12) & 0xf)); + } + static inline uint8_t GetAlpha(const uint8_t* pixel) + { + return uint8_t((255.0f / 15.0f) * float((*reinterpret_cast(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(pixel) >> 0) & 0xff); + } + static inline uint8_t GetGreen(const uint8_t* pixel) + { + return uint8_t((*reinterpret_cast(pixel) >> 0) & 0xff); + } + static inline uint8_t GetBlue(const uint8_t* pixel) + { + return uint8_t((*reinterpret_cast(pixel) >> 0) & 0xff); + } + static inline uint8_t GetAlpha(const uint8_t* pixel) + { + uint8_t l = uint8_t((*reinterpret_cast(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(pixel) >> 8) & 0xff); + } + static inline uint8_t GetGreen(const uint8_t* pixel) + { + return uint8_t((*reinterpret_cast(pixel) >> 8) & 0xff); + } + static inline uint8_t GetBlue(const uint8_t* pixel) + { + return uint8_t((*reinterpret_cast(pixel) >> 8) & 0xff); + } + static inline uint8_t GetAlpha(const uint8_t* pixel) + { + uint8_t l = uint8_t((*reinterpret_cast(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 + 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(pixel) >> (Channel * 4)) & 0xf)); + } + static inline uint8_t GetGreen(const uint8_t* pixel) + { + return uint8_t((255.0f / 15.0f) * float((*reinterpret_cast(pixel) >> (Channel * 4)) & 0xf)); + } + static inline uint8_t GetBlue(const uint8_t* pixel) + { + return uint8_t((255.0f / 15.0f) * float((*reinterpret_cast(pixel) >> (Channel * 4)) & 0xf)); + } + static inline uint8_t GetAlpha(const uint8_t* pixel) + { + uint8_t l = (*reinterpret_cast(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 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 file(new uint8_t[file_size], std::default_delete()); 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; } }