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-1999 PARALLAX SOFTWARE CORPORATION.  ALL RIGHTS RESERVED.
18
*/
19
 
20
/*
21
 *
22
 * Movie Playing Stuff
23
 *
24
 */
25
 
26
#include <string.h>
27
#ifndef macintosh
28
# include <sys/types.h>
29
# include <sys/stat.h>
30
# include <fcntl.h>
31
# ifndef _MSC_VER
32
#  include <unistd.h>
33
# endif
34
#endif // ! macintosh
35
#include <ctype.h>
36
 
37
#include "movie.h"
38
#include "window.h"
39
#include "console.h"
40
#include "config.h"
41
#include "physfsx.h"
42
#include "key.h"
43
#include "mouse.h"
44
#include "digi.h"
45
#include "songs.h"
46
#include "inferno.h"
47
#include "palette.h"
48
#include "strutil.h"
49
#include "dxxerror.h"
50
#include "u_mem.h"
51
#include "gr.h"
52
#include "gamefont.h"
53
#include "menu.h"
54
#include "libmve.h"
55
#include "text.h"
56
#include "screens.h"
57
#include "physfsrwops.h"
58
#if DXX_USE_OGL
59
#include "ogl_init.h"
60
#endif
61
#include "args.h"
62
 
63
#include "compiler-range_for.h"
64
#include "partial_range.h"
65
 
66
namespace {
67
 
68
// Subtitle data
69
struct subtitle {
70
        typename std::conditional<sizeof(char *) == sizeof(uint32_t), uint16_t, uint32_t>::type first_frame, last_frame;
71
        const char *msg;
72
};
73
 
74
#define MAX_ACTIVE_SUBTITLES 3
75
 
76
struct d_subtitle_state
77
{
78
        unsigned Num_subtitles = 0;
79
        std::unique_ptr<char[]> subtitle_raw_data;
80
        std::array<subtitle, 500> Subtitles;
81
};
82
 
83
static int init_subtitles(d_subtitle_state &SubtitleState, const char *filename);
84
 
85
// Movielib data
86
 
87
constexpr std::array<std::array<char, 8>, 3> movielib_files{{
88
        {"intro"}, {"other"}, {"robots"}
89
}};
90
 
91
struct loaded_movie_t
92
{
93
        std::array<char, FILENAME_LEN + 2> filename;
94
};
95
 
96
static loaded_movie_t extra_robot_movie_mission;
97
 
98
static RWops_ptr RoboFile;
99
 
100
// Function Prototypes
101
static int RunMovie(const char *filename, const char *subtitles, int highres_flag, int allow_abort,int dx,int dy);
102
 
103
static void draw_subtitles(const d_subtitle_state &, int frame_num);
104
 
105
// ----------------------------------------------------------------------
106
static void* MPlayAlloc(size_t size)
107
{
108
    return d_malloc(size);
109
}
110
 
111
static void MPlayFree(void *p)
112
{
113
    d_free(p);
114
}
115
 
116
//-----------------------------------------------------------------------
117
 
118
static unsigned int FileRead(void *handle, void *buf, unsigned int count)
119
{
120
    unsigned numread;
121
    numread = SDL_RWread(reinterpret_cast<SDL_RWops *>(handle), buf, 1, count);
122
    return (numread == count);
123
}
124
 
125
}
126
 
127
//-----------------------------------------------------------------------
128
 
129
 
130
//filename will actually get modified to be either low-res or high-res
131
//returns status.  see values in movie.h
132
int PlayMovie(const char *subtitles, const char *filename, int must_have)
133
{
134
        char name[FILENAME_LEN],*p;
135
        int ret;
136
 
137
        if (GameArg.SysNoMovies)
138
                return MOVIE_NOT_PLAYED;
139
 
140
        strcpy(name,filename);
141
 
142
        if ((p=strchr(name,'.')) == NULL)               //add extension, if missing
143
                strcat(name,".MVE");
144
 
145
        // Stop all digital sounds currently playing.
146
        digi_stop_digi_sounds();
147
 
148
        // Stop all songs
149
        songs_stop_all();
150
 
151
        // MD2211: if using SDL_Mixer, we never reinit the sound system
152
        if (CGameArg.SndDisableSdlMixer)
153
                digi_close();
154
 
155
        // Start sound
156
        MVE_sndInit(!CGameArg.SndNoSound ? 1 : -1);
157
 
158
        ret = RunMovie(name, subtitles, !GameArg.GfxSkipHiresMovie, must_have, -1, -1);
159
 
160
        // MD2211: if using SDL_Mixer, we never reinit the sound system
161
        if (!CGameArg.SndNoSound
162
                && CGameArg.SndDisableSdlMixer
163
        )
164
                digi_init();
165
 
166
        Screen_mode = -1;               //force screen reset
167
 
168
        return ret;
169
}
170
 
