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
#include <string.h> // for mem* functions
8
#if !defined(_WIN32) && !defined(macintosh)
9
#include <unistd.h>
10
#include <sys/types.h>
11
#include <sys/stat.h>
12
#include <fcntl.h>
13
#endif
14
 
15
#include "mvelib.h"
16
#include <memory>
17
 
18
static const char  MVE_HEADER[]  = "Interplay MVE File\x1A";
19
constexpr short MVE_HDRCONST1 = 0x001A;
20
constexpr short MVE_HDRCONST2 = 0x0100;
21
constexpr short MVE_HDRCONST3 = 0x1133;
22
 
23
mve_cb_Read mve_read;
24
mve_cb_Alloc mve_alloc;
25
mve_cb_Free mve_free;
26
mve_cb_ShowFrame mve_showframe;
27
mve_cb_SetPalette mve_setpalette;
28
 
29
/*
30
 * private utility functions
31
 */
32
static int16_t _mve_get_short(const unsigned char *data);
33
static uint16_t _mve_get_ushort(const unsigned char *data);
34
 
35
/*
36
 * private functions for mvefile
37
 */
38
static int _mvefile_open(MVEFILE *movie, void *stream);
39
static int  _mvefile_read_header(const MVEFILE *movie);
40
static void _mvefile_set_buffer_size(MVEFILE *movie, std::size_t buf_size);
41
static int _mvefile_fetch_next_chunk(MVEFILE *movie);
42
 
43
/*
44
 * private functions for mvestream
45
 */
46
static int _mvestream_open(MVESTREAM *movie, void *stream);
47
static void _mvestream_reset(MVESTREAM *movie);
48
 
49
/************************************************************
50
 * public MVEFILE functions
51
 ************************************************************/
52
 
53
/*
54
 * open an MVE file
55
 */
56
std::unique_ptr<MVEFILE> mvefile_open(void *stream)
57
{
58
    /* create the file */
59
        auto file = std::make_unique<MVEFILE>();
60
        if (! _mvefile_open(file.get(), stream))
61
    {
62
                return nullptr;
63
    }
64
 
65
    /* initialize the file */
66
        _mvefile_set_buffer_size(file.get(), 1024);
67
 
68
    /* verify the file's header */
69
        if (! _mvefile_read_header(file.get()))
70
    {
71
                return nullptr;
72
    }
73
 
74
    /* now, prefetch the next chunk */
75
        _mvefile_fetch_next_chunk(file.get());
76
    return file;
77
}
78
 
79
/*
80
 * reset a MVE file
81
 */
82
static void mvefile_reset(MVEFILE *file)
83
{
84
    /* initialize the file */
85
    _mvefile_set_buffer_size(file, 1024);
86
 
87
    /* verify the file's header */
88
    if (! _mvefile_read_header(file))
89
    {
90
                *file = {};
91
    }
92
 
93
    /* now, prefetch the next chunk */
94
    _mvefile_fetch_next_chunk(file);
95
}
96
 
97
static bool have_segment_header(const MVEFILE *movie)
98
{
99
        /* if nothing is cached, fail */
100
        if (movie->next_segment >= movie->cur_chunk.size())
101
                return false;
102
        /* if we don't have enough data to get a segment, fail */
103
        if (movie->cur_chunk.size() - movie->next_segment <= 4)
104
                return false;
105
        return true;
106
}
107
 
108
/*
109
 * get the size of the next segment
110
 */
111
int_fast32_t mvefile_get_next_segment_size(const MVEFILE *movie)
112
{
113
        if (!have_segment_header(movie))
114
        return -1;
115
    /* otherwise, get the data length */
116
    return _mve_get_short(&movie->cur_chunk[movie->next_segment]);
117
}
118
 
119
/*
120
 * get type of next segment in chunk (0xff if no more segments in chunk)
121
 */
