This commit was manufactured by cvs2svn to create tag 'd2x-0_1_2'.
[btb/d2x.git] / main / old / movie.c
1 /*
2 THE COMPUTER CODE CONTAINED HEREIN IS THE SOLE PROPERTY OF PARALLAX
3 SOFTWARE CORPORATION ("PARALLAX").  PARALLAX, IN DISTRIBUTING THE CODE TO
4 END-USERS, AND SUBJECT TO ALL OF THE TERMS AND CONDITIONS HEREIN, GRANTS A
5 ROYALTY-FREE, PERPETUAL LICENSE TO SUCH END-USERS FOR USE BY SUCH END-USERS
6 IN USING, DISPLAYING,  AND CREATING DERIVATIVE WORKS THEREOF, SO LONG AS
7 SUCH USE, DISPLAY OR CREATION IS FOR NON-COMMERCIAL, ROYALTY OR REVENUE
8 FREE PURPOSES.  IN NO EVENT SHALL THE END-USER USE THE COMPUTER CODE
9 CONTAINED HEREIN FOR REVENUE-BEARING PURPOSES.  THE END-USER UNDERSTANDS
10 AND AGREES TO THE TERMS HEREIN AND ACCEPTS THE SAME BY USE OF THIS FILE.  
11 COPYRIGHT 1993-1999 PARALLAX SOFTWARE CORPORATION.  ALL RIGHTS RESERVED.
12 */
13
14
15 #pragma off (unreferenced)
16 static char rcsid[] = "$Id: movie.c,v 1.1.1.1 2001-01-19 03:30:14 bradleyb Exp $";
17 #pragma on (unreferenced)
18
19
20 #include <dos.h>
21 #include <io.h>
22 #include <fcntl.h>
23 #include <stdlib.h>
24 #include <stdio.h>
25 #include <conio.h>
26 #include <malloc.h>
27 #include <string.h>
28 #include <ctype.h>
29
30 #include "pa_enabl.h"                   //$$POLY_ACC
31 #include "inferno.h"
32 #include "text.h"
33 #include "args.h"
34 #include "mem.h"
35 #include "sos.h"
36 #include "byteswap.h"
37 #include "cfile.h"
38 #include "gamefont.h"
39 #include "gr.h"
40 #include "palette.h"
41 #include "config.h"
42 #include "mvelib32.h"
43 #include "mvegfx.h"
44 #include "mono.h"
45 #include "error.h"
46 #include "digi.h"
47 #include "songs.h"
48 #include "timer.h"
49 #include "joy.h"
50 #include "key.h"
51 #include "movie.h"
52 #include "screens.h"
53 #include "newmenu.h"
54 #include "vga.h"
55 #include "menu.h"
56
57 #if defined(POLY_ACC)
58 #include "poly_acc.h"
59 #endif
60
61 extern char *Args[100];                                 // Arguments from args.c for
62 char *FirstVid,*SecondVid;
63 char *RoboBuffer[50];
64 char RobBufCount=0,PlayingBuf=0,RobBufLimit=0;
65
66 unsigned RobSX=75,RobSY=50,RobDX=100,RobDY=100;
67 int RoboFile=0,RoboFilePos=0,MVEPaletteCalls=0;
68
69 //      Function Prototypes
70 int RunMovie(char *filename, int highres_flag, int allow_abort,int dx,int dy);
71 extern void do_briefing_screens (char *,int);
72
73 // Subtitle data
74 typedef struct {
75         short first_frame,last_frame;
76         char *msg;
77 } subtitle;
78
79
80 // #define BUFFER_MOVIE 
81
82 #define MAX_SUBTITLES 500
83 subtitle Subtitles[MAX_SUBTITLES];
84 int Num_subtitles;
85
86 // ----------------------------------------------------------------------
87 void* __cdecl MPlayAlloc(unsigned size)
88 {
89     return malloc(size);
90 }
91
92 void __cdecl MPlayFree(void *p)
93 {
94     free(p);
95 }
96
97 extern WORD hSOSDigiDriver;
98
99 int HiResRoboMovie=0;
100
101 //-----------------------------------------------------------------------
102
103 unsigned __cdecl FileRead(int handle, void *buf, unsigned count)
104 {
105     unsigned numread;
106     numread = read(handle, buf, count);
107     return (numread == count);
108 }
109
110 #define VID_PLAY 0
111 #define VID_PAUSE 1
112
113 int Vid_State;
114
115 extern int Digi_initialized;
116 extern int digi_timer_rate;
117
118 int MovieHires = 0;             //default for now is lores
119
120 #define MOVIE_VOLUME_SCALE  (32767)             //32767 is MAX
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         int save_sample_rate;
129
130         #if defined(POLY_ACC)
131                 PA_DFX (pa_set_write_mode (1));
132                 PA_DFX (pa_set_frontbuffer_current());
133         #endif
134
135         if (!Digi_initialized)
136                 return MOVIE_NOT_PLAYED;
137
138         #ifndef RELEASE
139         if (FindArg("-nomovies"))
140                 return MOVIE_NOT_PLAYED;
141         #endif
142
143         strcpy(name,filename);
144
145         if ((p=strchr(name,'.')) == NULL)               //add extension, if missing
146                 strcat(name,".mve");
147
148         //check for escape already pressed & abort if so
149         while ((c=key_inkey()) != 0)
150                 if (c == KEY_ESC)
151                         return MOVIE_ABORTED;
152
153         // Stop all digital sounds currently playing.
154         digi_stop_all();
155
156         // Stop all songs
157         songs_stop_all();
158
159         save_sample_rate = digi_sample_rate;
160         digi_sample_rate = SAMPLE_RATE_22K;             //always 22K for movies
161         digi_reset(); digi_reset();
162
163         // Start sound 
164         if (hSOSDigiDriver < 0xffff) {
165                 MVE_SOS_sndInit(hSOSDigiDriver);
166                 MVE_sndVolume((Config_digi_volume*MOVIE_VOLUME_SCALE)/8);
167         }
168         else
169                 MVE_SOS_sndInit(-1);
170
171         MVE_rmFastMode (MVE_RM_NORMAL);
172         
173         ret = RunMovie(name,MovieHires,must_have,-1,-1);
174
175 //@@    if (ret == MOVIE_NOT_PLAYED) {          //couldn't find movie. try other version
176 //@@            name[strlen(name)-5] = MovieHires?'l':'h';                              //change name
177 //@@            ret = RunMovie(name,!MovieHires,allow_abort,-1,-1);     //try again
178 //@@    }
179
180         gr_palette_clear();             //clear out palette in case movie aborted
181
182         digi_sample_rate = save_sample_rate;            //restore rate for game
183         digi_reset(); digi_reset();
184
185         Screen_mode = -1;               //force screen reset
186
187         #if defined(POLY_ACC)
188                 PA_DFX(pa_set_write_mode (0));
189         #endif
190
191         return ret;
192
193 }
194  
195 void __cdecl MovieShowFrame (ubyte *buf,uint bufw,uint bufh,uint sx,uint sy,uint w,uint h,uint dstx,uint dsty)
196 {
197         grs_bitmap source_bm;
198
199         //mprintf((0,"MovieShowFrame %d,%d  %d,%d  %d,%d  %d,%d\n",bufw,bufh,sx,sy,w,h,dstx,dsty));
200
201         Assert(bufw == w && bufh == h);
202
203         source_bm.bm_x = source_bm.bm_y = 0;
204         source_bm.bm_w = source_bm.bm_rowsize = bufw;
205         source_bm.bm_h = bufh;
206         source_bm.bm_type = BM_LINEAR;
207         source_bm.bm_flags = 0;
208         source_bm.bm_data = buf;
209
210         gr_bm_ubitblt(bufw,bufh,dstx,dsty,sx,sy,&source_bm,&grd_curcanv->cv_bitmap);
211 }
212
213 //our routine to set the pallete, called from the movie code
214 void __cdecl MovieSetPalette(unsigned char *p, unsigned start, unsigned count)
215 {
216         if (count == 0)
217                 return;
218
219         //mprintf((0,"SetPalette p=%x, start=%d, count=%d\n",p,start,count));
220
221         //Color 0 should be black, and we get color 255
222         Assert(start>=1 && start+count-1<=254);
223
224         //Set color 0 to be black
225         gr_palette[0] = gr_palette[1] = gr_palette[2] = 0;
226
227         //Set color 255 to be our subtitle color
228         gr_palette[765] = gr_palette[766] = gr_palette[767] = 50;
229
230         //movie libs palette into our array  
231         memcpy(gr_palette+start*3,p+start*3,count*3);
232
233         //finally set the palette in the hardware
234         gr_palette_load(gr_palette);
235
236         //MVE_SetPalette(p, start, count);
237 }
238
239 typedef struct bkg {
240         short x, y, w, h;                       // The location of the menu.
241         grs_bitmap * bmp;                       // The background under the menu.
242 } bkg;
243
244 bkg movie_bg = {0,0,0,0,NULL};
245
246 #define BOX_BORDER (MenuHires?40:20)
247
248 void show_pause_message(char *msg)
249 {       
250         int w,h,aw;
251         int x,y;
252
253         gr_set_current_canvas(NULL);
254         gr_set_curfont( SMALL_FONT );
255
256         gr_get_string_size(msg,&w,&h,&aw);
257
258         x = (grd_curscreen->sc_w-w)/2;
259         y = (grd_curscreen->sc_h-h)/2;
260
261         if (movie_bg.bmp) {
262                 gr_free_bitmap(movie_bg.bmp);
263                 movie_bg.bmp = NULL;
264         }
265
266         // Save the background of the display
267         movie_bg.x=x; movie_bg.y=y; movie_bg.w=w; movie_bg.h=h;
268
269         movie_bg.bmp = gr_create_bitmap( w+BOX_BORDER, h+BOX_BORDER );
270
271         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 );
272
273         gr_setcolor(0);
274         gr_rect(x-BOX_BORDER/2,y-BOX_BORDER/2,x+w+BOX_BORDER/2-1,y+h+BOX_BORDER/2-1);
275
276         gr_set_fontcolor( 255, -1 );
277
278         gr_ustring( 0x8000, y, msg );
279 }
280
281 void clear_pause_message()
282 {
283
284         if (movie_bg.bmp) {
285
286                 gr_bitmap(movie_bg.x-BOX_BORDER/2, movie_bg.y-BOX_BORDER/2, movie_bg.bmp);
287         
288                 gr_free_bitmap(movie_bg.bmp);
289                 movie_bg.bmp = NULL;
290         }
291 }
292
293 int open_movie_file(char *filename,int must_have);
294
295 //returns status.  see movie.h
296 int RunMovie(char *filename, int hires_flag, int must_have,int dx,int dy)
297 {
298         int filehndl;
299         int result,aborted=0;
300         int track = 0;
301         int frame_num;
302
303         // Open Movie file.  If it doesn't exist, no movie, just return.
304
305         filehndl = open_movie_file(filename,must_have);
306
307         if (filehndl == -1) {
308                 #ifndef EDITOR
309                         if (must_have)
310                           {
311                                 strupr(filename);
312                                 Error("Cannot open movie file <%s>",filename);
313                           }     
314                          else
315                           return MOVIE_NOT_PLAYED;
316                 #else
317                         return MOVIE_NOT_PLAYED;
318                 #endif
319         }
320
321         MVE_memCallbacks(MPlayAlloc, MPlayFree);
322         MVE_ioCallbacks(FileRead);
323         
324 #if defined(POLY_ACC)
325     Assert(hires_flag);
326
327     pa_flush();
328         #ifdef PA_3DFX_VOODOO
329     pa_begin_lfb();
330     MVE_sfSVGA( 640, 480, 2048, 0, pa_get_buffer_address(0), 0, 0, NULL, 1);
331     pa_end_lfb();
332         #else
333     MVE_sfSVGA( 640, 480, 1280, 0, pa_get_buffer_address(0), 0, 0, NULL, 1);
334         #endif
335
336     pa_clear_buffer(0, 0);
337 #else
338         if (hires_flag) {
339                 vga_set_mode(SM_640x480V);
340                 if (!MVE_gfxMode(MVE_GFX_VESA_CURRENT)) {
341                         Int3();
342                         close(filehndl);                           // Close Movie File
343                         return MOVIE_NOT_PLAYED;
344                 }
345         }
346         else {
347                 vga_set_mode(SM_320x200C);
348                 if (!MVE_gfxMode(MVE_GFX_VGA_CURRENT)) {
349                         Int3();
350                         close(filehndl);                           // Close Movie File
351                         return MOVIE_NOT_PLAYED;
352                 }
353         }
354 #endif
355
356         Vid_State = VID_PLAY;                           // Set to PLAY
357
358         if (MVE_rmPrepMovie(filehndl, dx, dy, track)) {
359                 Int3();
360                 return MOVIE_NOT_PLAYED;        
361         }
362
363 #if !defined(POLY_ACC)
364         MVE_sfCallbacks ((mve_cb_ShowFrame *) MovieShowFrame);
365         MVE_palCallbacks ((mve_cb_SetPalette *) MovieSetPalette);
366 #endif
367
368         frame_num = 0;
369
370         FontHires = hires_flag;
371
372         while((result = MVE_rmStepMovie()) == 0) {
373                 int key;
374
375                 draw_subtitles(frame_num);
376
377                 key = key_inkey();
378
379                 // If ESCAPE pressed, then quit movie.
380                 if (key == KEY_ESC) {
381                         result = aborted = 1;
382                         break;
383                 }
384
385                 // If PAUSE pressed, then pause movie
386                 if (key == KEY_PAUSE) {
387                         MVE_rmHoldMovie();
388                         show_pause_message(TXT_PAUSE);
389                         while (!key_inkey()) ;
390                         clear_pause_message();
391                 }
392
393                 frame_num++;
394         }
395
396         Assert(aborted || result == MVE_ERR_EOF);               ///movie should be over
397                 
398         MVE_rmEndMovie();
399         MVE_ReleaseMem();
400
401         close(filehndl);                           // Close Movie File
402  
403         //MVE_gfxReset();
404
405         // Restore old graphic state
406
407         Screen_mode=-1;         //force reset of screen mode
408             
409 //@@   if (MenuHires) 
410 //@@            vga_set_mode(SM_640x480V);
411 //@@   else     
412 //@@            vga_set_mode(SM_320x200C);
413         
414         return (aborted?MOVIE_ABORTED:MOVIE_PLAYED_FULL);
415 }
416                 
417
418 int InitMovieBriefing ()
419  {
420 #if defined(POLY_ACC)
421     Assert(MenuHires);
422          pa_flush();
423          
424         #ifdef PA_3DFX_VOODOO
425     pa_begin_lfb();
426     MVE_sfSVGA( 640, 480, 2048, 0, pa_get_buffer_address(0), 0, 0, NULL, 1);
427          pa_end_lfb();
428         #else
429     MVE_sfSVGA( 640, 480, 1280, 0, pa_get_buffer_address(0), 0, 0, NULL, 1);
430         #endif
431
432     pa_clear_buffer(0, 0);
433     return 1;
434 #endif
435
436         if (MenuHires) {
437                 vga_set_mode(SM_640x480V);
438                 if (!MVE_gfxMode(MVE_GFX_VESA_CURRENT)) {
439                         Int3();
440                         return MOVIE_NOT_PLAYED;
441                 }
442         }
443         else {
444                 vga_set_mode(SM_320x200C);
445                 if (!MVE_gfxMode(MVE_GFX_VGA_CURRENT)) {
446                         Int3();
447                         return MOVIE_NOT_PLAYED;
448                 }
449         }
450   return (1);
451  }  
452   
453 int FlipFlop=0;
454
455 int MyShowFrame (void)
456  {
457   grs_bitmap source_bm;
458  
459   int rw,rh,rdx,rdy;
460
461   if (MenuHires)
462    { rw=320; rh=200; rdx=280; rdy=200;}
463   else
464    { rw=160; rh=100; rdx=140; rdy=80;}
465    
466         source_bm.bm_x = source_bm.bm_y = 0;
467         source_bm.bm_w = source_bm.bm_rowsize = rw;
468         source_bm.bm_h = rh;
469         source_bm.bm_type = BM_LINEAR;
470         source_bm.bm_flags = 0;
471    if (FlipFlop)
472           {
473                 #ifdef BUFFER_MOVIE
474                         memcpy (RoboBuffer[RobBufCount++],SecondVid,rw*rh);
475                 #endif
476                 source_bm.bm_data = SecondVid;
477           }
478         else
479           {
480                 source_bm.bm_data = FirstVid;
481                 #ifdef BUFFER_MOVIE
482                         memcpy (RoboBuffer[RobBufCount++],FirstVid,rw*rh);
483                 #endif
484           }
485
486   gr_bm_ubitblt(rw,rh,rdx,rdy,0,0,&source_bm,&grd_curcanv->cv_bitmap);
487
488   FlipFlop=1-FlipFlop;
489  
490   return (NULL);
491  }
492
493 #ifdef BUFFER_MOVIE
494 static fix RobBufTime=0;
495 #endif
496
497 void ShowRobotBuffer ()
498  {
499   // shows a frame from the robot buffer
500
501 #ifndef BUFFER_MOVIE
502         Int3(); // Get Jason...how'd we get here?
503         return;
504 #else
505   grs_bitmap source_bm;
506   int rw,rh,rdx,rdy;
507   
508   if (timer_get_approx_seconds()<(RobBufTime+fixdiv (F1_0,i2f(15))))
509    return;      
510
511   RobBufTime=timer_get_approx_seconds();        
512   
513   if (MenuHires)
514    { rw=320; rh=200; rdx=280; rdy=200;}
515   else
516    { rw=160; rh=100; rdx=140; rdy=80;}
517    
518         source_bm.bm_x = source_bm.bm_y = 0;
519         source_bm.bm_w = source_bm.bm_rowsize = rw;
520         source_bm.bm_h = rh;
521         source_bm.bm_type = BM_LINEAR;
522         source_bm.bm_flags = 0;
523   
524         source_bm.bm_data = RoboBuffer[RobBufCount];
525
526    gr_bm_ubitblt(rw,rh,rdx,rdy,0,0,&source_bm,&grd_curcanv->cv_bitmap);
527
528    RobBufCount++;
529    RobBufCount%=RobBufLimit;
530 #endif
531  } 
532
533 //returns 1 if frame updated ok
534 int RotateRobot ()
535 {
536         int err;
537
538         if (!Digi_initialized)                  //we should fix this for full version
539                 return 0;
540 #ifdef BUFFER_MOVIE
541         if (PlayingBuf)
542          {
543           ShowRobotBuffer ();
544           return (1);   
545          }                
546 #endif
547
548         #if defined(POLY_ACC)
549            PA_DFX (pa_set_write_mode (1));
550         #endif
551
552         err = MVE_rmStepMovie();
553
554         #if defined(POLY_ACC)
555            PA_DFX (pa_set_write_mode (0));
556         #endif
557
558
559         if (err == MVE_ERR_EOF)         //end of movie, so reset
560         {
561 #ifdef BUFFER_MOVIE
562            PlayingBuf=1;
563            RobBufLimit=RobBufCount;
564                 RobBufCount=0;
565                 //RobBufTime=timer_get_approx_seconds();
566            return 1;
567 #else
568                 reset_movie_file(RoboFile);
569 #if defined(POLY_ACC)
570                 if (MVE_rmPrepMovie(RoboFile, 280, 200, 0)) {
571 #else
572                 if (MVE_rmPrepMovie(RoboFile, -1, -1, 0)) {
573 #endif
574                         Int3();
575                         return 0;
576                 }
577 #endif
578         }
579         else if (err) {
580                 Int3();
581                 return 0;
582         }
583
584         return 1;
585 }
586
587 void FreeRoboBuffer (int n)
588  {
589   // frees the 64k frame buffers, starting with n and then working down
590    
591   #ifndef BUFFER_MOVIE
592         n++;    //kill warning
593         return;
594   #else
595   int i;
596  
597   for (i=n;i>=0;i--)
598         free (RoboBuffer[i]);
599
600   #endif 
601  }
602
603
604 void DeInitRobotMovie()
605  {
606   RobBufCount=0; PlayingBuf=0;
607  
608 #if !defined(POLY_ACC)
609   memset (FirstVid,0,64000);
610   memset (SecondVid,0,64000);
611   MyShowFrame();
612 #endif
613
614   MVE_rmEndMovie();
615   MVE_ReleaseMem();
616   free (FirstVid);
617   free (SecondVid);
618    
619   FreeRoboBuffer (49);  
620  
621   MVE_palCallbacks (MVE_SetPalette);
622   close(RoboFile);                           // Close Movie File
623  }
624
625 void __cdecl PaletteChecker (unsigned char *p,unsigned start,unsigned count)
626  {
627   int i;
628
629   for (i=0;i<256;i++)
630    if (p[i]!=0)
631     break;
632
633   if (i>=255 && (MVEPaletteCalls++)>0)
634    return;
635
636   MVE_SetPalette (p,start,count);
637  }
638
639
640 int InitRobotMovie (char *filename)
641  {
642         #ifdef BUFFER_MOVIE
643         int i;
644         #endif
645
646   FlipFlop=0;
647
648   RobBufCount=0; PlayingBuf=0; RobBufLimit=0;
649
650   if (FindArg("-nomovies"))
651         return (0); 
652    
653 //   digi_stop_all();
654
655 //@@   if (MovieHires)
656 //@@            filename[4]='h';
657 //@@    else
658 //@@            filename[4]='l';
659   
660    mprintf ((0,"RoboFile=%s\n",filename));
661
662 #ifdef BUFFER_MOVIE
663
664    for (i=0;i<50;i++)
665          {
666      if (MenuHires)
667                  RoboBuffer[i]=malloc(65000L);
668           else
669                  RoboBuffer[i]=malloc(17000L);
670
671           if (RoboBuffer[i]==NULL)
672                 {
673                  mprintf ((0,"ROBOERROR: Could't allocate frame %d!\n",i));
674                  if (i)
675                   FreeRoboBuffer (i-1);
676                  return (NULL);
677                 }
678          }
679 #endif
680  
681         if ((FirstVid=calloc (65000L,1))==NULL)
682          {
683           FreeRoboBuffer(49);   
684      return (NULL);
685          }
686    if ((SecondVid=calloc (65000L,1))==NULL)     
687          {
688           free (FirstVid);      
689           FreeRoboBuffer(49);   
690           return (NULL);
691          }
692
693         MVE_SOS_sndInit(-1);            //tell movies to play no sound for robots
694
695    MVE_memCallbacks(MPlayAlloc, MPlayFree);
696    MVE_ioCallbacks(FileRead);
697    MVE_memVID (FirstVid,SecondVid,65000);
698
699    RoboFile = open_movie_file(filename,1);
700
701         if (RoboFile == -1) {
702                 free (FirstVid);
703                 free (SecondVid);       
704                 FreeRoboBuffer (49);
705                 #ifdef RELEASE
706                         Error("Cannot open movie file <%s>",filename);
707                 #else
708                         return MOVIE_NOT_PLAYED;
709                 #endif
710         }
711
712    Vid_State = VID_PLAY;                           
713
714 #if !defined(POLY_ACC)
715    MVE_sfCallbacks ((mve_cb_ShowFrame *)MyShowFrame);
716 #endif
717       
718 #if defined(POLY_ACC)
719         if (MVE_rmPrepMovie(RoboFile, 280, 200, 0)) {
720 #else
721    if (MVE_rmPrepMovie(RoboFile, -1, -1, 0)) {
722 #endif
723                 Int3();
724                 free (FirstVid);
725                 free (SecondVid);       
726                 FreeRoboBuffer (49);
727                 return 0;
728         }
729
730 #if !defined(POLY_ACC)
731    MVE_palCallbacks (PaletteChecker);
732 #endif
733
734    RoboFilePos=lseek (RoboFile,0L,SEEK_CUR);
735
736    mprintf ((0,"RoboFilePos=%d!\n",RoboFilePos));
737    return (1);
738   }
739
740 /*
741  *              Subtitle system code
742  */
743
744 ubyte *subtitle_raw_data;
745
746 //search for next field following whitespace 
747 ubyte *next_field(ubyte *p)
748 {
749         while (*p && !isspace(*p))
750                 p++;
751
752         if (!*p)
753                 return NULL;
754
755         while (*p && isspace(*p))
756                 p++;
757
758         if (!*p)
759                 return NULL;
760
761         return p;
762 }
763
764 void change_filename_ext( char *dest, char *src, char *ext );
765 void decode_text_line(char *p);
766
767 int init_subtitles(char *filename)
768 {
769         CFILE *ifile;
770         int size,read_count;
771         ubyte *p;
772         int have_binary = 0;
773
774         Num_subtitles = 0;
775
776         if (! FindArg("-subtitles"))
777                 return 0;
778
779         ifile = cfopen(filename,"rb");          //try text version
780
781         if (!ifile) {                                                           //no text version, try binary version
782                 char filename2[FILENAME_LEN];
783                 change_filename_ext(filename2,filename,".TXB");
784                 ifile = cfopen(filename2,"rb");
785                 if (!ifile)
786                         return 0;
787                 have_binary = 1;
788         }
789
790         size = cfilelength(ifile);
791    
792    MALLOC (subtitle_raw_data, ubyte, size+1);
793
794    read_count = cfread(subtitle_raw_data, 1, size, ifile);
795
796         cfclose(ifile);
797
798         subtitle_raw_data[size] = 0;
799
800         if (read_count != size) {
801                 free(subtitle_raw_data);
802                 return 0;
803         }
804
805         p = subtitle_raw_data;
806
807         while (p && p < subtitle_raw_data+size) {
808                 char *endp;
809
810                 endp = strchr(p,'\n'); 
811                 if (endp) {
812                         if (endp[-1] == '\r')
813                                 endp[-1] = 0;           //handle 0d0a pair
814                         *endp = 0;                      //string termintor
815                 }
816
817                 if (have_binary)
818                         decode_text_line(p);
819
820                 if (*p != ';') {
821                         Subtitles[Num_subtitles].first_frame = atoi(p);
822                         p = next_field(p); if (!p) continue;
823                         Subtitles[Num_subtitles].last_frame = atoi(p);
824                         p = next_field(p); if (!p) continue;
825                         Subtitles[Num_subtitles].msg = p;
826
827                         Assert(Num_subtitles==0 || Subtitles[Num_subtitles].first_frame >= Subtitles[Num_subtitles-1].first_frame);
828                         Assert(Subtitles[Num_subtitles].last_frame >= Subtitles[Num_subtitles].first_frame);
829
830                         Num_subtitles++;
831                 }
832
833                 p = endp+1;
834
835         }
836
837         return 1;
838
839 }
840
841 void close_subtitles()
842 {
843         if (subtitle_raw_data)
844                 free(subtitle_raw_data);
845         subtitle_raw_data = NULL;
846         Num_subtitles = 0;
847 }
848
849 #define MAX_ACTIVE_SUBTITLES 3
850
851 //draw the subtitles for this frame
852 void draw_subtitles(int frame_num)
853 {
854         static int active_subtitles[MAX_ACTIVE_SUBTITLES];
855         static int num_active_subtitles,next_subtitle,line_spacing;
856         int t,y;
857         int must_erase=0;
858
859         if (frame_num == 0) {
860                 num_active_subtitles = 0;
861                 next_subtitle = 0;
862                 gr_set_curfont( GAME_FONT );
863                 line_spacing = grd_curcanv->cv_font->ft_h + (grd_curcanv->cv_font->ft_h >> 2);
864                 gr_set_fontcolor(255,-1);
865         }
866
867         //get rid of any subtitles that have expired
868         for (t=0;t<num_active_subtitles;)
869                 if (frame_num > Subtitles[active_subtitles[t]].last_frame) {
870                         int t2;
871                         for (t2=t;t2<num_active_subtitles-1;t2++)
872                                 active_subtitles[t2] = active_subtitles[t2+1];
873                         num_active_subtitles--;
874                         must_erase = 1;
875                 }
876                 else
877                         t++;
878
879         //get any subtitles new for this frame 
880         while (next_subtitle < Num_subtitles && frame_num >= Subtitles[next_subtitle].first_frame) {
881                 if (num_active_subtitles >= MAX_ACTIVE_SUBTITLES)
882                         Error("Too many active subtitles!");
883                 active_subtitles[num_active_subtitles++] = next_subtitle;
884                 next_subtitle++;
885         }
886
887         //find y coordinate for first line of subtitles
888         y = grd_curcanv->cv_bitmap.bm_h-((line_spacing+1)*MAX_ACTIVE_SUBTITLES+2);
889
890         //erase old subtitles if necessary
891         if (must_erase) {
892                 gr_setcolor(0);
893                 gr_rect(0,y,grd_curcanv->cv_bitmap.bm_w-1,grd_curcanv->cv_bitmap.bm_h-1);
894         }
895
896         //now draw the current subtitles
897         for (t=0;t<num_active_subtitles;t++)
898                 if (active_subtitles[t] != -1) {
899                         gr_string(0x8000,y,Subtitles[active_subtitles[t]].msg);
900                         y += line_spacing+1;
901                 }
902 }
903
904 typedef struct {
905         char name[FILENAME_LEN];
906         int offset,len;
907 } ml_entry;
908
909 #define MLF_ON_CD               1
910
911 typedef struct {
912         char            name[100];      //[FILENAME_LEN];
913         int             n_movies;
914         ubyte           flags,pad[3];
915         ml_entry        movies[];
916 } movielib;
917
918 #define MAX_MOVIES_PER_LIB              50              //determines size of malloc
919
920 movielib *init_new_movie_lib(char *filename,FILE *fp)
921 {
922         int nfiles,offset;
923         int i,n;
924         movielib *table;
925
926         //read movie file header
927
928         fread(&nfiles,4,1,fp);          //get number of files
929
930         table = malloc(sizeof(*table) + sizeof(ml_entry)*nfiles);
931
932         strcpy(table->name,filename);
933         table->n_movies = nfiles;
934
935         offset = 4+4+nfiles*(13+4);     //id + nfiles + nfiles * (filename + size)
936
937         for (i=0;i<nfiles;i++) {
938                 int len;
939
940                 n = fread( table->movies[i].name, 13, 1, fp );
941                 if ( n != 1 )
942                         break;          //end of file (probably)
943
944                 n = fread( &len, 4, 1, fp );
945                 if ( n != 1 )
946                         Error("error reading movie library <%s>",filename);
947
948                 table->movies[i].len = INTEL_INT(len);
949                 table->movies[i].offset = offset;
950
951                 offset += table->movies[i].len;
952
953         }
954
955         fclose(fp);
956
957         table->flags = 0;
958
959         return table;
960
961 }
962
963 movielib *init_old_movie_lib(char *filename,FILE *fp)
964 {
965         int nfiles,size;
966         int i;
967         movielib *table,*table2;
968
969         nfiles = 0;
970
971         //allocate big table
972         table = malloc(sizeof(*table) + sizeof(ml_entry)*MAX_MOVIES_PER_LIB);
973
974         while( 1 ) {
975                 int len;
976
977                 i = fread( table->movies[nfiles].name, 13, 1, fp );
978                 if ( i != 1 )
979                         break;          //end of file (probably)
980
981                 i = fread( &len, 4, 1, fp );
982                 if ( i != 1 )
983                         Error("error reading movie library <%s>",filename);
984
985                 table->movies[nfiles].len = INTEL_INT(len);
986                 table->movies[nfiles].offset = ftell( fp );
987
988                 fseek( fp, INTEL_INT(len), SEEK_CUR );          //skip data
989
990                 nfiles++;
991         }
992
993         //allocate correct-sized table
994         size = sizeof(*table) + sizeof(ml_entry)*nfiles;
995         table2 = malloc(size);
996         memcpy(table2,table,size);
997         free(table);
998         table = table2;
999
1000         strcpy(table->name,filename);
1001
1002         table->n_movies = nfiles;
1003
1004         fclose(fp);
1005
1006         table->flags = 0;
1007
1008         return table;
1009
1010 }
1011
1012 //find the specified movie library, and read in list of movies in it   
1013 movielib *init_movie_lib(char *filename)
1014 {
1015         //note: this based on cfile_init_hogfile()
1016
1017         char id[4];
1018         FILE * fp;
1019  
1020         fp = fopen( filename, "rb" );
1021         if ( fp == NULL ) 
1022                 return NULL;
1023
1024         fread( id, 4, 1, fp );
1025         if ( !strncmp( id, "DMVL", 4 ) )
1026                 return init_new_movie_lib(filename,fp);
1027         else if ( !strncmp( id, "DHF", 3 ) ) {
1028                 fseek(fp,-1,SEEK_CUR);          //old file had 3 char id
1029                 return init_old_movie_lib(filename,fp);
1030         }
1031         else {
1032                 fclose(fp);
1033                 return NULL;
1034         }
1035 }
1036
1037 #ifdef D2_OEM
1038 char *movielib_files[] = {"intro-l.mvl","other-l.mvl","robots-l.mvl","oem-l.mvl"};
1039 #else
1040 char *movielib_files[] = {"intro-l.mvl","other-l.mvl","robots-l.mvl"};
1041 #endif
1042
1043 #define N_BUILTIN_MOVIE_LIBS (sizeof(movielib_files)/sizeof(*movielib_files))
1044 #define N_MOVIE_LIBS (N_BUILTIN_MOVIE_LIBS+1)
1045 #define EXTRA_ROBOT_LIB N_BUILTIN_MOVIE_LIBS
1046 movielib *movie_libs[N_MOVIE_LIBS];
1047
1048 close_movie(int i)
1049 {
1050         if (movie_libs[i])
1051                 free(movie_libs[i]);
1052 }
1053
1054 close_movies()
1055 {
1056         int i;
1057
1058         for (i=0;i<N_MOVIE_LIBS;i++)
1059                 close_movie(i);
1060 }
1061
1062 #include "gamepal.h"
1063
1064 extern char CDROM_dir[];
1065 extern int MenuHiresAvailable;
1066
1067 extern ubyte last_palette_for_color_fonts[];
1068
1069 extern int force_rb_register;
1070
1071 //ask user to put the D2 CD in.
1072 //returns -1 if ESC pressed, 0 if OK chosen
1073 //CD may not have been inserted
1074 int request_cd()
1075 {
1076         ubyte save_pal[256*3];
1077         grs_canvas *save_canv,*tcanv;
1078         int ret,was_faded=gr_palette_faded_out;
1079
1080         gr_palette_clear();
1081
1082         save_canv = grd_curcanv;
1083         tcanv = gr_create_canvas(grd_curcanv->cv_w,grd_curcanv->cv_h);
1084
1085         gr_set_current_canvas(tcanv);
1086         gr_ubitmap(0,0,&save_canv->cv_bitmap);
1087         gr_set_current_canvas(save_canv);
1088
1089         gr_clear_canvas(BM_XRGB(0,0,0));
1090         
1091         memcpy(save_pal,gr_palette,sizeof(save_pal));
1092
1093         memcpy(gr_palette,last_palette_for_color_fonts,sizeof(gr_palette));
1094
1095 try_again:;
1096
1097         ret = nm_messagebox( "CD ERROR", 1, "Ok", "Please insert your Descent II CD");
1098
1099         if (ret == -1) {
1100                 int ret2;
1101
1102                 ret2 = nm_messagebox( "CD ERROR", 2, "Try Again", "Leave Game", "You must insert your\nDescent II CD to Continue");
1103
1104                 if (ret2 == -1 || ret2 == 0)
1105                         goto try_again;
1106         }
1107
1108         force_rb_register = 1;  //disc has changed; force register new CD    
1109         
1110         gr_palette_clear();
1111
1112         memcpy(gr_palette,save_pal,sizeof(save_pal));
1113         
1114         gr_ubitmap(0,0,&tcanv->cv_bitmap);
1115
1116         if (!was_faded)
1117                 gr_palette_load(gr_palette);
1118
1119         gr_free_canvas(tcanv);
1120
1121         return ret;
1122 }
1123
1124 //do we have the robot movies available
1125 int robot_movies=0;     //0 means none, 1 means lowres, 2 means hires
1126
1127 init_movie(char *filename,int libnum,int is_robots,int required)
1128 {
1129         int high_res;
1130
1131         #ifndef RELEASE
1132         if (FindArg("-nomovies")) {
1133                 movie_libs[libnum] = NULL;
1134                 return;
1135         }
1136         #endif
1137
1138         //for robots, load highres versions if highres menus set
1139         if (is_robots)
1140                 high_res = MenuHiresAvailable;
1141         else
1142                 high_res = MovieHires;
1143
1144         if (high_res)
1145                 strchr(filename,'.')[-1] = 'h';
1146
1147 try_again:;
1148
1149         if ((movie_libs[libnum] = init_movie_lib(filename)) == NULL) {
1150                 char name2[100];
1151                 
1152                 strcpy(name2,CDROM_dir);
1153                 strcat(name2,filename);
1154                 movie_libs[libnum] = init_movie_lib(name2);
1155
1156                 if (movie_libs[libnum] != NULL)
1157                         movie_libs[libnum]->flags |= MLF_ON_CD;
1158                 else {
1159                         if (required) {
1160                                 #if defined(RELEASE) && !defined(D2_OEM)                //allow no movies if not release
1161                                         strupr(filename);
1162                                         Error("Cannot open movie file <%s>",filename);
1163                                 #endif
1164                         }
1165                         #if defined(D2_OEM)             //if couldn't get higres, try low
1166                         if (is_robots == 1) {   //first try, try again with lowres
1167                                 strchr(filename,'.')[-1] = 'l';
1168                                 high_res = 0;
1169                                 is_robots++;
1170                                 goto try_again;
1171                         }
1172                         else if (is_robots == 2) {              //failed twice. bail with error
1173                                 strupr(filename);
1174                                 Error("Cannot open movie file <%s>",filename);
1175                         }
1176                         #endif
1177                 }
1178         }
1179
1180         if (is_robots && movie_libs[libnum]!=NULL)
1181                 robot_movies = high_res?2:1;
1182 }
1183
1184 //find and initialize the movie libraries
1185 init_movies()
1186 {
1187         int i;
1188         int is_robots;
1189
1190         for (i=0;i<N_BUILTIN_MOVIE_LIBS;i++) {
1191
1192                 if (!strnicmp(movielib_files[i],"robot",5))
1193                         is_robots = 1;
1194                 else
1195                         is_robots = 0;
1196
1197                 init_movie(movielib_files[i],i,is_robots,1);
1198         }
1199
1200         movie_libs[EXTRA_ROBOT_LIB] = NULL;
1201
1202         atexit(close_movies);
1203
1204 }
1205
1206 init_extra_robot_movie(char *filename)
1207 {
1208         close_movie(EXTRA_ROBOT_LIB);
1209         init_movie(filename,EXTRA_ROBOT_LIB,1,0);
1210 }
1211
1212
1213 int movie_handle,movie_start;
1214
1215 //looks through a movie library for a movie file
1216 //returns filehandle, with fileposition at movie, or -1 if can't find
1217 search_movie_lib(movielib *lib,char *filename,int must_have)
1218 {
1219         int i;
1220         int filehandle;
1221
1222         if (lib == NULL)
1223                 return -1;
1224
1225         for (i=0;i<lib->n_movies;i++)
1226                 if (!stricmp(filename,lib->movies[i].name)) {   //found the movie in a library 
1227                         int from_cd;
1228
1229                         from_cd = (lib->flags & MLF_ON_CD);
1230
1231                         if (from_cd)
1232                                 songs_stop_redbook();           //ready to read from CD
1233
1234                         do {            //keep trying until we get the file handle
1235
1236                                 movie_handle = filehandle = open(lib->name, O_RDONLY + O_BINARY);
1237
1238                                 if (must_have && from_cd && filehandle == -1) {         //didn't get file!
1239
1240                                         if (request_cd() == -1)         //ESC from requester
1241                                                 break;                                          //bail from here. will get error later
1242                                 }
1243
1244                         } while (must_have && from_cd && filehandle == -1);
1245
1246                         if (filehandle != -1)
1247                                 lseek(filehandle,(movie_start=lib->movies[i].offset),SEEK_SET);
1248
1249                         return filehandle;
1250                 }
1251
1252         return -1;
1253 }
1254
1255 //returns file handle
1256 int open_movie_file(char *filename,int must_have)
1257 {
1258         int filehandle,i;
1259
1260         for (i=0;i<N_MOVIE_LIBS;i++) {
1261
1262                 if ((filehandle = search_movie_lib(movie_libs[i],filename,must_have)) != -1)
1263                         return filehandle;
1264         }
1265
1266         return -1;              //couldn't find it
1267 }
1268
1269 //sets the file position to the start of this already-open file
1270 int reset_movie_file(int handle)
1271 {
1272         Assert(handle == movie_handle);
1273
1274         lseek(handle,movie_start,SEEK_SET);
1275
1276         return 0;               //everything is cool
1277 }
1278
1279