171
namespace {
172
 
173
static void MovieShowFrame(ubyte *buf, int dstx, int dsty, int bufw, int bufh, int sw, int sh)
174
{
175
        grs_bitmap source_bm;
176
        static palette_array_t old_pal;
177
        float scale = 1.0;
178
 
179
        if (old_pal != gr_palette)
180
        {
181
                old_pal = gr_palette;
182
                return;
183
        }
184
        old_pal = gr_palette;
185
 
186
        source_bm.bm_x = source_bm.bm_y = 0;
187
        source_bm.bm_w = source_bm.bm_rowsize = bufw;
188
        source_bm.bm_h = bufh;
189
        source_bm.set_type(bm_mode::linear);
190
        source_bm.clear_flags();
191
        source_bm.bm_data = buf;
192
 
193
        if (dstx == -1 && dsty == -1) // Fullscreen movie so set scale to fit the actual screen size
194
        {
195
                if ((static_cast<float>(SWIDTH)/SHEIGHT) < (static_cast<float>(sw)/bufh))
196
                        scale = (static_cast<float>(SWIDTH)/sw);
197
                else
198
                        scale = (static_cast<float>(SHEIGHT)/bufh);
199
        }
200
        else // Other (robot) movie so set scale to min. screen dimension
201
        {
202
                if ((static_cast<float>(SWIDTH)/bufw) < (static_cast<float>(SHEIGHT)/bufh))
203
                        scale = (static_cast<float>(SWIDTH)/sw);
204
                else
205
                        scale = (static_cast<float>(SHEIGHT)/sh);
206
        }
207
 
208
        if (dstx == -1) // center it
209
                dstx = (SWIDTH/2)-((bufw*scale)/2);
210
        if (dsty == -1) // center it
211
                dsty = (SHEIGHT/2)-((bufh*scale)/2);
212
 
213
#if DXX_USE_OGL
214
        glDisable (GL_BLEND);
215
 
216
        ogl_ubitblt_i(
217
                bufw*scale, bufh*scale,
218
                dstx, dsty,
219
                bufw, bufh, 0, 0, source_bm, grd_curcanv->cv_bitmap, (GameCfg.MovieTexFilt)?OGL_TEXFILT_TRLINEAR:OGL_TEXFILT_CLASSIC);
220
 
221
        glEnable (GL_BLEND);
222
#else
223
        gr_bm_ubitbltm(*grd_curcanv, bufw, bufh, dstx, dsty, 0, 0, source_bm);
224
#endif
225
}
226
 
227
//our routine to set the pallete, called from the movie code
228
static void MovieSetPalette(const unsigned char *p, unsigned start, unsigned count)
229
{
230
        if (count == 0)
231
                return;
232
 
233
        //Color 0 should be black, and we get color 255
234
        Assert(start>=1 && start+count-1<=254);
235
 
236
        //Set color 0 to be black
237
        gr_palette[0].r = gr_palette[0].g = gr_palette[0].b = 0;
238
 
239
        //Set color 255 to be our subtitle color
240
        gr_palette[255].r = gr_palette[255].g = gr_palette[255].b = 50;
241
 
242
        //movie libs palette into our array
243
        memcpy(&gr_palette[start],p+start*3,count*3);
244
}
245
 
246
struct movie : ignore_window_pointer_t
247
{
248
        MVE_StepStatus result;
249
        int aborted;
250
        int frame_num;
251
        int paused;
252
        MVESTREAM_ptr_t pMovie;
253
        d_subtitle_state SubtitleState;
254
};
255
 
256
static window_event_result show_pause_message(window *, const d_event &event, const unused_window_userdata_t *)
257
{
258
        window_event_result result;
259
 
260
        switch (event.type)
261
        {
262
                case EVENT_MOUSE_BUTTON_DOWN:
263
                        if (event_mouse_get_button(event) != 0)
264
                                return window_event_result::ignored;
265
                        DXX_BOOST_FALLTHROUGH;
266
                case EVENT_KEY_COMMAND:
267
                        if ((result = call_default_handler(event)) == window_event_result::ignored)
268
                        {
269
                                return window_event_result::close;
270
                        }
271
                        return result;
272
 
273
                case EVENT_WINDOW_DRAW:
274
                {
275
                        const char *msg = TXT_PAUSE;
276
                        int h;
277
                        int y;
278
 
279
                        gr_set_default_canvas();
280
                        auto &canvas = *grd_curcanv;
281
                        const auto &game_font = *GAME_FONT;
282
                        gr_get_string_size(game_font, msg, nullptr, &h, nullptr);
283
 
284
                        y = (grd_curscreen->get_screen_height() - h) / 2;
285
 
286
                        gr_set_fontcolor(canvas, 255, -1);
287
 
288
                        gr_ustring(canvas, game_font, 0x8000, y, msg);
289
                        break;
290
                }
291
 
292
                default:
293
                        break;
294
        }
295
        return window_event_result::ignored;
296
}
297
 
298
static window_event_result MovieHandler(window *, const d_event &event, movie *m)
299
{
300
        int key;
301
 
302
        switch (event.type)
303
        {
304
                case EVENT_WINDOW_ACTIVATED:
305
                        m->paused = 0;
306
                        break;
307
 
308
                case EVENT_WINDOW_DEACTIVATED:
309
                        m->paused = 1;
310
                        MVE_rmHoldMovie();
311
                        break;
312
 
313
                case EVENT_KEY_COMMAND:
314
                        key = event_key_get(event);
315
 
316
                        // If ESCAPE pressed, then quit movie.
317
                        if (key == KEY_ESC) {
318
                                m->result = MVE_StepStatus::EndOfFile;
319
                                m->aborted = 1;
320
                                return window_event_result::close;
321
                        }
322
 
323
                        // If PAUSE pressed, then pause movie
324
                        if ((key == KEY_PAUSE) || (key == KEY_COMMAND + KEY_P))
325
                        {
326
                                if (window_create(grd_curscreen->sc_canvas, 0, 0, SWIDTH, SHEIGHT, show_pause_message, unused_window_userdata))
327
                                        MVE_rmHoldMovie();
328
                                return window_event_result::handled;
329
                        }
330
                        break;
331
 
332
                case EVENT_WINDOW_DRAW:
333
                        if (!m->paused)
334
                        {
335
                                m->result = MVE_rmStepMovie(*m->pMovie.get());
336
                                if (m->result != MVE_StepStatus::Continue)
337
                                {
338
                                        return window_event_result::close;
339
                                }
340
                        }
341
 
342
                        draw_subtitles(m->SubtitleState, m->frame_num);
343
 
344
                        gr_palette_load(gr_palette);
345
 
346
                        if (!m->paused)
347
                                m->frame_num++;
348
                        break;
349
 
350
                case EVENT_WINDOW_CLOSE:
351
                        if (Quitting)
352
                        {
353
                                m->result = MVE_StepStatus::EndOfFile;
354
                                m->aborted = 1;
355
                        }
356
                        break;
357
 
358
                default:
359
                        break;
360
        }
361
        return window_event_result::ignored;
362
}
363
 
364
//returns status.  see movie.h
365
int RunMovie(const char *const filename, const char *const subtitles, const int hires_flag, const int must_have, const int dx, const int dy)
366
{
367
        movie m;
368
        int track = 0;
369
        int aborted = 0;
370
#if DXX_USE_OGL
371
        palette_array_t pal_save;
372
#endif
373
 
374
        init_subtitles(m.SubtitleState, subtitles);
375
 
376
        m.result = MVE_StepStatus::EndOfFile;
377
        m.aborted = 0;
378
        m.frame_num = 0;
379
        m.paused = 0;
380
 
381
        // Open Movie file.  If it doesn't exist, no movie, just return.
382
 
383
        auto filehndl = PHYSFSRWOPS_openRead(filename);
384
        if (!filehndl)
385
        {
386
                con_printf(must_have ? CON_URGENT : CON_VERBOSE, "Failed to open movie <%s>: %s", filename, PHYSFS_getErrorByCode(PHYSFS_getLastErrorCode()));
387
                return MOVIE_NOT_PLAYED;
388
        }
389
        const auto reshow = hide_menus();
390
        const auto wind = window_create(grd_curscreen->sc_canvas, 0, 0, SWIDTH, SHEIGHT, MovieHandler, &m);
391
        if (!wind)
392
        {
393
                if (reshow)
394
                        show_menus();
395
                return MOVIE_NOT_PLAYED;
396
        }
397
 
398
        MVE_memCallbacks(MPlayAlloc, MPlayFree);
399
        MVE_ioCallbacks(FileRead);
400
 
401
#if DXX_USE_OGL
402
        set_screen_mode(SCREEN_MOVIE);
403
        gr_copy_palette(pal_save, gr_palette);
404
        gr_palette_load(gr_palette);
405
        (void)hires_flag;
406
#else
407
        gr_set_mode(hires_flag ? screen_mode{640, 480} : screen_mode{320, 200});
408
#endif
409
        MVE_sfCallbacks(MovieShowFrame);
410
        MVE_palCallbacks(MovieSetPalette);
411
 
412
        if (MVE_rmPrepMovie(m.pMovie, filehndl, dx, dy, track)) {
413
                Int3();
414
                window_close(wind);
415
                if (reshow)
416
                        show_menus();
417
                return MOVIE_NOT_PLAYED;
418
        }
419
 
420
        MVE_sfCallbacks(MovieShowFrame);
421
        MVE_palCallbacks(MovieSetPalette);
422
 
423
        do {
424
                event_process_all();
425
        } while(window_get_front() == wind);
426
 
427
        assert(m.aborted || m.result == MVE_StepStatus::EndOfFile);      ///movie should be over
428
 
429
        m.pMovie.reset();
430
 
431
        //filehndl.reset();                           // Close Movie File
432
        if (reshow)
433
                show_menus();
434
        aborted = m.aborted;
435
 
436
        // Restore old graphic state
437
 
438
        Screen_mode=-1;  //force reset of screen mode
439
#if DXX_USE_OGL
440
        gr_copy_palette(gr_palette, pal_save);
441
        gr_palette_load(pal_save);
442
#endif
443
 
444
        return (aborted?MOVIE_ABORTED:MOVIE_PLAYED_FULL);
445
}
446
 
447
}
448
 
