]> icculus.org git repositories - btb/d2x.git/blob - main/movie.c
0fde764a8f92eda5a57b022026ed9044e05986af
[btb/d2x.git] / main / movie.c
1 /* $Id: movie.c,v 1.36 2004-08-06 20:36:02 schaffner Exp $ */
2 /*
3 THE COMPUTER CODE CONTAINED HEREIN IS THE SOLE PROPERTY OF PARALLAX
4 SOFTWARE CORPORATION ("PARALLAX").  PARALLAX, IN DISTRIBUTING THE CODE TO
5 END-USERS, AND SUBJECT TO ALL OF THE TERMS AND CONDITIONS HEREIN, GRANTS A
6 ROYALTY-FREE, PERPETUAL LICENSE TO SUCH END-USERS FOR USE BY SUCH END-USERS
7 IN USING, DISPLAYING,  AND CREATING DERIVATIVE WORKS THEREOF, SO LONG AS
8 SUCH USE, DISPLAY OR CREATION IS FOR NON-COMMERCIAL, ROYALTY OR REVENUE
9 FREE PURPOSES.  IN NO EVENT SHALL THE END-USER USE THE COMPUTER CODE
10 CONTAINED HEREIN FOR REVENUE-BEARING PURPOSES.  THE END-USER UNDERSTANDS
11 AND AGREES TO THE TERMS HEREIN AND ACCEPTS THE SAME BY USE OF THIS FILE.
12 COPYRIGHT 1993-1999 PARALLAX SOFTWARE CORPORATION.  ALL RIGHTS RESERVED.
13 */
14
15 /*
16  *
17  * Movie Playing Stuff
18  *
19  */
20
21 #ifdef HAVE_CONFIG_H
22 #include <conf.h>
23 #endif
24
25 #ifdef RCS
26 static char rcsid[] = "$Id: movie.c,v 1.36 2004-08-06 20:36:02 schaffner Exp $";
27 #endif
28
29 #define DEBUG_LEVEL CON_NORMAL
30
31 #include <string.h>
32 #ifndef macintosh
33 # ifndef _WIN32_WCE
34 #  include <sys/types.h>
35 #  include <sys/stat.h>
36 #  include <fcntl.h>
37 # endif
38 # ifndef _MSC_VER
39 #  include <unistd.h>
40 # endif
41 #endif // ! macintosh
42 #include <ctype.h>
43
44 #include "movie.h"
45 #include "console.h"
46 #include "args.h"
47 #include "key.h"
48 #include "digi.h"
49 #include "songs.h"
50 #include "inferno.h"
51 #include "palette.h"
52 #include "strutil.h"
53 #include "error.h"
54 #include "u_mem.h"
55 #include "byteswap.h"
56 #include "gr.h"
57 #include "gamefont.h"
58 #include "cfile.h"
59 #include "menu.h"
60 #include "libmve.h"
61 #include "text.h"
62 #include "screens.h"
63
64 extern int MenuHiresAvailable;
65 extern char CDROM_dir[];
66
67 #define VID_PLAY 0
68 #define VID_PAUSE 1
69
70 int Vid_State;
71
72
73 // Subtitle data
74 typedef struct {
75         short first_frame,last_frame;
76         char *msg;
77 } subtitle;
78
79 #define MAX_SUBTITLES 500
80 #define MAX_ACTIVE_SUBTITLES 3
81 subtitle Subtitles[MAX_SUBTITLES];
82 int Num_subtitles;
83
84 // Movielib data
85 typedef struct {
86         char name[FILENAME_LEN];
87         int offset,len;
88 } ml_entry;
89
90 #define MLF_ON_CD    1
91 #define MAX_MOVIES_PER_LIB    50    //determines size of malloc
92
93 typedef struct {
94         char     name[100]; //[FILENAME_LEN];
95         int      n_movies;
96         ubyte    flags,pad[3];
97         ml_entry *movies;
98 } movielib;
99
100 #ifdef D2_OEM
101 char movielib_files[][FILENAME_LEN] = {"intro-l.mvl","other-l.mvl","robots-l.mvl","oem-l.mvl"};
102 #else
103 char movielib_files[][FILENAME_LEN] = {"intro-l.mvl","other-l.mvl","robots-l.mvl"};
104 #endif
105
106 #define N_BUILTIN_MOVIE_LIBS (sizeof(movielib_files)/sizeof(*movielib_files))
107 #define N_MOVIE_LIBS (N_BUILTIN_MOVIE_LIBS+1)
108 #define EXTRA_ROBOT_LIB N_BUILTIN_MOVIE_LIBS
109 movielib *movie_libs[N_MOVIE_LIBS];
110
111
112 //do we have the robot movies available
113 int robot_movies = 0; //0 means none, 1 means lowres, 2 means hires
114
115 int MovieHires = 1;   //default is highres
116
117 CFILE *RoboFile = NULL;
118 int RoboFilePos = 0;
119
120 // Function Prototypes
121 int RunMovie(char *filename, int highres_flag, int allow_abort,int dx,int dy);
122
123 CFILE *open_movie_file(char *filename, int must_have);
124 int reset_movie_file(CFILE *handle);
125
126 void change_filename_ext( char *dest, char *src, char *ext );
127 void decode_text_line(char *p);
128 void draw_subtitles(int frame_num);
129
130
131 // ----------------------------------------------------------------------
132 void* MPlayAlloc(unsigned size)
133 {
134     return d_malloc(size);
135 }
136
137 void MPlayFree(void *p)
138 {
139     d_free(p);
140 }
141
142
143 //-----------------------------------------------------------------------
144
145 unsigned int FileRead(void *handle, void *buf, unsigned int count)
146 {
147     unsigned numread;
148     numread = cfread(buf, 1, count, (CFILE *)handle);
149     return (numread == count);
150 }
151
152
153 //-----------------------------------------------------------------------
154
155
156 //filename will actually get modified to be either low-res or high-res
157 //returns status.  see values in movie.h
158 int PlayMovie(const char *filename, int must_have)
159 {
160         char name[FILENAME_LEN],*p;
161         int c, ret;
162
163 #ifndef RELEASE
164         if (FindArg("-nomovies"))
165                 return MOVIE_NOT_PLAYED;
166 #endif
167
168         strcpy(name,filename);
169
170         if ((p=strchr(name,'.')) == NULL)               //add extension, if missing
171                 strcat(name,".mve");
172
173         //check for escape already pressed & abort if so
174         while ((c=key_inkey()) != 0)
175                 if (c == KEY_ESC)
176                         return MOVIE_ABORTED;
177
178         // Stop all digital sounds currently playing.
179         digi_stop_all();
180
181         // Stop all songs
182         songs_stop_all();
183
184         digi_close();
185
186         // Start sound
187         if (!FindArg("-nosound"))
188                 MVE_sndInit(1);
189         else
190                 MVE_sndInit(-1);
191
192         ret = RunMovie(name,MovieHires,must_have,-1,-1);
193
194         if (!FindArg("-nosound"))
195                 digi_init();
196
197         Screen_mode = -1;               //force screen reset
198
199         return ret;
200 }
201
202
203 void MovieShowFrame(ubyte *buf, uint bufw, uint bufh, uint sx, uint sy,
204                                         uint w, uint h, uint dstx, uint dsty)
205 {
206         grs_bitmap source_bm;
207
208         //mprintf((0,"MovieShowFrame %d,%d  %d,%d  %d,%d  %d,%d\n",bufw,bufh,sx,sy,w,h,dstx,dsty));
209
210         Assert(bufw == w && bufh == h);
211
212         source_bm.bm_x = source_bm.bm_y = 0;
213         source_bm.bm_w = source_bm.bm_rowsize = bufw;
214         source_bm.bm_h = bufh;
215         source_bm.bm_type = BM_LINEAR;
216         source_bm.bm_flags = 0;
217         source_bm.bm_data = buf;
218
219         gr_bm_ubitblt(bufw,bufh,dstx,dsty,sx,sy,&source_bm,&grd_curcanv->cv_bitmap);
220 }
221
222 //our routine to set the pallete, called from the movie code
223 void MovieSetPalette(unsigned char *p, unsigned start, unsigned count)
224 {
225         if (count == 0)
226                 return;
227
228         //mprintf((0,"SetPalette p=%x, start=%d, count=%d\n",p,start,count));
229
230         //Color 0 should be black, and we get color 255
231         Assert(start>=1 && start+count-1<=254);
232
233         //Set color 0 to be black
234         gr_palette[0] = gr_palette[1] = gr_palette[2] = 0;
235
236         //Set color 255 to be our subtitle color
237         gr_palette[765] = gr_palette[766] = gr_palette[767] = 50;
238
239         //movie libs palette into our array
240         memcpy(gr_palette+start*3,p+start*3,count*3);
241
242         //finally set the palette in the hardware
243         //gr_palette_load(gr_palette);
244
245         //MVE_SetPalette(p, start, count);
246 }
247
248
249 #if 0
250 typedef struct bkg {
251         short x, y, w, h;           // The location of the menu.
252         grs_bitmap * bmp;               // The background under the menu.
253 } bkg;
254
255 bkg movie_bg = {0,0,0,0,NULL};
256 #endif
257
258 #define BOX_BORDER (MenuHires?40:20)
259
260
261 void show_pause_message(char *msg)
262 {
263         int w,h,aw;
264         int x,y;
265
266         gr_set_current_canvas(NULL);
267         gr_set_curfont( SMALL_FONT );
268
269         gr_get_string_size(msg,&w,&h,&aw);
270
271         x = (grd_curscreen->sc_w-w)/2;
272         y = (grd_curscreen->sc_h-h)/2;
273
274 #if 0
275         if (movie_bg.bmp) {
276                 gr_free_bitmap(movie_bg.bmp);
277                 movie_bg.bmp = NULL;
278         }
279
280         // Save the background of the display
281         movie_bg.x=x; movie_bg.y=y; movie_bg.w=w; movie_bg.h=h;
282
283         movie_bg.bmp = gr_create_bitmap( w+BOX_BORDER, h+BOX_BORDER );
284
285         gr_bm_ubitblt(w+BOX_BORDER, h+BOX_BORDER, 0, 0, x-BOX_BORDER/2, y-BOX_BORDER/2, &(grd_curcanv->cv_bitmap), movie_bg.bmp );
286 #endif
287
288         gr_setcolor(0);
289         gr_rect(x-BOX_BORDER/2,y-BOX_BORDER/2,x+w+BOX_BORDER/2-1,y+h+BOX_BORDER/2-1);
290
291         gr_set_fontcolor( 255, -1 );
292
293         gr_ustring( 0x8000, y, msg );
294
295         gr_update();
296 }
297
298 void clear_pause_message()
299 {
300 #if 0
301         if (movie_bg.bmp) {
302
303                 gr_bitmap(movie_bg.x-BOX_BORDER/2, movie_bg.y-BOX_BORDER/2, movie_bg.bmp);
304
305                 gr_free_bitmap(movie_bg.bmp);
306                 movie_bg.bmp = NULL;
307         }
308 #endif
309 }
310
311
312 //returns status.  see movie.h
313 int RunMovie(char *filename, int hires_flag, int must_have,int dx,int dy)
314 {
315         CFILE *filehndl;
316         int result=1,aborted=0;
317         int track = 0;
318         int frame_num;
319         int key;
320 #ifdef OGL
321         ubyte pal_save[768];
322 #endif
323
324         result=1;
325
326         // Open Movie file.  If it doesn't exist, no movie, just return.
327
328         filehndl = open_movie_file(filename,must_have);
329
330         if (filehndl == NULL)
331         {
332                 if (must_have)
333                         con_printf(CON_NORMAL, "movie: RunMovie: Cannot open movie <%s>\n",filename);
334                 return MOVIE_NOT_PLAYED;
335         }
336
337         MVE_memCallbacks(MPlayAlloc, MPlayFree);
338         MVE_ioCallbacks(FileRead);
339
340         if (hires_flag) {
341                 gr_set_mode(SM(640,480));
342         } else {
343                 gr_set_mode(SM(320,200));
344         }
345 #ifdef OGL
346         set_screen_mode(SCREEN_MENU);
347         gr_copy_palette(pal_save, gr_palette, 768);
348         memset(gr_palette, 0, 768);
349         gr_palette_load(gr_palette);
350 #endif
351
352 #if !defined(POLY_ACC)
353         MVE_sfCallbacks(MovieShowFrame);
354         MVE_palCallbacks(MovieSetPalette);
355 #endif
356
357         if (MVE_rmPrepMovie((void *)filehndl, dx, dy, track)) {
358                 Int3();
359                 return MOVIE_NOT_PLAYED;
360         }
361
362         frame_num = 0;
363
364         FontHires = FontHiresAvailable && hires_flag;
365
366         while((result = MVE_rmStepMovie()) == 0) {
367
368                 draw_subtitles(frame_num);
369
370                 gr_palette_load(gr_palette); // moved this here because of flashing
371
372                 gr_update();
373
374                 key = key_inkey();
375
376                 // If ESCAPE pressed, then quit movie.
377                 if (key == KEY_ESC) {
378                         result = aborted = 1;
379                         break;
380                 }
381
382                 // If PAUSE pressed, then pause movie
383                 if (key == KEY_PAUSE) {
384                         MVE_rmHoldMovie();
385                         show_pause_message(TXT_PAUSE);
386                         while (!key_inkey()) ;
387                         clear_pause_message();
388                 }
389
390 #ifdef GR_SUPPORTS_FULLSCREEN_TOGGLE
391                 if ((key == KEY_ALTED+KEY_ENTER) ||
392                     (key == KEY_ALTED+KEY_PADENTER))
393                         gr_toggle_fullscreen();
394 #endif
395
396                 frame_num++;
397         }
398
399         Assert(aborted || result == MVE_ERR_EOF);        ///movie should be over
400
401     MVE_rmEndMovie();
402
403         cfclose(filehndl);                           // Close Movie File
404
405         // Restore old graphic state
406
407         Screen_mode=-1;  //force reset of screen mode
408 #ifdef OGL
409         gr_copy_palette(gr_palette, pal_save, 768);
410         gr_palette_load(pal_save);
411 #endif
412
413         return (aborted?MOVIE_ABORTED:MOVIE_PLAYED_FULL);
414 }
415
416
417 int InitMovieBriefing()
418 {
419 #if 0
420         if (MenuHires)
421                 gr_set_mode(SM(640,480));
422         else
423                 gr_set_mode(SM(320,200));
424
425         gr_init_sub_canvas( &VR_screen_pages[0], &grd_curscreen->sc_canvas, 0, 0, grd_curscreen->sc_w, grd_curscreen->sc_h );
426         gr_init_sub_canvas( &VR_screen_pages[1], &grd_curscreen->sc_canvas, 0, 0, grd_curscreen->sc_w, grd_curscreen->sc_h );
427 #endif
428
429         return 1;
430 }
431
432
433 //returns 1 if frame updated ok
434 int RotateRobot()
435 {
436         int err;
437
438         err = MVE_rmStepMovie();
439
440         gr_palette_load(gr_palette);
441
442         if (err == MVE_ERR_EOF)     //end of movie, so reset
443         {
444                 reset_movie_file(RoboFile);
445                 if (MVE_rmPrepMovie((void *)RoboFile, MenuHires?280:140, MenuHires?200:80, 0)) {
446                         Int3();
447                         return 0;
448                 }
449         }
450         else if (err) {
451                 Int3();
452                 return 0;
453         }
454
455         return 1;
456 }
457
458
459 void DeInitRobotMovie(void)
460 {
461         MVE_rmEndMovie();
462         cfclose(RoboFile);                           // Close Movie File
463 }
464
465
466 int InitRobotMovie(char *filename)
467 {
468         if (FindArg("-nomovies"))
469                 return 0;
470
471         con_printf(DEBUG_LEVEL, "RoboFile=%s\n", filename);
472
473         MVE_sndInit(-1);        //tell movies to play no sound for robots
474
475         RoboFile = open_movie_file(filename, 1);
476
477         if (RoboFile == NULL)
478         {
479                 Warning("movie: InitRobotMovie: Cannot open movie file <%s>",filename);
480                 return MOVIE_NOT_PLAYED;
481         }
482
483         Vid_State = VID_PLAY;
484
485         if (MVE_rmPrepMovie((void *)RoboFile, MenuHires?280:140, MenuHires?200:80, 0)) {
486                 Int3();
487                 return 0;
488         }
489
490         RoboFilePos = cfseek(RoboFile, 0L, SEEK_CUR);
491
492         con_printf(DEBUG_LEVEL, "RoboFilePos=%d!\n", RoboFilePos);
493
494         return 1;
495 }
496
497
498 /*
499  *              Subtitle system code
500  */
501
502 char *subtitle_raw_data;
503
504
505 //search for next field following whitespace 
506 char *next_field (char *p)
507 {
508         while (*p && !isspace(*p))
509                 p++;
510
511         if (!*p)
512                 return NULL;
513
514         while (*p && isspace(*p))
515                 p++;
516
517         if (!*p)
518                 return NULL;
519
520         return p;
521 }
522
523
524 int init_subtitles(char *filename)
525 {
526         CFILE *ifile;
527         int size,read_count;
528         char *p;
529         int have_binary = 0;
530
531         Num_subtitles = 0;
532
533         if (! FindArg("-subtitles"))
534                 return 0;
535
536         ifile = cfopen(filename,"rb");          //try text version
537
538         if (!ifile) {                                                           //no text version, try binary version
539                 char filename2[FILENAME_LEN];
540                 change_filename_ext(filename2,filename,".TXB");
541                 ifile = cfopen(filename2,"rb");
542                 if (!ifile)
543                         return 0;
544                 have_binary = 1;
545         }
546
547         size = cfilelength(ifile);
548
549         MALLOC (subtitle_raw_data, char, size+1);
550
551         read_count = cfread(subtitle_raw_data, 1, size, ifile);
552
553         cfclose(ifile);
554
555         subtitle_raw_data[size] = 0;
556
557         if (read_count != size) {
558                 d_free(subtitle_raw_data);
559                 return 0;
560         }
561
562         p = subtitle_raw_data;
563
564         while (p && p < subtitle_raw_data+size) {
565                 char *endp;
566
567                 endp = strchr(p,'\n'); 
568                 if (endp) {
569                         if (endp[-1] == '\r')
570                                 endp[-1] = 0;           //handle 0d0a pair
571                         *endp = 0;                      //string termintor
572                 }
573
574                 if (have_binary)
575                         decode_text_line(p);
576
577                 if (*p != ';') {
578                         Subtitles[Num_subtitles].first_frame = atoi(p);
579                         p = next_field(p); if (!p) continue;
580                         Subtitles[Num_subtitles].last_frame = atoi(p);
581                         p = next_field(p); if (!p) continue;
582                         Subtitles[Num_subtitles].msg = p;
583
584                         Assert(Num_subtitles==0 || Subtitles[Num_subtitles].first_frame >= Subtitles[Num_subtitles-1].first_frame);
585                         Assert(Subtitles[Num_subtitles].last_frame >= Subtitles[Num_subtitles].first_frame);
586
587                         Num_subtitles++;
588                 }
589
590                 p = endp+1;
591
592         }
593
594         return 1;
595 }
596
597
598 void close_subtitles()
599 {
600         if (subtitle_raw_data)
601                 d_free(subtitle_raw_data);
602         subtitle_raw_data = NULL;
603         Num_subtitles = 0;
604 }
605
606
607 //draw the subtitles for this frame
608 void draw_subtitles(int frame_num)
609 {
610         static int active_subtitles[MAX_ACTIVE_SUBTITLES];
611         static int num_active_subtitles,next_subtitle,line_spacing;
612         int t,y;
613         int must_erase=0;
614
615         if (frame_num == 0) {
616                 num_active_subtitles = 0;
617                 next_subtitle = 0;
618                 gr_set_curfont( GAME_FONT );
619                 line_spacing = grd_curcanv->cv_font->ft_h + (grd_curcanv->cv_font->ft_h >> 2);
620                 gr_set_fontcolor(255,-1);
621         }
622
623         //get rid of any subtitles that have expired
624         for (t=0;t<num_active_subtitles;)
625                 if (frame_num > Subtitles[active_subtitles[t]].last_frame) {
626                         int t2;
627                         for (t2=t;t2<num_active_subtitles-1;t2++)
628                                 active_subtitles[t2] = active_subtitles[t2+1];
629                         num_active_subtitles--;
630                         must_erase = 1;
631                 }
632                 else
633                         t++;
634
635         //get any subtitles new for this frame 
636         while (next_subtitle < Num_subtitles && frame_num >= Subtitles[next_subtitle].first_frame) {
637                 if (num_active_subtitles >= MAX_ACTIVE_SUBTITLES)
638                         Error("Too many active subtitles!");
639                 active_subtitles[num_active_subtitles++] = next_subtitle;
640                 next_subtitle++;
641         }
642
643         //find y coordinate for first line of subtitles
644         y = grd_curcanv->cv_bitmap.bm_h-((line_spacing+1)*MAX_ACTIVE_SUBTITLES+2);
645
646         //erase old subtitles if necessary
647         if (must_erase) {
648                 gr_setcolor(0);
649                 gr_rect(0,y,grd_curcanv->cv_bitmap.bm_w-1,grd_curcanv->cv_bitmap.bm_h-1);
650         }
651
652         //now draw the current subtitles
653         for (t=0;t<num_active_subtitles;t++)
654                 if (active_subtitles[t] != -1) {
655                         gr_string(0x8000,y,Subtitles[active_subtitles[t]].msg);
656                         y += line_spacing+1;
657                 }
658 }
659
660
661 movielib *init_new_movie_lib(char *filename, CFILE *fp)
662 {
663         int nfiles,offset;
664         int i,n;
665         movielib *table;
666
667         //read movie file header
668
669         nfiles = cfile_read_int(fp);        //get number of files
670
671         //table = d_malloc(sizeof(*table) + sizeof(ml_entry)*nfiles);
672         MALLOC(table, movielib, 1);
673         MALLOC(table->movies, ml_entry, nfiles);
674
675         strcpy(table->name,filename);
676         table->n_movies = nfiles;
677
678         offset = 4+4+nfiles*(13+4);     //id + nfiles + nfiles * (filename + size)
679
680         for (i=0;i<nfiles;i++) {
681                 int len;
682
683                 n = cfread(table->movies[i].name, 13, 1, fp);
684                 if ( n != 1 )
685                         break;          //end of file (probably)
686
687                 len = cfile_read_int(fp);
688
689                 table->movies[i].len = len;
690                 table->movies[i].offset = offset;
691
692                 offset += table->movies[i].len;
693
694         }
695
696         cfclose(fp);
697
698         table->flags = 0;
699
700         return table;
701
702 }
703
704
705 movielib *init_old_movie_lib(char *filename, CFILE *fp)
706 {
707         int nfiles,size;
708         int i;
709         movielib *table,*table2;
710
711         nfiles = 0;
712
713         //allocate big table
714         table = d_malloc(sizeof(*table) + sizeof(ml_entry)*MAX_MOVIES_PER_LIB);
715
716         while( 1 ) {
717                 int len;
718
719                 i = cfread(table->movies[nfiles].name, 13, 1, fp);
720                 if ( i != 1 )
721                         break;          //end of file (probably)
722
723                 i = cfread(&len, 4, 1, fp);
724                 if ( i != 1 )
725                         Error("error reading movie library <%s>",filename);
726
727                 table->movies[nfiles].len = INTEL_INT(len);
728                 table->movies[nfiles].offset = cftell(fp);
729
730                 cfseek(fp, INTEL_INT(len), SEEK_CUR);       //skip data
731
732                 nfiles++;
733         }
734
735         //allocate correct-sized table
736         size = sizeof(*table) + sizeof(ml_entry)*nfiles;
737         table2 = d_malloc(size);
738         memcpy(table2,table,size);
739         d_free(table);
740         table = table2;
741
742         strcpy(table->name,filename);
743
744         table->n_movies = nfiles;
745
746         cfclose(fp);
747
748         table->flags = 0;
749
750         return table;
751
752 }
753
754
755 //find the specified movie library, and read in list of movies in it
756 movielib *init_movie_lib(char *filename)
757 {
758         //note: this based on cfile_init_hogfile()
759
760         char id[4];
761         CFILE *fp;
762
763         fp = cfopen(filename, "rb");
764
765         if ( fp == NULL )
766                 return NULL;
767
768         cfread(id, 4, 1, fp);
769         if ( !strncmp( id, "DMVL", 4 ) )
770                 return init_new_movie_lib(filename,fp);
771         else if ( !strncmp( id, "DHF", 3 ) ) {
772                 cfseek(fp,-1,SEEK_CUR);         //old file had 3 char id
773                 return init_old_movie_lib(filename,fp);
774         }
775         else {
776                 cfclose(fp);
777                 return NULL;
778         }
779 }
780
781
782 void close_movie(int i)
783 {
784         if (movie_libs[i]) {
785                 d_free(movie_libs[i]->movies);
786                 d_free(movie_libs[i]);
787         }
788 }
789
790
791 void close_movies()
792 {
793         int i;
794
795         for (i=0;i<N_MOVIE_LIBS;i++)
796                 close_movie(i);
797 }
798
799
800 //ask user to put the D2 CD in.
801 //returns -1 if ESC pressed, 0 if OK chosen
802 //CD may not have been inserted
803 int request_cd(void)
804 {
805 #if 0
806         ubyte save_pal[256*3];
807         grs_canvas *save_canv,*tcanv;
808         int ret,was_faded=gr_palette_faded_out;
809
810         gr_palette_clear();
811
812         save_canv = grd_curcanv;
813         tcanv = gr_create_canvas(grd_curcanv->cv_w,grd_curcanv->cv_h);
814
815         gr_set_current_canvas(tcanv);
816         gr_ubitmap(0,0,&save_canv->cv_bitmap);
817         gr_set_current_canvas(save_canv);
818
819         gr_clear_canvas(BM_XRGB(0,0,0));
820
821         memcpy(save_pal,gr_palette,sizeof(save_pal));
822
823         memcpy(gr_palette,last_palette_for_color_fonts,sizeof(gr_palette));
824
825  try_again:;
826
827         ret = nm_messagebox( "CD ERROR", 1, "Ok", "Please insert your Descent II CD");
828
829         if (ret == -1) {
830                 int ret2;
831
832                 ret2 = nm_messagebox( "CD ERROR", 2, "Try Again", "Leave Game", "You must insert your\nDescent II CD to Continue");
833
834                 if (ret2 == -1 || ret2 == 0)
835                         goto try_again;
836         }
837
838         force_rb_register = 1;  //disc has changed; force register new CD
839
840         gr_palette_clear();
841
842         memcpy(gr_palette,save_pal,sizeof(save_pal));
843
844         gr_ubitmap(0,0,&tcanv->cv_bitmap);
845
846         if (!was_faded)
847                 gr_palette_load(gr_palette);
848
849         gr_free_canvas(tcanv);
850
851         return ret;
852 #else
853         con_printf(DEBUG_LEVEL, "STUB: movie: request_cd\n");
854         return 0;
855 #endif
856 }
857
858
859 void init_movie(char *filename, int libnum, int is_robots, int required)
860 {
861         int high_res, try;
862         char *res = strchr(filename, '.') - 1; // 'h' == high resolution, 'l' == low
863
864 #ifndef RELEASE
865         if (FindArg("-nomovies")) {
866                 movie_libs[libnum] = NULL;
867                 return;
868         }
869 #endif
870
871         //for robots, load highres versions if highres menus set
872         if (is_robots)
873                 high_res = MenuHiresAvailable;
874         else
875                 high_res = MovieHires;
876
877         if (high_res)
878                 *res = 'h';
879
880         for (try = 0; (movie_libs[libnum] = init_movie_lib(filename)) == NULL; try++) {
881                 char name2[100];
882
883                 strcpy(name2,CDROM_dir);
884                 strcat(name2,filename);
885                 movie_libs[libnum] = init_movie_lib(name2);
886
887                 if (movie_libs[libnum] != NULL) {
888                         movie_libs[libnum]->flags |= MLF_ON_CD;
889                         break; // we found our movie on the CD
890                 } else {
891                         if (try == 0) { // first try
892                                 if (*res == 'h') { // try low res instead
893                                         *res = 'l';
894                                         high_res = 0;
895                                 } else if (*res == 'l') { // try high
896                                         *res = 'h';
897                                         high_res = 1;
898                                 } else {
899                                         if (required)
900                                                 Warning("Cannot open movie file <%s>",filename);
901                                         break;
902                                 }
903                         } else { // try == 1
904                                 if (required) {
905                                         *res = '*';
906                                         Warning("Cannot open any movie file <%s>", filename);
907                                 }
908                                 break;
909                         }
910                 }
911         }
912
913         if (is_robots && movie_libs[libnum]!=NULL)
914                 robot_movies = high_res?2:1;
915 }
916
917
918 //find and initialize the movie libraries
919 void init_movies()
920 {
921         int i;
922         int is_robots;
923
924         for (i=0;i<N_BUILTIN_MOVIE_LIBS;i++) {
925
926                 if (!strnicmp(movielib_files[i],"robot",5))
927                         is_robots = 1;
928                 else
929                         is_robots = 0;
930
931                 init_movie(movielib_files[i],i,is_robots,1);
932         }
933
934         movie_libs[EXTRA_ROBOT_LIB] = NULL;
935
936         atexit(close_movies);
937 }
938
939
940 void init_extra_robot_movie(char *filename)
941 {
942         close_movie(EXTRA_ROBOT_LIB);
943         init_movie(filename,EXTRA_ROBOT_LIB,1,0);
944 }
945
946
947 CFILE *movie_handle;
948 int movie_start;
949
950 //looks through a movie library for a movie file
951 //returns filehandle, with fileposition at movie, or -1 if can't find
952 CFILE *search_movie_lib(movielib *lib, char *filename, int must_have)
953 {
954         int i;
955         CFILE *filehandle;
956
957         if (lib == NULL)
958                 return NULL;
959
960         for (i=0;i<lib->n_movies;i++)
961                 if (!stricmp(filename,lib->movies[i].name)) {   //found the movie in a library 
962                         int from_cd;
963
964                         from_cd = (lib->flags & MLF_ON_CD);
965
966                         if (from_cd)
967                                 songs_stop_redbook();           //ready to read from CD
968
969                         do {            //keep trying until we get the file handle
970
971                                 movie_handle = filehandle = cfopen(lib->name, "rb");
972
973                                 if (must_have && from_cd && filehandle == NULL)
974                                 {   //didn't get file!
975
976                                         if (request_cd() == -1)         //ESC from requester
977                                                 break;                                          //bail from here. will get error later
978                                 }
979
980                         } while (must_have && from_cd && filehandle == NULL);
981
982                         if (filehandle)
983                                 cfseek(filehandle, (movie_start = lib->movies[i].offset), SEEK_SET);
984
985                         return filehandle;
986                 }
987
988         return NULL;
989 }
990
991
992 //returns file handle
993 CFILE *open_movie_file(char *filename, int must_have)
994 {
995         CFILE *filehandle;
996         int i;
997
998         for (i=0;i<N_MOVIE_LIBS;i++) {
999                 if ((filehandle = search_movie_lib(movie_libs[i], filename, must_have)) != NULL)
1000                         return filehandle;
1001         }
1002
1003         return NULL;    //couldn't find it
1004 }
1005
1006 //sets the file position to the start of this already-open file
1007 int reset_movie_file(CFILE *handle)
1008 {
1009         Assert(handle == movie_handle);
1010
1011         cfseek(handle, movie_start, SEEK_SET);
1012
1013         return 0;       //everything is cool
1014 }