Subversion Repositories Games.Descent

Rev

Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
1 pmbaty 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
}