449
//returns 1 if frame updated ok
450
int RotateRobot(MVESTREAM_ptr_t &pMovie)
451
{
452
        auto err = MVE_rmStepMovie(*pMovie.get());
453
        gr_palette_load(gr_palette);
454
 
455
        if (err == MVE_StepStatus::EndOfFile)     //end of movie, so reset
456
        {
457
                SDL_RWseek(RoboFile, 0, SEEK_SET);
458
                if (MVE_rmPrepMovie(pMovie, RoboFile, SWIDTH/2.3, SHEIGHT/2.3, 0))
459
                {
460
                        Int3();
461
                        return 0;
462
                }
463
                err = MVE_rmStepMovie(*pMovie.get());
464
        }
465
        if (err != MVE_StepStatus::Continue)
466
        {
467
                Int3();
468
                return 0;
469
        }
470
 
471
        return 1;
472
}
473
 
474
 
475
void DeInitRobotMovie(MVESTREAM_ptr_t &pMovie)
476
{
477
        pMovie.reset();
478
        //RoboFile.reset();                           // Close Movie File
479
}
480
 
481
 
482
int InitRobotMovie(const char *filename, MVESTREAM_ptr_t &pMovie)
483
{
484
        if (GameArg.SysNoMovies)
485
                return 0;
486
 
487
        con_printf(CON_DEBUG, "RoboFile=%s", filename);
488
 
489
        MVE_memCallbacks(MPlayAlloc, MPlayFree);
490
        MVE_ioCallbacks(FileRead);
491
        MVE_sfCallbacks(MovieShowFrame);
492
        MVE_palCallbacks(MovieSetPalette);
493
        MVE_sndInit(-1);        //tell movies to play no sound for robots
494
 
495
        RoboFile = PHYSFSRWOPS_openRead(filename);
496
 
497
        if (!RoboFile)
498
        {
499
                con_printf(CON_URGENT, "Can't open movie <%s>: %s", filename, PHYSFS_getErrorByCode(PHYSFS_getLastErrorCode()));
500
                return MOVIE_NOT_PLAYED;
501
        }
502
        if (MVE_rmPrepMovie(pMovie, RoboFile, SWIDTH/2.3, SHEIGHT/2.3, 0)) {
503
                Int3();
504
                return 0;
505
        }
506
 
507
        return 1;
508
}
509
 
