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