122
unsigned char mvefile_get_next_segment_major(const MVEFILE *movie)
123
{
124
        if (!have_segment_header(movie))
125
        return 0xff;
126
    /* otherwise, get the data length */
127
    return movie->cur_chunk[movie->next_segment + 2];
128
}
129
 
130
/*
131
 * get subtype (version) of next segment in chunk (0xff if no more segments in
132
 * chunk)
133
 */
134
unsigned char mvefile_get_next_segment_minor(const MVEFILE *movie)
135
{
136
        if (!have_segment_header(movie))
137
        return 0xff;
138
    /* otherwise, get the data length */
139
    return movie->cur_chunk[movie->next_segment + 3];
140
}
141
 
142
/*
143
 * see next segment (return NULL if no next segment)
144
 */
145
const unsigned char *mvefile_get_next_segment(const MVEFILE *movie)
146
{
147
        if (!have_segment_header(movie))
148
        return NULL;
149
 
150
    /* otherwise, get the data length */
151
    return &movie->cur_chunk[movie->next_segment + 4];
152
}
153
 
154
/*
155
 * advance to next segment
156
 */
157
void mvefile_advance_segment(MVEFILE *movie)
158
{
159
    if (!have_segment_header(movie))
160
        return;
161
    /* else, advance to next segment */
162
    movie->next_segment +=
163
        (4 + _mve_get_ushort(&movie->cur_chunk[movie->next_segment]));
164
}
165
 
166
/*
167
 * fetch the next chunk (return 0 if at end of stream)
168
 */
169
int mvefile_fetch_next_chunk(MVEFILE *movie)
170
{
171
    return _mvefile_fetch_next_chunk(movie);
172
}
173
 
174
/************************************************************
175
 * public MVESTREAM functions
176
 ************************************************************/
177
 
178
/*
179
 * open an MVE stream
180
 */
181
MVESTREAM_ptr_t mve_open(void *stream)
182
{
183
    /* allocate */
184
        auto movie = std::make_unique<MVESTREAM>();
185
 
186
    /* open */
187
    if (! _mvestream_open(movie.get(), stream))
188
    {
189
        return nullptr;
190
    }
191
    return MVESTREAM_ptr_t(movie.release());
192
}
193
 
194
/*
195
 * reset an MVE stream
196
 */
197
void mve_reset(MVESTREAM *movie)
198
{
199
        _mvestream_reset(movie);
200
}
201
 
202
/*
203
 * set segment type handler
204
 */
205
void mve_set_handler(MVESTREAM &movie, unsigned char major, MVESEGMENTHANDLER handler)
206
{
207
    if (major < 32)
208
        movie.handlers[major] = handler;
209
}
210
 
211
/*
212
 * set segment handler context
213
 */
214
void mve_set_handler_context(MVESTREAM *movie, void *context)
215
{
216
    movie->context = context;
217
}
218
 
219
/*
220
 * play next chunk
221
 */
222
int mve_play_next_chunk(MVESTREAM &movie)
223
{
224
        const auto m = movie.movie.get();
225
    /* loop over segments */
226
        /* advance to next segment */
227
        for (;; mvefile_advance_segment(m))
228
    {
229
                const auto major = mvefile_get_next_segment_major(m);
230
                if (major == 0xff)
231
                        break;
232
                if (major >= movie.handlers.size())
233
                        continue;
234
        /* check whether to handle the segment */
235
                if (const auto handler = movie.handlers[major])
236
        {
237
                        const auto minor = mvefile_get_next_segment_minor(m);
238
                        const auto len = mvefile_get_next_segment_size(m);
239
                        const auto data = mvefile_get_next_segment(m);
240
 
241
            if (!handler(major, minor, data, len, movie.context))
242
                return 0;
243
        }
244
    }
245
 
246
        if (!mvefile_fetch_next_chunk(m))
247
        return 0;
248
 
249
    /* return status */
250
    return 1;
251
}
252
 
253
/************************************************************
254
 * private functions
255
 ************************************************************/
256
 
