]> icculus.org git repositories - btb/d2x.git/blob - main/movie.c
Fixed many opengl glitches
[btb/d2x.git] / main / movie.c
1 /* $Id: movie.c,v 1.23 2003-02-28 09:56:10 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.23 2003-02-28 09:56:10 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 = 0;   //default for now is lores
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
242         result=1;
243
244         // Open Movie file.  If it doesn't exist, no movie, just return.
245
246         filehndl = open_movie_file(filename,must_have);
247
248         if (filehndl == -1) {
249                 if (must_have)
250                         Warning("movie: RunMovie: Cannot open movie <%s>\n",filename);
251                 return MOVIE_NOT_PLAYED;
252         }
253
254         if (hires_flag) {
255                 gr_set_mode(SM(640,480));
256         } else {
257                 gr_set_mode(SM(320,200));
258         }
259 #ifdef OGL
260         set_screen_mode(SCREEN_MENU);
261 #endif
262
263         if (MVE_rmPrepMovie(filehndl, dx, dy, track)) {
264                 Int3();
265                 return MOVIE_NOT_PLAYED;
266         }
267
268         frame_num = 0;
269
270         FontHires = FontHiresAvailable && hires_flag;
271
272         while((result = MVE_rmStepMovie()) == 0) {
273
274                 draw_subtitles(frame_num);
275
276                 gr_update();
277
278                 key = key_inkey();
279
280                 // If ESCAPE pressed, then quit movie.
281                 if (key == KEY_ESC) {
282                         result = aborted = 1;
283                         break;
284                 }
285
286                 // If PAUSE pressed, then pause movie
287                 if (key == KEY_PAUSE) {
288                         MVE_rmHoldMovie();
289                         show_pause_message(TXT_PAUSE);
290                         while (!key_inkey()) ;
291                         clear_pause_message();
292                 }
293
294 #ifdef GR_SUPPORTS_FULLSCREEN_TOGGLE
295                 if ((key == KEY_CTRLED+KEY_SHIFTED+KEY_PADENTER) ||
296                         (key == KEY_ALTED+KEY_CTRLED+KEY_PADENTER) ||
297                         (key == KEY_ALTED+KEY_SHIFTED+KEY_PADENTER))
298                         gr_toggle_fullscreen();
299 #endif
300
301                 frame_num++;
302         }
303
304         Assert(aborted || result == MVE_ERR_EOF);        ///movie should be over
305
306     MVE_rmEndMovie();
307
308         close(filehndl);                           // Close Movie File
309
310         // Restore old graphic state
311
312         Screen_mode=-1;  //force reset of screen mode
313
314         return (aborted?MOVIE_ABORTED:MOVIE_PLAYED_FULL);
315 }
316
317
318 int InitMovieBriefing()
319 {
320 #if 0
321         if (MenuHires)
322                 gr_set_mode(SM(640,480));
323         else
324                 gr_set_mode(SM(320,200));
325
326         gr_init_sub_canvas( &VR_screen_pages[0], &grd_curscreen->sc_canvas, 0, 0, grd_curscreen->sc_w, grd_curscreen->sc_h );
327         gr_init_sub_canvas( &VR_screen_pages[1], &grd_curscreen->sc_canvas, 0, 0, grd_curscreen->sc_w, grd_curscreen->sc_h );
328 #endif
329
330         return 1;
331 }
332
333
334 //returns 1 if frame updated ok
335 int RotateRobot()
336 {
337         int err;
338
339         err = MVE_rmStepMovie();
340
341         if (err == MVE_ERR_EOF)     //end of movie, so reset
342         {
343                 reset_movie_file(RoboFile);
344                 if (MVE_rmPrepMovie(RoboFile, MenuHires?280:140, MenuHires?200:80, 0)) {
345                         Int3();
346                         return 0;
347                 }
348         }
349         else if (err) {
350                 Int3();
351                 return 0;
352         }
353
354         return 1;
355 }
356
357
358 void DeInitRobotMovie(void)
359 {
360         MVE_rmEndMovie();
361         close(RoboFile);                           // Close Movie File
362 }
363
364
365 int InitRobotMovie(char *filename)
366 {
367         if (FindArg("-nomovies"))
368                 return 0;
369
370         con_printf(DEBUG_LEVEL, "RoboFile=%s\n", filename);
371
372         MVE_sndInit(-1);        //tell movies to play no sound for robots
373
374         RoboFile = open_movie_file(filename, 1);
375
376         if (RoboFile == -1) {
377                 Warning("movie: InitRobotMovie: Cannot open movie file <%s>",filename);
378                 return MOVIE_NOT_PLAYED;
379         }
380
381         Vid_State = VID_PLAY;
382
383         if (MVE_rmPrepMovie(RoboFile, MenuHires?280:140, MenuHires?200:80, 0)) {
384                 Int3();
385                 return 0;
386         }
387
388         RoboFilePos=lseek (RoboFile,0L,SEEK_CUR);
389
390         con_printf(DEBUG_LEVEL, "RoboFilePos=%d!\n", RoboFilePos);
391
392         return 1;
393 }
394
395
396 /*
397  *              Subtitle system code
398  */
399
400 ubyte *subtitle_raw_data;
401
402
403 //search for next field following whitespace 
404 ubyte *next_field(ubyte *p)
405 {
406         while (*p && !isspace(*p))
407                 p++;
408
409         if (!*p)
410                 return NULL;
411
412         while (*p && isspace(*p))
413                 p++;
414
415         if (!*p)
416                 return NULL;
417
418         return p;
419 }
420
421
422 int init_subtitles(char *filename)
423 {
424         CFILE *ifile;
425         int size,read_count;
426         ubyte *p;
427         int have_binary = 0;
428
429         Num_subtitles = 0;
430
431         if (! FindArg("-subtitles"))
432                 return 0;
433
434         ifile = cfopen(filename,"rb");          //try text version
435
436         if (!ifile) {                                                           //no text version, try binary version
437                 char filename2[FILENAME_LEN];
438                 change_filename_ext(filename2,filename,".TXB");
439                 ifile = cfopen(filename2,"rb");
440                 if (!ifile)
441                         return 0;
442                 have_binary = 1;
443         }
444
445         size = cfilelength(ifile);
446
447         MALLOC (subtitle_raw_data, ubyte, size+1);
448
449         read_count = cfread(subtitle_raw_data, 1, size, ifile);
450
451         cfclose(ifile);
452
453         subtitle_raw_data[size] = 0;
454
455         if (read_count != size) {
456                 d_free(subtitle_raw_data);
457                 return 0;
458         }
459
460         p = subtitle_raw_data;
461
462         while (p && p < subtitle_raw_data+size) {
463                 char *endp;
464
465                 endp = strchr(p,'\n'); 
466                 if (endp) {
467                         if (endp[-1] == '\r')
468                                 endp[-1] = 0;           //handle 0d0a pair
469                         *endp = 0;                      //string termintor
470                 }
471
472                 if (have_binary)
473                         decode_text_line(p);
474
475                 if (*p != ';') {
476                         Subtitles[Num_subtitles].first_frame = atoi(p);
477                         p = next_field(p); if (!p) continue;
478                         Subtitles[Num_subtitles].last_frame = atoi(p);
479                         p = next_field(p); if (!p) continue;
480                         Subtitles[Num_subtitles].msg = p;
481
482                         Assert(Num_subtitles==0 || Subtitles[Num_subtitles].first_frame >= Subtitles[Num_subtitles-1].first_frame);
483                         Assert(Subtitles[Num_subtitles].last_frame >= Subtitles[Num_subtitles].first_frame);
484
485                         Num_subtitles++;
486                 }
487
488                 p = endp+1;
489
490         }
491
492         return 1;
493 }
494
495
496 void close_subtitles()
497 {
498         if (subtitle_raw_data)
499                 d_free(subtitle_raw_data);
500         subtitle_raw_data = NULL;
501         Num_subtitles = 0;
502 }
503
504
505 //draw the subtitles for this frame
506 void draw_subtitles(int frame_num)
507 {
508         static int active_subtitles[MAX_ACTIVE_SUBTITLES];
509         static int num_active_subtitles,next_subtitle,line_spacing;
510         int t,y;
511         int must_erase=0;
512
513         if (frame_num == 0) {
514                 num_active_subtitles = 0;
515                 next_subtitle = 0;
516                 gr_set_curfont( GAME_FONT );
517                 line_spacing = grd_curcanv->cv_font->ft_h + (grd_curcanv->cv_font->ft_h >> 2);
518                 gr_set_fontcolor(255,-1);
519         }
520
521         //get rid of any subtitles that have expired
522         for (t=0;t<num_active_subtitles;)
523                 if (frame_num > Subtitles[active_subtitles[t]].last_frame) {
524                         int t2;
525                         for (t2=t;t2<num_active_subtitles-1;t2++)
526                                 active_subtitles[t2] = active_subtitles[t2+1];
527                         num_active_subtitles--;
528                         must_erase = 1;
529                 }
530                 else
531                         t++;
532
533         //get any subtitles new for this frame 
534         while (next_subtitle < Num_subtitles && frame_num >= Subtitles[next_subtitle].first_frame) {
535                 if (num_active_subtitles >= MAX_ACTIVE_SUBTITLES)
536                         Error("Too many active subtitles!");
537                 active_subtitles[num_active_subtitles++] = next_subtitle;
538                 next_subtitle++;
539         }
540
541         //find y coordinate for first line of subtitles
542         y = grd_curcanv->cv_bitmap.bm_h-((line_spacing+1)*MAX_ACTIVE_SUBTITLES+2);
543
544         //erase old subtitles if necessary
545         if (must_erase) {
546                 gr_setcolor(0);
547                 gr_rect(0,y,grd_curcanv->cv_bitmap.bm_w-1,grd_curcanv->cv_bitmap.bm_h-1);
548         }
549
550         //now draw the current subtitles
551         for (t=0;t<num_active_subtitles;t++)
552                 if (active_subtitles[t] != -1) {
553                         gr_string(0x8000,y,Subtitles[active_subtitles[t]].msg);
554                         y += line_spacing+1;
555                 }
556 }
557
558
559 movielib *init_new_movie_lib(char *filename,FILE *fp)
560 {
561         int nfiles,offset;
562         int i,n;
563         movielib *table;
564
565         //read movie file header
566
567         nfiles = file_read_int(fp);             //get number of files
568
569         //table = d_malloc(sizeof(*table) + sizeof(ml_entry)*nfiles);
570         MALLOC(table, movielib, 1);
571         MALLOC(table->movies, ml_entry, nfiles);
572
573         strcpy(table->name,filename);
574         table->n_movies = nfiles;
575
576         offset = 4+4+nfiles*(13+4);     //id + nfiles + nfiles * (filename + size)
577
578         for (i=0;i<nfiles;i++) {
579                 int len;
580
581                 n = fread( table->movies[i].name, 13, 1, fp );
582                 if ( n != 1 )
583                         break;          //end of file (probably)
584
585                 len = file_read_int(fp);
586
587                 table->movies[i].len = len;
588                 table->movies[i].offset = offset;
589
590                 offset += table->movies[i].len;
591
592         }
593
594         fclose(fp);
595
596         table->flags = 0;
597
598         return table;
599
600 }
601
602
603 movielib *init_old_movie_lib(char *filename,FILE *fp)
604 {
605         int nfiles,size;
606         int i;
607         movielib *table,*table2;
608
609         nfiles = 0;
610
611         //allocate big table
612         table = d_malloc(sizeof(*table) + sizeof(ml_entry)*MAX_MOVIES_PER_LIB);
613
614         while( 1 ) {
615                 int len;
616
617                 i = fread( table->movies[nfiles].name, 13, 1, fp );
618                 if ( i != 1 )
619                         break;          //end of file (probably)
620
621                 i = fread( &len, 4, 1, fp );
622                 if ( i != 1 )
623                         Error("error reading movie library <%s>",filename);
624
625                 table->movies[nfiles].len = INTEL_INT(len);
626                 table->movies[nfiles].offset = ftell( fp );
627
628                 fseek( fp, INTEL_INT(len), SEEK_CUR );          //skip data
629
630                 nfiles++;
631         }
632
633         //allocate correct-sized table
634         size = sizeof(*table) + sizeof(ml_entry)*nfiles;
635         table2 = d_malloc(size);
636         memcpy(table2,table,size);
637         d_free(table);
638         table = table2;
639
640         strcpy(table->name,filename);
641
642         table->n_movies = nfiles;
643
644         fclose(fp);
645
646         table->flags = 0;
647
648         return table;
649
650 }
651
652
653 //find the specified movie library, and read in list of movies in it
654 movielib *init_movie_lib(char *filename)
655 {
656         //note: this based on cfile_init_hogfile()
657
658         char id[4];
659         FILE * fp;
660
661         fp = fopen( filename, "rb" );
662
663         if ((fp == NULL) && (AltHogdir_initialized)) {
664                 char temp[128];
665                 strcpy(temp, AltHogDir);
666                 strcat(temp, "/");
667                 strcat(temp, filename);
668                 fp = fopen(temp, "rb");
669         }
670
671         if ( fp == NULL )
672                 return NULL;
673
674         fread( id, 4, 1, fp );
675         if ( !strncmp( id, "DMVL", 4 ) )
676                 return init_new_movie_lib(filename,fp);
677         else if ( !strncmp( id, "DHF", 3 ) ) {
678                 fseek(fp,-1,SEEK_CUR);          //old file had 3 char id
679                 return init_old_movie_lib(filename,fp);
680         }
681         else {
682                 fclose(fp);
683                 return NULL;
684         }
685 }
686
687
688 void close_movie(int i)
689 {
690         if (movie_libs[i]) {
691                 d_free(movie_libs[i]->movies);
692                 d_free(movie_libs[i]);
693         }
694 }
695
696
697 void close_movies()
698 {
699         int i;
700
701         for (i=0;i<N_MOVIE_LIBS;i++)
702                 close_movie(i);
703 }
704
705
706 //ask user to put the D2 CD in.
707 //returns -1 if ESC pressed, 0 if OK chosen
708 //CD may not have been inserted
709 int request_cd(void)
710 {
711 #if 0
712         ubyte save_pal[256*3];
713         grs_canvas *save_canv,*tcanv;
714         int ret,was_faded=gr_palette_faded_out;
715
716         gr_palette_clear();
717
718         save_canv = grd_curcanv;
719         tcanv = gr_create_canvas(grd_curcanv->cv_w,grd_curcanv->cv_h);
720
721         gr_set_current_canvas(tcanv);
722         gr_ubitmap(0,0,&save_canv->cv_bitmap);
723         gr_set_current_canvas(save_canv);
724
725         gr_clear_canvas(BM_XRGB(0,0,0));
726
727         memcpy(save_pal,gr_palette,sizeof(save_pal));
728
729         memcpy(gr_palette,last_palette_for_color_fonts,sizeof(gr_palette));
730
731  try_again:;
732
733         ret = nm_messagebox( "CD ERROR", 1, "Ok", "Please insert your Descent II CD");
734
735         if (ret == -1) {
736                 int ret2;
737
738                 ret2 = nm_messagebox( "CD ERROR", 2, "Try Again", "Leave Game", "You must insert your\nDescent II CD to Continue");
739
740                 if (ret2 == -1 || ret2 == 0)
741                         goto try_again;
742         }
743
744         force_rb_register = 1;  //disc has changed; force register new CD
745
746         gr_palette_clear();
747
748         memcpy(gr_palette,save_pal,sizeof(save_pal));
749
750         gr_ubitmap(0,0,&tcanv->cv_bitmap);
751
752         if (!was_faded)
753                 gr_palette_load(gr_palette);
754
755         gr_free_canvas(tcanv);
756
757         return ret;
758 #else
759         con_printf(DEBUG_LEVEL, "STUB: movie: request_cd\n");
760         return 0;
761 #endif
762 }
763
764
765 void init_movie(char *filename,int libnum,int is_robots,int required)
766 {
767         int high_res;
768         int try = 0;
769
770 #ifndef RELEASE
771         if (FindArg("-nomovies")) {
772                 movie_libs[libnum] = NULL;
773                 return;
774         }
775 #endif
776
777         //for robots, load highres versions if highres menus set
778         if (is_robots)
779                 high_res = MenuHiresAvailable;
780         else
781                 high_res = MovieHires;
782
783         if (high_res)
784                 strchr(filename,'.')[-1] = 'h';
785
786 try_again:;
787
788         if ((movie_libs[libnum] = init_movie_lib(filename)) == NULL) {
789                 char name2[100];
790
791                 strcpy(name2,CDROM_dir);
792                 strcat(name2,filename);
793                 movie_libs[libnum] = init_movie_lib(name2);
794
795                 if (movie_libs[libnum] != NULL)
796                         movie_libs[libnum]->flags |= MLF_ON_CD;
797                 else {
798                         if (required) {
799                                 Warning("Cannot open movie file <%s>\n",filename);
800                         }
801
802                         if (!try) {                                         // first try
803                                 if (strchr(filename, '.')[-1] == 'h') {         // try again with lowres
804                                         strchr(filename, '.')[-1] = 'l';
805                                         high_res = 0;
806                                         Warning("Trying to open movie file <%s> instead\n", filename);
807                                         try++;
808                                         goto try_again;
809                                 } else if (strchr(filename, '.')[-1] == 'l') {  // try again with highres
810                                         strchr(filename, '.')[-1] = 'h';
811                                         high_res = 1;
812                                         Warning("Trying to open movie file <%s> instead\n", filename);
813                                         try++;
814                                         goto try_again;
815                                 }
816                         }
817                 }
818         }
819
820         if (is_robots && movie_libs[libnum]!=NULL)
821                 robot_movies = high_res?2:1;
822 }
823
824
825 //find and initialize the movie libraries
826 void init_movies()
827 {
828         int i;
829         int is_robots;
830
831         for (i=0;i<N_BUILTIN_MOVIE_LIBS;i++) {
832
833                 if (!strnicmp(movielib_files[i],"robot",5))
834                         is_robots = 1;
835                 else
836                         is_robots = 0;
837
838                 init_movie(movielib_files[i],i,is_robots,1);
839         }
840
841         movie_libs[EXTRA_ROBOT_LIB] = NULL;
842
843         atexit(close_movies);
844 }
845
846
847 void init_extra_robot_movie(char *filename)
848 {
849         close_movie(EXTRA_ROBOT_LIB);
850         init_movie(filename,EXTRA_ROBOT_LIB,1,0);
851 }
852
853
854 int movie_handle,movie_start;
855
856 //looks through a movie library for a movie file
857 //returns filehandle, with fileposition at movie, or -1 if can't find
858 int search_movie_lib(movielib *lib,char *filename,int must_have)
859 {
860         int i;
861         int filehandle;
862
863         if (lib == NULL)
864                 return -1;
865
866         for (i=0;i<lib->n_movies;i++)
867                 if (!stricmp(filename,lib->movies[i].name)) {   //found the movie in a library 
868                         int from_cd;
869
870                         from_cd = (lib->flags & MLF_ON_CD);
871
872                         if (from_cd)
873                                 songs_stop_redbook();           //ready to read from CD
874
875                         do {            //keep trying until we get the file handle
876
877 #ifdef O_BINARY
878                                 movie_handle = filehandle = open(lib->name, O_RDONLY | O_BINARY);
879 #else
880                                 movie_handle = filehandle = open(lib->name, O_RDONLY);
881 #endif
882
883                                 if ((filehandle == -1) && (AltHogdir_initialized)) {
884                                         char temp[128];
885                                         strcpy(temp, AltHogDir);
886                                         strcat(temp, "/");
887                                         strcat(temp, lib->name);
888 #ifdef O_BINARY
889                                         movie_handle = filehandle = open(temp, O_RDONLY | O_BINARY);
890 #else
891                                         movie_handle = filehandle = open(temp, O_RDONLY);
892 #endif
893                                 }
894
895                                 if (must_have && from_cd && filehandle == -1) {         //didn't get file!
896
897                                         if (request_cd() == -1)         //ESC from requester
898                                                 break;                                          //bail from here. will get error later
899                                 }
900
901                         } while (must_have && from_cd && filehandle == -1);
902
903                         if (filehandle != -1)
904                                 lseek(filehandle,(movie_start=lib->movies[i].offset),SEEK_SET);
905
906                         return filehandle;
907                 }
908
909         return -1;
910 }
911
912
913 //returns file handle
914 int open_movie_file(char *filename,int must_have)
915 {
916         int filehandle,i;
917
918         for (i=0;i<N_MOVIE_LIBS;i++) {
919                 if ((filehandle = search_movie_lib(movie_libs[i],filename,must_have)) != -1)
920                         return filehandle;
921         }
922
923         return -1;              //couldn't find it
924 }
925
926 //sets the file position to the start of this already-open file
927 int reset_movie_file(int handle)
928 {
929         Assert(handle == movie_handle);
930
931         lseek(handle,movie_start,SEEK_SET);
932
933         return 0;       //everything is cool
934 }