510
namespace {
511
 
512
/*
513
 *              Subtitle system code
514
 */
515
 
516
//search for next field following whitespace
517
static char *next_field (char *p)
518
{
519
        while (*p && !isspace(*p))
520
                p++;
521
 
522
        if (!*p)
523
                return NULL;
524
 
525
        while (*p && isspace(*p))
526
                p++;
527
 
528
        if (!*p)
529
                return NULL;
530
 
531
        return p;
532
}
533
 
534
static int init_subtitles(d_subtitle_state &SubtitleState, const char *const filename)
535
{
536
        if (!filename)
537
                return 0;
538
        int size,read_count;
539
        char *p;
540
        int have_binary = 0;
541
 
542
        SubtitleState.Num_subtitles = 0;
543
 
544
        if (!GameCfg.MovieSubtitles)
545
        {
546
                con_puts(CON_VERBOSE, "Rebirth: movie subtitles are disabled");
547
                return 0;
548
        }
549
 
550
        auto ifile = PHYSFSX_openReadBuffered(filename);                //try text version
551
 
552
        if (!ifile) {                                                           //no text version, try binary version
553
                char filename2[FILENAME_LEN];
554
                change_filename_extension(filename2, filename, ".txb");
555
                ifile = PHYSFSX_openReadBuffered(filename2);
556
                if (!ifile)
557
                {
558
                        con_printf(CON_VERBOSE, "Rebirth: skipping subtitles because cannot open \"%s\" or \"%s\"", filename, filename2);
559
                        return 0;
560
                }
561
                have_binary = 1;
562
                con_printf(CON_VERBOSE, "Rebirth: found encoded subtitles in \"%s\"", filename2);
563
        }
564
        else
565
                con_printf(CON_VERBOSE, "Rebirth: found text subtitles in \"%s\"", filename);
566
 
567
        size = PHYSFS_fileLength(ifile);
568
 
569
        const auto subtitle_raw_data = (SubtitleState.subtitle_raw_data = std::make_unique<char[]>(size + 1)).get();
570
        read_count = PHYSFS_read(ifile, subtitle_raw_data, 1, size);
571
        ifile.reset();
572
 
573
        if (read_count != size) {
574
                con_puts(CON_VERBOSE, "Rebirth: skipping subtitles because cannot read full subtitle file");
575
                return 0;
576
        }
577
 
578
        subtitle_raw_data[size] = 0;
579
        p = subtitle_raw_data;
580
 
581
        while (p && p < subtitle_raw_data+size) {
582
                char *endp;
583
 
584
                endp = strchr(p,'\n');
585
                if (endp) {
586
                        if (endp[-1] == '\r')
587
                                endp[-1] = 0;           //handle 0d0a pair
588
                        *endp = 0;                      //string termintor
589
                }
590
 
591
                if (have_binary)
592
                        decode_text_line(p);
593
 
594
                if (*p != ';') {
595
                        const auto Num_subtitles = SubtitleState.Num_subtitles;
596
                        auto &Subtitles = SubtitleState.Subtitles;
597
                        auto &s = Subtitles[SubtitleState.Num_subtitles++];
598
                        s.first_frame = atoi(p);
599
                        p = next_field(p); if (!p) continue;
600
                        s.last_frame = atoi(p);
601
                        p = next_field(p); if (!p) continue;
602
                        s.msg = p;
603
 
604
                        if (Num_subtitles)
605
                        {
606
                                assert(s.first_frame >= Subtitles[Num_subtitles - 1].first_frame);
607
                        }
608
                        assert(s.last_frame >= s.first_frame);
609
                }
610
 
611
                p = endp+1;
612
        }
613
        return 1;
614
}
615
 
616
//draw the subtitles for this frame
617
static void draw_subtitles(const d_subtitle_state &SubtitleState, const int frame_num)
618
{
619
        static int active_subtitles[MAX_ACTIVE_SUBTITLES];
620
        static int next_subtitle;
621
        static unsigned num_active_subtitles;
622
        int y;
623
        int must_erase=0;
624
 
625
        if (frame_num == 0) {
626
                num_active_subtitles = 0;
627
                next_subtitle = 0;
628
                gr_set_curfont(*grd_curcanv, GAME_FONT);
629
                gr_set_fontcolor(*grd_curcanv, 255, -1);
630
        }
631
 
632
        //get rid of any subtitles that have expired
633
        auto &Subtitles = SubtitleState.Subtitles;
634
        for (int t=0;t<num_active_subtitles;)
635
                if (frame_num > Subtitles[active_subtitles[t]].last_frame) {
636
                        int t2;
637
                        for (t2=t;t2<num_active_subtitles-1;t2++)
638
                                active_subtitles[t2] = active_subtitles[t2+1];
639
                        num_active_subtitles--;
640
                        must_erase = 1;
641
                }
642
                else
643
                        t++;
644
 
645
        //get any subtitles new for this frame
646
        while (next_subtitle < SubtitleState.Num_subtitles && frame_num >= Subtitles[next_subtitle].first_frame) {
647
                if (num_active_subtitles >= MAX_ACTIVE_SUBTITLES)
648
                        Error("Too many active subtitles!");
649
                active_subtitles[num_active_subtitles++] = next_subtitle;
650
                next_subtitle++;
651
        }
652
 
653
        //find y coordinate for first line of subtitles
654
        const auto &&line_spacing = LINE_SPACING(*grd_curcanv->cv_font, *GAME_FONT);
655
        y = grd_curcanv->cv_bitmap.bm_h - (line_spacing * (MAX_ACTIVE_SUBTITLES + 2));
656
 
657
        //erase old subtitles if necessary
658
        if (must_erase) {
659
                gr_rect(*grd_curcanv, 0,y,grd_curcanv->cv_bitmap.bm_w-1,grd_curcanv->cv_bitmap.bm_h-1, color_palette_index{0});
660
        }
661
 
662
        //now draw the current subtitles
663
        range_for (const auto &t, partial_range(active_subtitles, num_active_subtitles))
664
                if (t != -1)
665
                {
666
                        gr_string(*grd_curcanv, *grd_curcanv->cv_font, 0x8000, y, Subtitles[t].msg);
667
                        y += line_spacing;
668
                }
669
}
670
 
671
static int init_movie(const char *movielib, char resolution, int required, loaded_movie_t &movie)
672
{
673
        snprintf(&movie.filename[0], movie.filename.size(), "%s-%c.mvl", movielib, resolution);
674
        auto r = PHYSFSX_contfile_init(&movie.filename[0], 0);
675
        if (!r)
676
        {
677
                if (required || CGameArg.DbgVerbose)
678
                        con_printf(CON_URGENT, "Can't open movielib <%s>: %s", &movie.filename[0], PHYSFS_getErrorByCode(PHYSFS_getLastErrorCode()));
679
                movie.filename[0] = 0;
680
        }
681
        return r;
682
}
683
 
684
static void init_movie(const char *movielib, int required, loaded_movie_t &movie)
685
{
686
        if (!GameArg.GfxSkipHiresMovie)
687
        {
688
                if (init_movie(movielib, 'h', required, movie))
689
                        return;
690
        }
691
        init_movie(movielib, 'l', required, movie);
692
}
693
 
694
}
695
 
696
//find and initialize the movie libraries
697
void init_movies()
698
{
699
        if (GameArg.SysNoMovies)
700
                return;
701
 
702
        range_for (auto &i, movielib_files)
703
        {
704
                loaded_movie_t m;
705
                init_movie(&i[0], 1, m);
706
        }
707
}
708
 
709
void close_extra_robot_movie()
710
{
711
        const auto movielib = &extra_robot_movie_mission.filename[0];
712
        if (!*movielib)
713
                return;
714
        if (!PHYSFSX_contfile_close(movielib))
715
        {
716
                con_printf(CON_URGENT, "Can't close movielib <%s>: %s", movielib, PHYSFS_getErrorByCode(PHYSFS_getLastErrorCode()));
717
        }
718
        *movielib = 0;
719
}
720
 
721
void init_extra_robot_movie(const char *movielib)
722
{
723
        if (GameArg.SysNoMovies)
724
                return;
725
        init_movie(movielib, 0, extra_robot_movie_mission);
726
}