Subversion Repositories Games.Descent

Rev

Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
1 pmbaty 1
/*
2
 * This file is part of the DXX-Rebirth project <https://www.dxx-rebirth.com/>.
3
 * It is copyright by its individual contributors, as recorded in the
4
 * project's Git history.  See COPYING.txt at the top level for license
5
 * terms and a link to the Git history.
6
 */
7
 
8
/*
9
 *
10
 * Some simple physfs extensions
11
 *
12
 */
13
 
14
#pragma once
15
 
16
#include <cstddef>
17
#include <memory>
18
#include <string.h>
19
#include <stdarg.h>
20
#include <type_traits>
21
 
22
// When PhysicsFS can *easily* be built as a framework on Mac OS X,
23
// the framework form will be supported again -kreatordxx
24
#if 1   //!(defined(__APPLE__) && defined(__MACH__))
25
#include <physfs.h>
26
#else
27
#include <physfs/physfs.h>
28
#endif
29
 
30
#include "fmtcheck.h"
31
#include "dxxsconf.h"
32
#include "dsx-ns.h"
33
#include "dxxerror.h"
34
#include "vecmat.h"
35
#include "byteutil.h"
36
 
37
#ifdef __cplusplus
38
#include <stdexcept>
39
#include "u_mem.h"
40
#include "pack.h"
41
#include "ntstring.h"
42
#include "fwd-partial_range.h"
43
#include <array>
44
#include <memory>
45
 
46
#ifdef DXX_CONSTANT_TRUE
47
#define _DXX_PHYSFS_CHECK_SIZE_CONSTANT(S,v)    DXX_CONSTANT_TRUE((S) > (v))
48
#define _DXX_PHYSFS_CHECK_SIZE(S,C,v)   _DXX_PHYSFS_CHECK_SIZE_CONSTANT(static_cast<size_t>(S) * static_cast<size_t>(C), v)
49
#define DXX_PHYSFS_CHECK_READ_SIZE_OBJECT_SIZE(S,C,v)   \
50
        (void)(__builtin_object_size(v, 1) != static_cast<size_t>(-1) && _DXX_PHYSFS_CHECK_SIZE(S,C,__builtin_object_size(v, 1)) && (DXX_ALWAYS_ERROR_FUNCTION(dxx_trap_overwrite, "read size exceeds element size"), 0))
51
#define DXX_PHYSFS_CHECK_READ_SIZE_ARRAY_SIZE(S,C)      \
52
        (void)(_DXX_PHYSFS_CHECK_SIZE(S,C,sizeof(v)) && (DXX_ALWAYS_ERROR_FUNCTION(dxx_trap_overwrite, "read size exceeds array size"), 0))
53
#define DXX_PHYSFS_CHECK_WRITE_SIZE_OBJECT_SIZE(S,C,v)  \
54
        (void)(__builtin_object_size(v, 1) != static_cast<size_t>(-1) && _DXX_PHYSFS_CHECK_SIZE(S,C,__builtin_object_size(v, 1)) && (DXX_ALWAYS_ERROR_FUNCTION(dxx_trap_overwrite, "write size exceeds element size"), 0))
55
#define DXX_PHYSFS_CHECK_WRITE_ELEMENT_SIZE_CONSTANT(S,C)       \
56
        ((void)(dxx_builtin_constant_p(S) || dxx_builtin_constant_p(C) || \
57
                (DXX_ALWAYS_ERROR_FUNCTION(dxx_trap_nonconstant_size, "array element size is not constant"), 0)))
58
#define DXX_PHYSFS_CHECK_WRITE_SIZE_ARRAY_SIZE(S,C)     \
59
        ((void)(_DXX_PHYSFS_CHECK_SIZE(S,C,sizeof(v)) && \
60
                (DXX_ALWAYS_ERROR_FUNCTION(dxx_trap_overread, "write size exceeds array size"), 0)))
61
#else
62
#define DXX_PHYSFS_CHECK_READ_SIZE_OBJECT_SIZE(S,C,v)   ((void)0)
63
#define DXX_PHYSFS_CHECK_READ_SIZE_ARRAY_SIZE(S,C)      ((void)0)
64
#define DXX_PHYSFS_CHECK_WRITE_SIZE_OBJECT_SIZE(S,C,v)  ((void)0)
65
#define DXX_PHYSFS_CHECK_WRITE_ELEMENT_SIZE_CONSTANT(S,C) ((void)0)
66
#define DXX_PHYSFS_CHECK_WRITE_SIZE_ARRAY_SIZE(S,C) ((void)0)
67
#endif
68
 