257
/*
258
 * allocate an MVEFILE
259
 */
260
MVEFILE::MVEFILE()
261
{
262
}
263
 
264
/*
265
 * free an MVE file
266
 */
267
MVEFILE::~MVEFILE()
268
{
269
}
270
 
271
/*
272
 * open the file stream in thie object
273
 */
274
static int _mvefile_open(MVEFILE *file, void *stream)
275
{
276
    file->stream = stream;
277
    if (! file->stream)
278
        return 0;
279
 
280
    return 1;
281
}
282
 
283
/*
284
 * read and verify the header of the recently opened file
285
 */
286
static int _mvefile_read_header(const MVEFILE *movie)
287
{
288
    unsigned char buffer[26];
289
 
290
    /* check the file is open */
291
    if (! movie->stream)
292
        return 0;
293
 
294
    /* check the file is long enough */
295
    if (! mve_read(movie->stream, buffer, 26))
296
        return 0;
297
 
298
    /* check the signature */
299
    if (memcmp(buffer, MVE_HEADER, 20))
300
        return 0;
301
 
302
    /* check the hard-coded constants */
303
    if (_mve_get_short(buffer+20) != MVE_HDRCONST1)
304
        return 0;
305
    if (_mve_get_short(buffer+22) != MVE_HDRCONST2)
306
        return 0;
307
    if (_mve_get_short(buffer+24) != MVE_HDRCONST3)
308
        return 0;
309
 
310
    return 1;
311
}
312
 
313
static void _mvefile_set_buffer_size(MVEFILE *movie, std::size_t buf_size)
314
{
315
    /* check if this would be a redundant operation */
316
        if (buf_size <= movie->cur_chunk.size())
317
        return;
318
 
319
    /* allocate new buffer */
320
    /* copy old data */
321
    /* free old buffer */
322
    /* install new buffer */
323
        movie->cur_chunk.resize(100 + buf_size);
324
}
325
 
326
static int _mvefile_fetch_next_chunk(MVEFILE *movie)
327
{
328
    unsigned char buffer[4];
329
    unsigned short length;
330
 
331
    /* fail if not open */
332
    if (! movie->stream)
333
        return 0;
334
 
335
    /* fail if we can't read the next segment descriptor */
336
    if (! mve_read(movie->stream, buffer, 4))
337
        return 0;
338
 
339
    /* pull out the next length */
340
    length = _mve_get_short(buffer);
341
 
342
    /* make sure we've got sufficient space */
343
    _mvefile_set_buffer_size(movie, length);
344
 
345
    /* read the chunk */
346
    if (! mve_read(movie->stream, &movie->cur_chunk[0], length))
347
        return 0;
348
    movie->cur_chunk.resize(length);
349
    movie->next_segment = 0;
350
 
351
    return 1;
352
}
353
 
354
static int16_t _mve_get_short(const unsigned char *data)
355
{
356
    short value;
357
    value = data[0] | (data[1] << 8);
358
    return value;
359
}
360
 
361
static uint16_t _mve_get_ushort(const unsigned char *data)
362
{
363
    unsigned short value;
364
    value = data[0] | (data[1] << 8);
365
    return value;
366
}
367
 
368
/*
369
 * allocate an MVESTREAM
370
 */
371
MVESTREAM::MVESTREAM()
372
    /* allocate and zero-initialize everything */
373
{
374
}
375
 
376
MVESTREAM::~MVESTREAM()
377
{
378
}
379
 
380
/*
381
 * open an MVESTREAM object
382
 */
383
static int _mvestream_open(MVESTREAM *movie, void *stream)
384
{
385
    movie->movie = mvefile_open(stream);
386
 
387
    return (movie->movie == NULL) ? 0 : 1;
388
}
389
 
390
/*
391
 * reset an MVESTREAM
392
 */
393
static void _mvestream_reset(MVESTREAM *movie)
394
{
395
        mvefile_reset(movie->movie.get());
396
}