Subversion Repositories Games.Descent

Rev

Blame | Last modification | View Log | Download | RSS feed

  1. /*
  2.  * Portions of this file are copyright Rebirth contributors and licensed as
  3.  * described in COPYING.txt.
  4.  * Portions of this file are copyright Parallax Software and licensed
  5.  * according to the Parallax license below.
  6.  * See COPYING.txt for license details.
  7.  
  8. THE COMPUTER CODE CONTAINED HEREIN IS THE SOLE PROPERTY OF PARALLAX
  9. SOFTWARE CORPORATION ("PARALLAX").  PARALLAX, IN DISTRIBUTING THE CODE TO
  10. END-USERS, AND SUBJECT TO ALL OF THE TERMS AND CONDITIONS HEREIN, GRANTS A
  11. ROYALTY-FREE, PERPETUAL LICENSE TO SUCH END-USERS FOR USE BY SUCH END-USERS
  12. IN USING, DISPLAYING,  AND CREATING DERIVATIVE WORKS THEREOF, SO LONG AS
  13. SUCH USE, DISPLAY OR CREATION IS FOR NON-COMMERCIAL, ROYALTY OR REVENUE
  14. FREE PURPOSES.  IN NO EVENT SHALL THE END-USER USE THE COMPUTER CODE
  15. CONTAINED HEREIN FOR REVENUE-BEARING PURPOSES.  THE END-USER UNDERSTANDS
  16. AND AGREES TO THE TERMS HEREIN AND ACCEPTS THE SAME BY USE OF THIS FILE.
  17. COPYRIGHT 1993-1998 PARALLAX SOFTWARE CORPORATION.  ALL RIGHTS RESERVED.
  18. */
  19. /*
  20.  *
  21.  * Routines to read/write pcx images.
  22.  *
  23.  */
  24.  
  25. #include <stdlib.h>
  26. #include <stdio.h>
  27. #include <string.h>
  28.  
  29. #include "gr.h"
  30. #include "grdef.h"
  31. #include "u_mem.h"
  32. #include "pcx.h"
  33. #include "physfsx.h"
  34. #include "palette.h"
  35.  
  36. #include "dxxsconf.h"
  37. #include "dsx-ns.h"
  38. #include "compiler-poison.h"
  39. #include "compiler-range_for.h"
  40. #include "d_range.h"
  41. #include "partial_range.h"
  42. #include "console.h"
  43.  
  44. #if DXX_USE_SDLIMAGE
  45. #include "physfsrwops.h"
  46. #include <SDL_image.h>
  47. #endif
  48.  
  49. namespace dcx {
  50.  
  51. namespace {
  52.  
  53. #if DXX_USE_SDLIMAGE
  54. struct RAII_SDL_Surface
  55. {
  56.         struct deleter
  57.         {
  58.                 void operator()(SDL_Surface *s) const
  59.                 {
  60.                         SDL_FreeSurface(s);
  61.                 }
  62.         };
  63.         std::unique_ptr<SDL_Surface, deleter> surface;
  64.         explicit RAII_SDL_Surface(SDL_Surface *const s) :
  65.                 surface(s)
  66.         {
  67.         }
  68. };
  69. #endif
  70.  
  71. }
  72.  
  73. #if !DXX_USE_OGL && DXX_USE_SCREENSHOT_FORMAT_LEGACY
  74. static int pcx_encode_byte(ubyte byt, ubyte cnt, PHYSFS_File *fid);
  75. static int pcx_encode_line(const uint8_t *inBuff, uint_fast32_t inLen, PHYSFS_File *fp);
  76.  
  77. /* PCX Header data type */
  78. struct PCXHeader
  79. {
  80.         ubyte   Manufacturer;
  81.         ubyte   Version;
  82.         ubyte   Encoding;
  83.         ubyte   BitsPerPixel;
  84.         short   Xmin;
  85.         short   Ymin;
  86.         short   Xmax;
  87.         short   Ymax;
  88.         short   Hdpi;
  89.         short   Vdpi;
  90.         ubyte   ColorMap[16][3];
  91.         ubyte   Reserved;
  92.         ubyte   Nplanes;
  93.         short   BytesPerLine;
  94.         ubyte   filler[60];
  95. } __pack__;
  96. #endif
  97.  
  98. #define PCXHEADER_SIZE 128
  99.  
  100. #if DXX_USE_SDLIMAGE
  101. static pcx_result pcx_read_bitmap(const char *const filename, grs_main_bitmap &bmp, palette_array_t &palette, RWops_ptr rw)
  102. {
  103.         RAII_SDL_Surface surface(IMG_LoadPCX_RW(/*rw.get()*/rw)); // Pierre-Marie Baty
  104.         SDL_RWclose(rw); // Pierre-Marie Baty
  105.         if (!surface.surface)
  106.         {
  107.                 con_printf(CON_NORMAL, "%s:%u: failed to create surface from \"%s\"", __FILE__, __LINE__, filename);
  108.                 return pcx_result::ERROR_OPENING;
  109.         }
  110.         const auto &s = *surface.surface.get();
  111.         const auto fmt = s.format;
  112.         if (!fmt || fmt->BitsPerPixel != 8)
  113.                 return pcx_result::ERROR_WRONG_VERSION;
  114.         const auto fpal = fmt->palette;
  115.         if (!fpal || fpal->ncolors != palette.size())
  116.                 return pcx_result::ERROR_NO_PALETTE;
  117.         const unsigned xsize = s.w;
  118.         const unsigned ysize = s.h;
  119.         if (xsize > 3840)
  120.                 return pcx_result::ERROR_MEMORY;
  121.         if (ysize > 2400)
  122.                 return pcx_result::ERROR_MEMORY;
  123.         DXX_CHECK_MEM_IS_DEFINED(s.pixels, xsize * ysize);
  124.         gr_init_bitmap_alloc(bmp, bm_mode::linear, 0, 0, xsize, ysize, xsize);
  125.         std::copy_n(reinterpret_cast<const uint8_t *>(s.pixels), xsize * ysize, &bmp.get_bitmap_data()[0]);
  126.         {
  127.                 const auto a = [](const SDL_Color &c) {
  128.                         return rgb_t{
  129.                                 static_cast<uint8_t>(c.r >> 2),
  130.                                 static_cast<uint8_t>(c.g >> 2),
  131.                                 static_cast<uint8_t>(c.b >> 2)
  132.                         };
  133.                 };
  134.                 std::transform(fpal->colors, fpal->colors + palette.size(), palette.begin(), a);
  135.         }
  136.         return pcx_result::SUCCESS;
  137. }
  138. #else
  139. static pcx_result pcx_read_blank(const char *const filename, grs_main_bitmap &bmp, palette_array_t &palette)
  140. {
  141.         con_printf(CON_NORMAL, "%s:%u: PCX support disabled at compile time; cannot read file \"%s\"", __FILE__, __LINE__, filename);
  142.         constexpr unsigned xsize = 640;
  143.         constexpr unsigned ysize = 480;
  144.         gr_init_bitmap_alloc(bmp, bm_mode::linear, 0, 0, xsize, ysize, xsize);
  145.         auto &bitmap_data = *reinterpret_cast<uint8_t (*)[ysize][xsize]>(bmp.get_bitmap_data());
  146.         constexpr uint8_t border = 1;
  147.         constexpr uint8_t body = 0;
  148.         std::fill(&bitmap_data[0][0], &bitmap_data[2][0], border);
  149.         std::fill(&bitmap_data[2][0], &bitmap_data[ysize - 2][0], body);
  150.         std::fill(&bitmap_data[ysize - 2][0], &bitmap_data[ysize][0], border);
  151.         for (auto &&y : xrange(2u, ysize - 2))
  152.         {
  153.                 bitmap_data[y][0] = border;
  154.                 bitmap_data[y][1] = border;
  155.                 bitmap_data[y][xsize - 2] = border;
  156.                 bitmap_data[y][xsize - 1] = border;
  157.         }
  158.         palette = {};
  159.         palette[border] = {63, 63, 63};
  160.         return pcx_result::SUCCESS;
  161. }
  162. #endif
  163.  
  164. }
  165.  
  166. #if defined(DXX_BUILD_DESCENT_I)
  167. namespace dsx {
  168.  
  169. #if DXX_USE_SDLIMAGE
  170. static std::pair<std::unique_ptr<uint8_t[]>, std::size_t> load_physfs_blob(const char *const filename)
  171. {
  172.         RAIIPHYSFS_File file(PHYSFSX_openReadBuffered(filename));
  173.         if (!file)
  174.         {
  175.                 con_printf(CON_VERBOSE, "%s:%u: failed to open \"%s\"", __FILE__, __LINE__, filename);
  176.                 return {};
  177.         }
  178.         const std::size_t fsize = PHYSFS_fileLength(file);
  179.         if (fsize > 0x100000)
  180.         {
  181.                 con_printf(CON_VERBOSE, "%s:%u: file too large \"%s\"", __FILE__, __LINE__, filename);
  182.                 return {};
  183.         }
  184.         auto encoded_buffer = std::make_unique<uint8_t[]>(fsize);
  185.         if (PHYSFS_read(file, encoded_buffer.get(), 1, fsize) < fsize)
  186.         {
  187.                 con_printf(CON_VERBOSE, "%s:%u: failed to read \"%s\"", __FILE__, __LINE__, filename);
  188.                 return {};
  189.         }
  190.         return {std::move(encoded_buffer), fsize};
  191. }
  192.  
  193. static std::pair<std::unique_ptr<uint8_t[]>, std::size_t> load_decoded_physfs_blob(const char *const filename)
  194. {
  195.         auto encoded_blob = load_physfs_blob(filename);
  196.         auto &&[encoded_buffer, fsize] = encoded_blob;
  197.         if (!encoded_buffer)
  198.                 return encoded_blob;
  199.         const std::size_t data_size = fsize - 1;
  200.         auto &&transform_predicate = [xor_value = encoded_buffer[data_size]](uint8_t c) mutable {
  201.                 return c ^ --xor_value;
  202.         };
  203.         auto &&decoded_buffer = std::make_unique<uint8_t[]>(data_size);
  204.         const auto b = encoded_buffer.get();
  205.         std::transform(std::make_reverse_iterator(std::next(b, data_size)), std::make_reverse_iterator(b), decoded_buffer.get(), transform_predicate);
  206.         return {std::move(decoded_buffer), data_size};
  207. }
  208. #endif
  209.  
  210. pcx_result bald_guy_load(const char *const filename, grs_main_bitmap &bmp, palette_array_t &palette)
  211. {
  212. #if DXX_USE_SDLIMAGE
  213.         const auto &&[bguy_data, data_size] = load_decoded_physfs_blob(filename);
  214.         if (!bguy_data)
  215.                 return pcx_result::ERROR_OPENING;
  216.  
  217.         RWops_ptr rw(SDL_RWFromConstMem(bguy_data.get(), data_size));
  218.         return pcx_read_bitmap(filename, bmp, palette, std::move(rw));
  219. #else
  220.         return pcx_read_blank(filename, bmp, palette);
  221. #endif
  222. }
  223.  
  224. }
  225. #endif
  226.  
  227. namespace dcx {
  228.  
  229. pcx_result pcx_read_bitmap(const char *const filename, grs_main_bitmap &bmp, palette_array_t &palette)
  230. {
  231. #if DXX_USE_SDLIMAGE
  232.         auto rw = PHYSFSRWOPS_openRead(filename);
  233.         if (!rw)
  234.         {
  235.                 con_printf(CON_NORMAL, "%s:%u: failed to open \"%s\"", __FILE__, __LINE__, filename);
  236.                 return pcx_result::ERROR_OPENING;
  237.         }
  238.         return pcx_read_bitmap(filename, bmp, palette, std::move(rw));
  239. #else
  240.         return pcx_read_blank(filename, bmp, palette);
  241. #endif
  242. }
  243.  
  244. #if !DXX_USE_OGL && DXX_USE_SCREENSHOT_FORMAT_LEGACY
  245. pcx_result pcx_write_bitmap(PHYSFS_File *const PCXfile, const grs_bitmap *const bmp, palette_array_t &palette)
  246. {
  247.         int retval;
  248.         ubyte data;
  249.         PCXHeader header{};
  250.  
  251.         header.Manufacturer = 10;
  252.         header.Encoding = 1;
  253.         header.Nplanes = 1;
  254.         header.BitsPerPixel = 8;
  255.         header.Version = 5;
  256.         header.Xmax = bmp->bm_w-1;
  257.         header.Ymax = bmp->bm_h-1;
  258.         header.BytesPerLine = bmp->bm_w;
  259.  
  260.         if (PHYSFS_write(PCXfile, &header, PCXHEADER_SIZE, 1) != 1)
  261.         {
  262.                 return pcx_result::ERROR_WRITING;
  263.         }
  264.  
  265.         {
  266.                 const uint_fast32_t bm_w = bmp->bm_w;
  267.                 const uint_fast32_t bm_rowsize = bmp->bm_rowsize;
  268.                 const auto bm_data = bmp->get_bitmap_data();
  269.                 const auto e = &bm_data[bm_rowsize * bmp->bm_h];
  270.                 for (auto i = &bm_data[0]; i != e; i += bm_rowsize)
  271.                 {
  272.                         if (!pcx_encode_line(i, bm_w, PCXfile))
  273.                         {
  274.                         return pcx_result::ERROR_WRITING;
  275.                         }
  276.                 }
  277.         }
  278.  
  279.         // Mark an extended palette
  280.         data = 12;
  281.         if (PHYSFS_write(PCXfile, &data, 1, 1) != 1)
  282.         {
  283.                 return pcx_result::ERROR_WRITING;
  284.         }
  285.  
  286.         retval = PHYSFS_write(PCXfile, &palette[0], sizeof(palette), 1);
  287.         if (retval !=1) {
  288.                 return pcx_result::ERROR_WRITING;
  289.         }
  290.         return pcx_result::SUCCESS;
  291. }
  292.  
  293. // returns number of bytes written into outBuff, 0 if failed
  294. int pcx_encode_line(const uint8_t *inBuff, uint_fast32_t inLen, PHYSFS_File *fp)
  295. {
  296.         ubyte last;
  297.         int i;
  298.         int total;
  299.         ubyte runCount;         // max single runlength is 63
  300.         total = 0;
  301.         last = *(inBuff);
  302.         runCount = 1;
  303.  
  304.         range_for (const auto ub, unchecked_partial_range(inBuff, 1u, inLen))
  305.         {
  306.                 if (ub == last) {
  307.                         runCount++;                     // it encodes
  308.                         if (runCount == 63)     {
  309.                                 if (!(i=pcx_encode_byte(last, runCount, fp)))
  310.                                         return(0);
  311.                                 total += i;
  312.                                 runCount = 0;
  313.                         }
  314.                 } else {        // this != last
  315.                         if (runCount)   {
  316.                                 if (!(i=pcx_encode_byte(last, runCount, fp)))
  317.                                         return(0);
  318.                                 total += i;
  319.                         }
  320.                         last = ub;
  321.                         runCount = 1;
  322.                 }
  323.         }
  324.  
  325.         if (runCount)   {               // finish up
  326.                 if (!(i=pcx_encode_byte(last, runCount, fp)))
  327.                         return 0;
  328.                 return total + i;
  329.         }
  330.         return total;
  331. }
  332.  
  333. static inline int PHYSFSX_putc(PHYSFS_File *file, uint8_t ch)
  334. {
  335.         if (PHYSFS_write(file, &ch, 1, 1) < 1)
  336.                 return -1;
  337.         else
  338.                 return ch;
  339. }
  340.  
  341. // subroutine for writing an encoded byte pair
  342. // returns count of bytes written, 0 if error
  343. int pcx_encode_byte(ubyte byt, ubyte cnt, PHYSFS_File *fid)
  344. {
  345.         if (cnt) {
  346.                 if ( (cnt==1) && (0xc0 != (0xc0 & byt)) )       {
  347.                         if(EOF == PHYSFSX_putc(fid, static_cast<int>(byt)))
  348.                                 return 0;       // disk write error (probably full)
  349.                         return 1;
  350.                 } else {
  351.                         if(EOF == PHYSFSX_putc(fid, 0xC0 | cnt))
  352.                                 return 0;       // disk write error
  353.                         if(EOF == PHYSFSX_putc(fid, static_cast<int>(byt)))
  354.                                 return 0;       // disk write error
  355.                         return 2;
  356.                 }
  357.         }
  358.         return 0;
  359. }
  360. #endif
  361.  
  362. //text for error messges
  363. constexpr char pcx_error_messages[] = {
  364.         "No error.\0"
  365.         "Error opening file.\0"
  366.         "Could not read PCX header.\0"
  367.         "Unsupported PCX version.\0"
  368.         "Error reading data.\0"
  369.         "Could not find palette information.\0"
  370.         "Error writing data.\0"
  371. };
  372.  
  373.  
  374. //function to return pointer to error message
  375. const char *pcx_errormsg(const pcx_result r)
  376. {
  377.         const char *p = pcx_error_messages;
  378.  
  379.         unsigned error_number = static_cast<unsigned>(r);
  380.         while (error_number--) {
  381.                 if (p == std::end(pcx_error_messages))
  382.                         return nullptr;
  383.                 p += strlen(p)+1;
  384.         }
  385.  
  386.         return p;
  387. }
  388.  
  389. }
  390.