69
#define DXX_PHYSFS_CHECK_WRITE_CONSTANTS(S,C)   \
70
        ((void)(DXX_PHYSFS_CHECK_WRITE_ELEMENT_SIZE_CONSTANT(S,C),      \
71
        DXX_PHYSFS_CHECK_WRITE_SIZE_ARRAY_SIZE(S,C), 0))        \
72
 
73
namespace dcx {
74
 
75
template <typename V>
76
__attribute_always_inline()
77
static inline PHYSFS_sint64 PHYSFSX_check_read(PHYSFS_File *file, V *v, const PHYSFS_uint32 S, const PHYSFS_uint32 C)
78
{
79
        if constexpr (std::is_integral<V>::value)
80
                static_assert(std::is_pod<V>::value, "non-POD integral value read");
81
        else
82
                static_assert(std::is_pod<V>::value, "non-POD non-integral value read");
83
        DXX_PHYSFS_CHECK_READ_SIZE_OBJECT_SIZE(S, C, v);
84
        return (S == 0 ? 0 : PHYSFS_readBytes(file, v, S * C) / S); // Pierre-Marie Baty -- work around PHYSFS_read() deprecation
85
}
86
 
87
template <typename V, std::size_t N>
88
__attribute_always_inline()
89
static inline PHYSFS_sint64 PHYSFSX_check_read(PHYSFS_File *file, std::array<V, N> &v, PHYSFS_uint32 S, PHYSFS_uint32 C)
90
{
91
        static_assert(std::is_pod<V>::value, "C++ array of non-POD elements read");
92
        DXX_PHYSFS_CHECK_READ_SIZE_ARRAY_SIZE(S, C);
93
        return PHYSFSX_check_read(file, &v[0], S, C);
94
}
95
 
96
template <typename V, typename D>
97
__attribute_always_inline()
98
static inline PHYSFS_sint64 PHYSFSX_check_read(PHYSFS_File *file, const std::unique_ptr<V, D> &v, PHYSFS_uint32 S, PHYSFS_uint32 C)
99
{
100
        return PHYSFSX_check_read(file, v.get(), S, C);
101
}
102
 
103
template <typename V>
104
__attribute_always_inline()
105
static inline PHYSFS_sint64 PHYSFSX_check_write(PHYSFS_File *file, const V *v, const PHYSFS_uint32 S, const PHYSFS_uint32 C)
106
{
107
        if constexpr (std::is_integral<V>::value)
108
        {
109
                static_assert(std::is_pod<V>::value, "non-POD integral value written");
110
                DXX_PHYSFS_CHECK_WRITE_ELEMENT_SIZE_CONSTANT(S,C);
111
        }
112
        else
113
                static_assert(std::is_pod<V>::value, "non-POD non-integral value written");
114
        DXX_PHYSFS_CHECK_WRITE_SIZE_OBJECT_SIZE(S, C, v);
115
        return (S == 0 ? 0 : PHYSFS_writeBytes(file, v, S * C) / S); // Pierre-Marie Baty -- work around PHYSFS_write() deprecation
116
}
117
 
118
template <typename V, std::size_t N>
119
__attribute_always_inline()
120
static inline PHYSFS_sint64 PHYSFSX_check_write(PHYSFS_File *file, const std::array<V, N> &v, PHYSFS_uint32 S, PHYSFS_uint32 C)
121
{
122
        static_assert(std::is_pod<V>::value, "C++ array of non-POD elements written");
123
        DXX_PHYSFS_CHECK_WRITE_CONSTANTS(S,C);
124
        return PHYSFSX_check_write(file, &v[0], S, C);
125
}
126
 
127
template <typename T, typename D>
128
__attribute_always_inline()
129
static inline PHYSFS_sint64 PHYSFSX_check_write(PHYSFS_File *file, const std::unique_ptr<T, D> &p, PHYSFS_uint32 S, PHYSFS_uint32 C)
130
{
131
        return PHYSFSX_check_write(file, p.get(), S, C);
132
}
133
 
134
template <typename V>
135
PHYSFS_sint64 PHYSFSX_check_read(PHYSFS_File *file, exact_type<V> v, PHYSFS_uint32 S, PHYSFS_uint32 C) = delete;
136
template <typename V>
137
PHYSFS_sint64 PHYSFSX_check_write(PHYSFS_File *file, exact_type<V> v, PHYSFS_uint32 S, PHYSFS_uint32 C) = delete;
138
 
139
template <typename V>
140
PHYSFS_sint64 PHYSFSX_check_read(PHYSFS_File *file, V **v, PHYSFS_uint32 S, PHYSFS_uint32 C) = delete;
141
template <typename V>
142
PHYSFS_sint64 PHYSFSX_check_write(PHYSFS_File *file, V **v, PHYSFS_uint32 S, PHYSFS_uint32 C) = delete;
143
#define PHYSFS_read(F,V,S,C)    PHYSFSX_check_read(F,V,S,C)
144
#define PHYSFS_write(F,V,S,C)   PHYSFSX_check_write(F,V,S,C)
145
 
146
static inline PHYSFS_sint16 PHYSFSX_readSXE16(PHYSFS_File *file, int swap)
147
{
148
        PHYSFS_sint16 val;
149
 
150
        PHYSFS_read(file, &val, sizeof(val), 1);
151
 
152
        return swap ? SWAPSHORT(val) : val;
153
}
154
 
155
static inline PHYSFS_sint32 PHYSFSX_readSXE32(PHYSFS_File *file, int swap)
156
{
157
        PHYSFS_sint32 val;
158
 
159
        PHYSFS_read(file, &val, sizeof(val), 1);
160
 
161
        return swap ? SWAPINT(val) : val;
162
}
163
 
164
static inline int PHYSFSX_writeU8(PHYSFS_File *file, PHYSFS_uint8 val)
165
{
166
        return PHYSFS_write(file, &val, 1, 1);
167
}
168
 
169
static inline int PHYSFSX_writeString(PHYSFS_File *file, const char *s)
170
{
171
        return PHYSFS_write(file, s, 1, strlen(s) + 1);
172
}
173
 
174
static inline int PHYSFSX_puts(PHYSFS_File *file, const char *s, size_t len) __attribute_nonnull();
175
static inline int PHYSFSX_puts(PHYSFS_File *file, const char *s, size_t len)
176
{
177
        return PHYSFS_write(file, s, 1, len);
178
}
179
 
180
template <size_t len>
181
static inline int PHYSFSX_puts_literal(PHYSFS_File *file, const char (&s)[len]) __attribute_nonnull();
182
template <size_t len>
183
static inline int PHYSFSX_puts_literal(PHYSFS_File *file, const char (&s)[len])
184
{
185
        return PHYSFSX_puts(file, s, len - 1);
186
}
187
#define PHYSFSX_puts(A1,S,...)  (PHYSFSX_puts(A1,S, _dxx_call_puts_parameter2(1, ## __VA_ARGS__, strlen(S))))
188
 
189
static inline int PHYSFSX_fgetc(PHYSFS_File *const fp)
190
{
191
        unsigned char c;
192
 
193
        if (PHYSFS_read(fp, &c, 1, 1) != 1)
194
                return EOF;
195
 
196
        return c;
197
}
198
 
199
static inline int PHYSFSX_fseek(PHYSFS_File *fp, long int offset, int where)
200
{
201
        int c, goal_position;
202
 
203
        switch(where)
204
        {
205
        case SEEK_SET:
206
                goal_position = offset;
207
                break;
208
        case SEEK_CUR:
209
                goal_position = PHYSFS_tell(fp) + offset;
210
                break;
211
        case SEEK_END:
212
                goal_position = PHYSFS_fileLength(fp) + offset;
213
                break;
214
        default:
215
                return 1;
216
        }
217
        c = PHYSFS_seek(fp, goal_position);
218
        return !c;
219
}
220
 
221
template <std::size_t N>
222
struct PHYSFSX_gets_line_t
223
{
224
        PHYSFSX_gets_line_t() = default;
225
        PHYSFSX_gets_line_t(const PHYSFSX_gets_line_t &) = delete;
226
        PHYSFSX_gets_line_t &operator=(const PHYSFSX_gets_line_t &) = delete;
227
        PHYSFSX_gets_line_t(PHYSFSX_gets_line_t &&) = default;
228
        PHYSFSX_gets_line_t &operator=(PHYSFSX_gets_line_t &&) = default;
229
        using line_t = std::array<char, N>;
230
#if DXX_HAVE_POISON
231
        /* Force onto heap to improve checker accuracy */
232
        std::unique_ptr<line_t> m_line;
233
        const line_t &line() const { return *m_line.get(); }
234
        line_t &line() { return *m_line.get(); }
235
        line_t &next()
236
        {
237
                m_line = std::make_unique<line_t>();
238
                return *m_line.get();
239
        }
240
#else
241
        line_t m_line;
242
        const line_t &line() const { return m_line; }
243
        line_t &line() { return m_line; }
244
        line_t &next() { return m_line; }
245
#endif
246
        operator line_t &() { return line(); }
247
        operator const line_t &() const { return line(); }
248
        operator char *() { return line().data(); }
249
        operator const char *() const { return line().data(); }
250
        typename line_t::reference operator[](typename line_t::size_type i) { return line()[i]; }
251
        typename line_t::reference operator[](int i) { return operator[](static_cast<typename line_t::size_type>(i)); }
252
        typename line_t::const_reference operator[](typename line_t::size_type i) const { return line()[i]; }
253
        typename line_t::const_reference operator[](int i) const { return operator[](static_cast<typename line_t::size_type>(i)); }
254
        constexpr std::size_t size() const { return N; }
255
        typename line_t::const_iterator begin() const { return line().begin(); }
256
        typename line_t::const_iterator end() const { return line().end(); }
257
};
258
 
259
template <>
260
struct PHYSFSX_gets_line_t<0>
261
{
262
#define DXX_ALLOCATE_PHYSFS_LINE(n)     std::make_unique<char[]>(n)
263
        std::unique_ptr<char[]> m_line;
264
        std::size_t m_length;
265
        PHYSFSX_gets_line_t(std::size_t n) :
266
#if !DXX_HAVE_POISON
267
                m_line(DXX_ALLOCATE_PHYSFS_LINE(n)),
268
#endif
269
                m_length(n)
270
        {
271
        }
272
        char *line() { return m_line.get(); }
273
        const char *line() const { return m_line.get(); }
274
        char *next()
275
        {
276
#if DXX_HAVE_POISON
277
                /* Reallocate to tell checker to undefine the buffer */
278
                m_line = DXX_ALLOCATE_PHYSFS_LINE(m_length);
279
#endif
280
                return m_line.get();
281
        }
282
        std::size_t size() const { return m_length; }
283
        operator const char *() const { return m_line.get(); }
284
        const char *begin() const { return *this; }
285
        const char *end() const { return begin() + m_length; }
286
        operator const void *() const = delete;
287
#undef DXX_ALLOCATE_PHYSFS_LINE
288
};
289
 
290
class PHYSFSX_fgets_t
291
{
292
        static char *get(char *buf, std::size_t n, PHYSFS_File *const fp);
293
        static char *get(char *buf, std::size_t offset, std::size_t n, PHYSFS_File *const fp)
294
        {
295
                if (offset > n)
296
                        throw std::invalid_argument("offset too large");
297
                return get(&buf[offset], n - offset, fp);
298
        }
299
public:
300
        template <std::size_t n>
301
                __attribute_nonnull()
302
                char *operator()(PHYSFSX_gets_line_t<n> &buf, PHYSFS_File *const fp, std::size_t offset = 0) const
303
                {
304
                        return get(&buf.next()[0], offset, buf.size(), fp);
305
                }
306
        template <std::size_t n>
307
                __attribute_nonnull()
308
                char *operator()(ntstring<n> &buf, PHYSFS_File *const fp, std::size_t offset = 0) const
309
                {
310
                        auto r = get(&buf.data()[0], offset, buf.size(), fp);
311
                        buf.back() = 0;
312
                        return r;
313
                }
314
};
315
 
316
constexpr PHYSFSX_fgets_t PHYSFSX_fgets{};
317
 
318
static inline int PHYSFSX_printf(PHYSFS_File *file, const char *format, ...) __attribute_format_printf(2, 3);
319
static inline int PHYSFSX_printf(PHYSFS_File *file, const char *format, ...)
320
#define PHYSFSX_printf(A1,F,...)        dxx_call_printf_checked(PHYSFSX_printf,PHYSFSX_puts_literal,(A1),(F),##__VA_ARGS__)
321
{
322
        char buffer[1024];
323
        va_list args;
324
 
325
        va_start(args, format);
326
        size_t len = vsnprintf(buffer, sizeof(buffer), format, args);
327
        va_end(args);
328
 
329
        return PHYSFSX_puts(file, buffer, len);
330
}
331
 
332
#define PHYSFSX_writeFix        PHYSFS_writeSLE32
333
#define PHYSFSX_writeFixAng     PHYSFS_writeSLE16
334
 
335
static inline int PHYSFSX_writeVector(PHYSFS_File *file, const vms_vector &v)
336
{
337
        if (PHYSFSX_writeFix(file, v.x) < 1 ||
338
                PHYSFSX_writeFix(file, v.y) < 1 ||
339
                PHYSFSX_writeFix(file, v.z) < 1)
340
                return 0;
341
 
342
        return 1;
343
}
344
 
345
__attribute_cold
346
__attribute_noreturn
347
void PHYSFSX_read_helper_report_error(const char *const filename, const unsigned line, const char *const func, PHYSFS_File *const file);
348
 
349
template <typename T, int (*F)(PHYSFS_File *, T *)>
350
static T PHYSFSX_read_helper(const char *const filename, const unsigned line, const char *const func, PHYSFS_File *const file)
351
{
352
        T i;
353
        if (!F(file, &i))
354
                PHYSFSX_read_helper_report_error(filename, line, func, file);
355
        return i;
356
}
357
 
358
template <typename T1, int (*F)(PHYSFS_File *, T1 *), typename T2, T1 T2::*m1, T1 T2::*m2, T1 T2::*m3>
359
static void PHYSFSX_read_sequence_helper(const char *const filename, const unsigned line, const char *const func, PHYSFS_File *const file, T2 *const i)
360
{
361
        if (unlikely(!F(file, &(i->*m1)) ||
362
                !F(file, &(i->*m2)) ||
363
                !F(file, &(i->*m3))))
364
                PHYSFSX_read_helper_report_error(filename, line, func, file);
365
}
366
 
367
static inline int PHYSFSX_readS8(PHYSFS_File *const file, int8_t *const b)
368
{
369
        return (PHYSFS_read(file, b, sizeof(*b), 1) == 1);
370
}
371
 
372
#define PHYSFSX_readByte(F)     (PHYSFSX_read_helper<int8_t, PHYSFSX_readS8>(__FILE__, __LINE__, __func__, (F)))
373
#define PHYSFSX_readShort(F)    (PHYSFSX_read_helper<int16_t, PHYSFS_readSLE16>(__FILE__, __LINE__, __func__, (F)))
374
#define PHYSFSX_readInt(F)      (PHYSFSX_read_helper<int32_t, PHYSFS_readSLE32>(__FILE__, __LINE__, __func__, (F)))
375
#define PHYSFSX_readFix(F)      (PHYSFSX_read_helper<fix, PHYSFS_readSLE32>(__FILE__, __LINE__, __func__, (F)))
376
#define PHYSFSX_readFixAng(F)   (PHYSFSX_read_helper<fixang, PHYSFS_readSLE16>(__FILE__, __LINE__, __func__, (F)))
377
#define PHYSFSX_readVector(F,V) (PHYSFSX_read_sequence_helper<fix, PHYSFS_readSLE32, vms_vector, &vms_vector::x, &vms_vector::y, &vms_vector::z>(__FILE__, __LINE__, __func__, (F), &(V)))
378
#define PHYSFSX_readAngleVec(V,F)       (PHYSFSX_read_sequence_helper<fixang, PHYSFS_readSLE16, vms_angvec, &vms_angvec::p, &vms_angvec::b, &vms_angvec::h>(__FILE__, __LINE__, __func__, (F), (V)))
379
 
380
static inline void PHYSFSX_readMatrix(const char *const filename, const unsigned line, const char *const func, vms_matrix *const m, PHYSFS_File *const file)
381
{
382
        auto &PHYSFSX_readVector = PHYSFSX_read_sequence_helper<fix, PHYSFS_readSLE32, vms_vector, &vms_vector::x, &vms_vector::y, &vms_vector::z>;
383
        (PHYSFSX_readVector)(filename, line, func, file, &m->rvec);
384
        (PHYSFSX_readVector)(filename, line, func, file, &m->uvec);
385
        (PHYSFSX_readVector)(filename, line, func, file, &m->fvec);
386
}
387
 
388
#define PHYSFSX_readMatrix(M,F) ((PHYSFSX_readMatrix)(__FILE__, __LINE__, __func__, (M), (F)))
389
 
390
#define PHYSFSX_contfile_init PHYSFSX_addRelToSearchPath
391
#define PHYSFSX_contfile_close PHYSFSX_removeRelFromSearchPath
392
 
393
class PHYSFS_File_deleter
394
{
395
public:
396
        int operator()(PHYSFS_File *fp) const
397
        {
398
                return PHYSFS_close(fp);
399
        }
400
};
401
 
402
class RAIIPHYSFS_File : public std::unique_ptr<PHYSFS_File, PHYSFS_File_deleter>
403
{
404
        typedef std::unique_ptr<PHYSFS_File, PHYSFS_File_deleter> base_t;
405
public:
406
        DXX_INHERIT_CONSTRUCTORS(RAIIPHYSFS_File, base_t);
407
        using base_t::operator bool;
408
        operator PHYSFS_File *() const && = delete;
409
        operator PHYSFS_File *() const &
410
        {
411
                return get();
412
        }
413
        int close()
414
        {
415
                /* Like reset(), but returns result */
416
                int r = get_deleter()(get());
417
                if (r)
418
                        release();
419
                return r;
420
        }
421
        template <typename T>
422
                bool operator==(T) const = delete;
423
        template <typename T>
424
                bool operator!=(T) const = delete;
425
};
426
 
427
typedef char file_extension_t[5];
428
__attribute_nonnull()
429
__attribute_warn_unused_result
430
int PHYSFSX_checkMatchingExtension(const char *filename, const partial_range_t<const file_extension_t *>);
431
 
432
template <std::size_t count>
433
__attribute_nonnull()
434
__attribute_warn_unused_result
435
static inline int PHYSFSX_checkMatchingExtension(const std::array<file_extension_t, count> &exts, const char *filename)
436
{
437
        return PHYSFSX_checkMatchingExtension(filename, exts);
438
}
439
 
440
extern int PHYSFSX_addRelToSearchPath(const char *relname, int add_to_end);
441
extern int PHYSFSX_removeRelFromSearchPath(const char *relname);
442
extern int PHYSFSX_fsize(const char *hogname);
443
extern void PHYSFSX_listSearchPathContent();
444
int PHYSFSX_getRealPath(const char *stdPath, char *realPath, std::size_t);
445
 
446
template <std::size_t N>
447
static inline int PHYSFSX_getRealPath(const char *stdPath, std::array<char, N> &realPath)
448
{
449
        return PHYSFSX_getRealPath(stdPath, realPath.data(), N);
450
}
451
 
452
extern int PHYSFSX_isNewPath(const char *path);
453
extern int PHYSFSX_rename(const char *oldpath, const char *newpath);
454
 
455
#define PHYSFSX_exists(F,I)     ((I) ? PHYSFSX_exists_ignorecase(F) : PHYSFS_exists(F))
456
int PHYSFSX_exists_ignorecase(const char *filename);
457
RAIIPHYSFS_File PHYSFSX_openReadBuffered(const char *filename);
458
RAIIPHYSFS_File PHYSFSX_openWriteBuffered(const char *filename);
459
extern void PHYSFSX_addArchiveContent();
460
extern void PHYSFSX_removeArchiveContent();
461
}
462
 
463
#ifdef dsx
464
namespace dsx {
465
 
466
bool PHYSFSX_init(int argc, char *argv[]);
467
int PHYSFSX_checkSupportedArchiveTypes();
468
 
469
}
470
#endif
471
 
472
#endif