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