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