initial movie support
[btb/d2x.git] / main / movie.c
1 /* $ Id: $ */
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.2 2002-07-22 02:20:05 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 "mvelib.h"
49 #include "text.h"
50
51 extern int MenuHiresAvailable;
52 extern char CDROM_dir[];
53
54 #define VID_PLAY 0
55 #define VID_PAUSE 1
56
57 int Vid_State;
58
59
60 // Subtitle data
61 typedef struct {
62         short first_frame,last_frame;
63         char *msg;
64 } subtitle;
65
66 #define MAX_SUBTITLES 500
67 #define MAX_ACTIVE_SUBTITLES 3
68 subtitle Subtitles[MAX_SUBTITLES];
69 int Num_subtitles;
70
71
72 typedef struct {
73         char name[FILENAME_LEN];
74         int offset,len;
75 } ml_entry;
76
77 #define MLF_ON_CD    1
78 #define MAX_MOVIES_PER_LIB    50    //determines size of malloc
79
80
81 typedef struct {
82         char     name[100]; //[FILENAME_LEN];
83         int      n_movies;
84         ubyte    flags,pad[3];
85         ml_entry *movies;
86 } movielib;
87
88 #ifdef D2_OEM
89 char *movielib_files[] = {"intro-l.mvl","other-l.mvl","robots-l.mvl","oem-l.mvl"};
90 #else
91 char *movielib_files[] = {"intro-l.mvl","other-l.mvl","robots-l.mvl"};
92 #endif
93
94 #define N_BUILTIN_MOVIE_LIBS (sizeof(movielib_files)/sizeof(*movielib_files))
95 #define N_MOVIE_LIBS (N_BUILTIN_MOVIE_LIBS+1)
96 #define EXTRA_ROBOT_LIB N_BUILTIN_MOVIE_LIBS
97 movielib *movie_libs[N_MOVIE_LIBS];
98
99 int MVEPaletteCalls = 0;
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
107 //      Function Prototypes
108 int RunMovie(char *filename, int highres_flag, int allow_abort,int dx,int dy);
109
110 int open_movie_file(char *filename,int must_have);
111
112 void change_filename_ext( char *dest, char *src, char *ext );
113 void decode_text_line(char *p);
114 void draw_subtitles(int frame_num);
115
116
117 //filename will actually get modified to be either low-res or high-res
118 //returns status.  see values in movie.h
119 int PlayMovie(const char *filename, int must_have)
120 {
121         char name[FILENAME_LEN],*p;
122         int c, ret;
123         int save_sample_rate;
124         
125         con_printf(DEBUG_LEVEL, "movie: PlayMovie: %s %d\n", filename, must_have);
126
127 #ifndef RELEASE
128         if (FindArg("-nomovies"))
129                 return MOVIE_NOT_PLAYED;
130 #endif
131
132         strcpy(name,filename);
133
134         if ((p=strchr(name,'.')) == NULL)               //add extension, if missing
135                 strcat(name,".mve");
136
137         //check for escape already pressed & abort if so
138         while ((c=key_inkey()) != 0)
139                 if (c == KEY_ESC)
140                         return MOVIE_ABORTED;
141
142         // Stop all digital sounds currently playing.
143         digi_stop_all();
144
145         // Stop all songs
146         songs_stop_all();
147
148         save_sample_rate = digi_sample_rate;
149         digi_sample_rate = SAMPLE_RATE_22K;             //always 22K for movies
150         digi_reset(); digi_reset();
151
152         ret = RunMovie(name,MovieHires,must_have,-1,-1);
153
154         //gr_palette_clear();           //clear out palette in case movie aborted
155
156         digi_sample_rate = save_sample_rate;            //restore rate for game
157         digi_reset(); digi_reset();
158
159         //Screen_mode = -1;             //force screen reset
160
161         return ret;
162 }
163
164 void initializeMovie(MVESTREAM *mve);
165 void shutdownMovie(MVESTREAM *mve);
166
167 //returns status.  see movie.h
168 int RunMovie(char *filename, int hires_flag, int must_have,int dx,int dy)
169 {
170         int filehndl;
171         int result=1,aborted=0;
172         int frame_num;
173         MVESTREAM *mve;
174
175         con_printf(DEBUG_LEVEL, "movie: RunMovie: %s, %d, %d, %d\n", filename, hires_flag, must_have, dx, dy);
176
177         // Open Movie file.  If it doesn't exist, no movie, just return.
178
179         filehndl = open_movie_file(filename,must_have);
180
181         if (filehndl == -1) {
182 #ifndef EDITOR
183                 if (must_have) {
184                         strupr(filename);
185                         Error("movie: RunMovie: Cannot open movie file <%s>",filename);
186                 } else
187                         return MOVIE_NOT_PLAYED;
188 #else
189                 return MOVIE_NOT_PLAYED;
190 #endif
191         }
192
193 #if 0
194         if (hires_flag)
195                 gr_set_mode(SM(640,480));
196         else
197                 gr_set_mode(SM(320,200));
198 #endif
199
200         frame_num = 0;
201
202         FontHires = hires_flag;
203
204     mve = mve_open(filehndl);
205     if (mve == NULL)
206     {
207         fprintf(stderr, "can't open MVE file '%s'\n", filename);
208         return 1;
209     }
210
211     initializeMovie(mve);
212
213         while((result = mve_play_next_chunk(mve))) {
214                 int key;
215
216                 draw_subtitles(frame_num);
217
218                 key = key_inkey();
219
220                 // If ESCAPE pressed, then quit movie.
221                 if (key == KEY_ESC) {
222                         result = aborted = 1;
223                         break;
224                 }
225
226                 // If PAUSE pressed, then pause movie
227                 if (key == KEY_PAUSE) {
228                         //MVE_rmHoldMovie();
229                         //show_pause_message(TXT_PAUSE);
230                         while (!key_inkey()) ;
231                         //clear_pause_message();
232                 }
233
234                 frame_num++;
235         }
236
237         Assert(aborted || !result);      ///movie should be over
238                 
239     shutdownMovie(mve);
240     mve_close(mve);
241
242         close(filehndl);                           // Close Movie File
243  
244         // Restore old graphic state
245
246         //Screen_mode=-1;  //force reset of screen mode
247
248         return (aborted?MOVIE_ABORTED:MOVIE_PLAYED_FULL);
249 }
250
251
252 int InitMovieBriefing()
253 {
254         con_printf(DEBUG_LEVEL, "movie: InitMovieBriefing\n");
255
256 #if 0
257         if (MenuHires)
258                 gr_set_mode(SM(640,480));
259         else
260                 gr_set_mode(SM(320,200));
261 #endif
262         return 1;
263 }
264
265
266 void RotateRobot()
267 {
268         con_printf(DEBUG_LEVEL, "STUB: movie: RotateRobot\n");
269 }
270
271
272 void DeInitRobotMovie(void)
273 {
274         con_printf(DEBUG_LEVEL, "STUB: movie: DeInitRobotMovie\n");
275 }
276
277
278 int InitRobotMovie(char *a)
279 {
280         con_printf(DEBUG_LEVEL, "STUB: movie: InitRobotMovie: %s\n", a);
281
282         return 0;
283 }
284
285
286 /*
287  *              Subtitle system code
288  */
289
290 ubyte *subtitle_raw_data;
291
292
293 //search for next field following whitespace 
294 ubyte *next_field(ubyte *p)
295 {
296         con_printf(DEBUG_LEVEL, "movie: next_field: %c\n", *p);
297
298         while (*p && !isspace(*p))
299                 p++;
300
301         if (!*p)
302                 return NULL;
303
304         while (*p && isspace(*p))
305                 p++;
306
307         if (!*p)
308                 return NULL;
309
310         return p;
311 }
312
313
314 int init_subtitles(char *filename)
315 {
316         CFILE *ifile;
317         int size,read_count;
318         ubyte *p;
319         int have_binary = 0;
320
321         con_printf(DEBUG_LEVEL, "movie: init_subtitles: %s\n", filename);
322
323         Num_subtitles = 0;
324
325         if (! FindArg("-subtitles"))
326                 return 0;
327
328         ifile = cfopen(filename,"rb");          //try text version
329
330         if (!ifile) {                                                           //no text version, try binary version
331                 char filename2[FILENAME_LEN];
332                 change_filename_ext(filename2,filename,".TXB");
333                 ifile = cfopen(filename2,"rb");
334                 if (!ifile)
335                         return 0;
336                 have_binary = 1;
337         }
338
339         size = cfilelength(ifile);
340    
341         MALLOC (subtitle_raw_data, ubyte, size+1);
342
343         read_count = cfread(subtitle_raw_data, 1, size, ifile);
344
345         cfclose(ifile);
346
347         subtitle_raw_data[size] = 0;
348
349         if (read_count != size) {
350                 d_free(subtitle_raw_data);
351                 return 0;
352         }
353
354         p = subtitle_raw_data;
355
356         while (p && p < subtitle_raw_data+size) {
357                 char *endp;
358
359                 endp = strchr(p,'\n'); 
360                 if (endp) {
361                         if (endp[-1] == '\r')
362                                 endp[-1] = 0;           //handle 0d0a pair
363                         *endp = 0;                      //string termintor
364                 }
365
366                 if (have_binary)
367                         decode_text_line(p);
368
369                 if (*p != ';') {
370                         Subtitles[Num_subtitles].first_frame = atoi(p);
371                         p = next_field(p); if (!p) continue;
372                         Subtitles[Num_subtitles].last_frame = atoi(p);
373                         p = next_field(p); if (!p) continue;
374                         Subtitles[Num_subtitles].msg = p;
375
376                         Assert(Num_subtitles==0 || Subtitles[Num_subtitles].first_frame >= Subtitles[Num_subtitles-1].first_frame);
377                         Assert(Subtitles[Num_subtitles].last_frame >= Subtitles[Num_subtitles].first_frame);
378
379                         Num_subtitles++;
380                 }
381
382                 p = endp+1;
383
384         }
385
386         return 1;
387 }
388
389
390 void close_subtitles()
391 {
392         con_printf(DEBUG_LEVEL, "movie: close_subtitles\n");
393
394         if (subtitle_raw_data)
395                 d_free(subtitle_raw_data);
396         subtitle_raw_data = NULL;
397         Num_subtitles = 0;
398 }
399
400
401 //draw the subtitles for this frame
402 void draw_subtitles(int frame_num)
403 {
404         static int active_subtitles[MAX_ACTIVE_SUBTITLES];
405         static int num_active_subtitles,next_subtitle,line_spacing;
406         int t,y;
407         int must_erase=0;
408
409         con_printf(DEBUG_LEVEL, "movie: draw_subtitles: %d\n", frame_num);
410
411         if (frame_num == 0) {
412                 num_active_subtitles = 0;
413                 next_subtitle = 0;
414                 gr_set_curfont( GAME_FONT );
415                 line_spacing = grd_curcanv->cv_font->ft_h + (grd_curcanv->cv_font->ft_h >> 2);
416                 gr_set_fontcolor(255,-1);
417         }
418
419         //get rid of any subtitles that have expired
420         for (t=0;t<num_active_subtitles;)
421                 if (frame_num > Subtitles[active_subtitles[t]].last_frame) {
422                         int t2;
423                         for (t2=t;t2<num_active_subtitles-1;t2++)
424                                 active_subtitles[t2] = active_subtitles[t2+1];
425                         num_active_subtitles--;
426                         must_erase = 1;
427                 }
428                 else
429                         t++;
430
431         //get any subtitles new for this frame 
432         while (next_subtitle < Num_subtitles && frame_num >= Subtitles[next_subtitle].first_frame) {
433                 if (num_active_subtitles >= MAX_ACTIVE_SUBTITLES)
434                         Error("Too many active subtitles!");
435                 active_subtitles[num_active_subtitles++] = next_subtitle;
436                 next_subtitle++;
437         }
438
439         //find y coordinate for first line of subtitles
440         y = grd_curcanv->cv_bitmap.bm_h-((line_spacing+1)*MAX_ACTIVE_SUBTITLES+2);
441
442         //erase old subtitles if necessary
443         if (must_erase) {
444                 gr_setcolor(0);
445                 gr_rect(0,y,grd_curcanv->cv_bitmap.bm_w-1,grd_curcanv->cv_bitmap.bm_h-1);
446         }
447
448         //now draw the current subtitles
449         for (t=0;t<num_active_subtitles;t++)
450                 if (active_subtitles[t] != -1) {
451                         con_printf(DEBUG_LEVEL, "%s\n", Subtitles[active_subtitles[t]].msg);
452                         gr_string(0x8000,y,Subtitles[active_subtitles[t]].msg);
453                         y += line_spacing+1;
454                 }
455 }
456
457
458 int request_cd(void)
459 {
460         con_printf(DEBUG_LEVEL, "STUB: movie: request_cd\n");
461         return 0;
462 }
463
464
465 movielib *init_new_movie_lib(char *filename,FILE *fp)
466 {
467         int nfiles,offset;
468         int i,n;
469         movielib *table;
470
471         con_printf(DEBUG_LEVEL, "movie: init_new_movie_lib: %s\n", filename);
472
473         //read movie file header
474
475         fread(&nfiles,4,1,fp);          //get number of files
476
477         //table = d_malloc(sizeof(*table) + sizeof(ml_entry)*nfiles);
478         MALLOC(table, movielib, 1);
479         MALLOC(table->movies, ml_entry, nfiles);
480
481         strcpy(table->name,filename);
482         table->n_movies = nfiles;
483
484         offset = 4+4+nfiles*(13+4);     //id + nfiles + nfiles * (filename + size)
485
486         for (i=0;i<nfiles;i++) {
487                 int len;
488
489                 n = fread( table->movies[i].name, 13, 1, fp );
490                 if ( n != 1 )
491                         break;          //end of file (probably)
492
493                 n = fread( &len, 4, 1, fp );
494                 if ( n != 1 )
495                         Error("error reading movie library <%s>",filename);
496
497                 table->movies[i].len = INTEL_INT(len);
498                 table->movies[i].offset = offset;
499
500                 offset += table->movies[i].len;
501
502         }
503
504         fclose(fp);
505
506         table->flags = 0;
507
508         return table;
509
510 }
511
512
513 movielib *init_old_movie_lib(char *filename,FILE *fp)
514 {
515         int nfiles,size;
516         int i;
517         movielib *table,*table2;
518
519         con_printf(DEBUG_LEVEL, "movie: init_old_movie_lib: %s\n", filename);
520
521         nfiles = 0;
522
523         //allocate big table
524         table = d_malloc(sizeof(*table) + sizeof(ml_entry)*MAX_MOVIES_PER_LIB);
525
526         while( 1 ) {
527                 int len;
528
529                 i = fread( table->movies[nfiles].name, 13, 1, fp );
530                 if ( i != 1 )
531                         break;          //end of file (probably)
532
533                 i = fread( &len, 4, 1, fp );
534                 if ( i != 1 )
535                         Error("error reading movie library <%s>",filename);
536
537                 table->movies[nfiles].len = INTEL_INT(len);
538                 table->movies[nfiles].offset = ftell( fp );
539
540                 fseek( fp, INTEL_INT(len), SEEK_CUR );          //skip data
541
542                 nfiles++;
543         }
544
545         //allocate correct-sized table
546         size = sizeof(*table) + sizeof(ml_entry)*nfiles;
547         table2 = d_malloc(size);
548         memcpy(table2,table,size);
549         d_free(table);
550         table = table2;
551
552         strcpy(table->name,filename);
553
554         table->n_movies = nfiles;
555
556         fclose(fp);
557
558         table->flags = 0;
559
560         return table;
561
562 }
563
564
565 //find the specified movie library, and read in list of movies in it   
566 movielib *init_movie_lib(char *filename)
567 {
568         //note: this based on cfile_init_hogfile()
569
570         char id[4];
571         FILE * fp;
572  
573         con_printf(DEBUG_LEVEL, "movie: init_movie_lib: %s\n", filename);
574
575         fp = fopen( filename, "rb" );
576         if ( fp == NULL ) 
577                 return NULL;
578
579         fread( id, 4, 1, fp );
580         if ( !strncmp( id, "DMVL", 4 ) )
581                 return init_new_movie_lib(filename,fp);
582         else if ( !strncmp( id, "DHF", 3 ) ) {
583                 fseek(fp,-1,SEEK_CUR);          //old file had 3 char id
584                 return init_old_movie_lib(filename,fp);
585         }
586         else {
587                 fclose(fp);
588                 return NULL;
589         }
590 }
591
592
593 void close_movie(int i)
594 {
595         con_printf(DEBUG_LEVEL, "movie: close_movie\n");
596
597         if (movie_libs[i]) {
598                 d_free(movie_libs[i]->movies);
599                 d_free(movie_libs[i]);
600         }
601 }
602
603
604 void close_movies()
605 {
606         int i;
607
608         con_printf(DEBUG_LEVEL, "movie: close_movies\n");
609
610         for (i=0;i<N_MOVIE_LIBS;i++)
611                 close_movie(i);
612 }
613
614
615 void init_movie(char *filename,int libnum,int is_robots,int required)
616 {
617         int high_res;
618
619         con_printf(DEBUG_LEVEL, "movie: init_movie: %s, %d, %d, %d\n", filename, libnum, is_robots, required);
620
621 #ifndef RELEASE
622         if (FindArg("-nomovies")) {
623                 movie_libs[libnum] = NULL;
624                 return;
625         }
626 #endif
627
628         //for robots, load highres versions if highres menus set
629         if (is_robots)
630                 high_res = MenuHiresAvailable;
631         else
632                 high_res = MovieHires;
633
634         if (high_res)
635                 strchr(filename,'.')[-1] = 'h';
636
637 #if defined(D2_OEM)
638 try_again:;
639 #endif
640
641         if ((movie_libs[libnum] = init_movie_lib(filename)) == NULL) {
642                 char name2[100];
643                 
644                 strcpy(name2,CDROM_dir);
645                 strcat(name2,filename);
646                 movie_libs[libnum] = init_movie_lib(name2);
647
648                 if (movie_libs[libnum] != NULL)
649                         movie_libs[libnum]->flags |= MLF_ON_CD;
650                 else {
651                         if (required) {
652 #if defined(RELEASE) && !defined(D2_OEM)                //allow no movies if not release
653                                         strupr(filename);
654                                         Error("Cannot open movie file <%s>",filename);
655 #endif
656                         }
657 #if defined(D2_OEM)             //if couldn't get higres, try low
658                         if (is_robots == 1) {   //first try, try again with lowres
659                                 strchr(filename,'.')[-1] = 'l';
660                                 high_res = 0;
661                                 is_robots++;
662                                 goto try_again;
663                         }
664                         else if (is_robots == 2) {              //failed twice. bail with error
665                                 strupr(filename);
666                                 Error("Cannot open movie file <%s>",filename);
667                         }
668 #endif
669                 }
670         }
671
672         if (is_robots && movie_libs[libnum]!=NULL)
673                 robot_movies = high_res?2:1;
674 }
675
676
677 //find and initialize the movie libraries
678 void init_movies()
679 {
680         int i;
681         int is_robots;
682
683         con_printf(DEBUG_LEVEL, "movie: init_movies\n");
684
685         for (i=0;i<N_BUILTIN_MOVIE_LIBS;i++) {
686
687                 if (!strnicmp(movielib_files[i],"robot",5))
688                         is_robots = 1;
689                 else
690                         is_robots = 0;
691
692                 init_movie(movielib_files[i],i,is_robots,1);
693         }
694
695         movie_libs[EXTRA_ROBOT_LIB] = NULL;
696
697         atexit(close_movies);
698 }
699
700
701 void init_extra_robot_movie(char *f)
702 {
703         con_printf(DEBUG_LEVEL, "STUB: movie: init_extra_robot_movie: %s\n", f);
704 }
705
706
707 //looks through a movie library for a movie file
708 //returns filehandle, with fileposition at movie, or -1 if can't find
709 int search_movie_lib(movielib *lib,char *filename,int must_have)
710 {
711         int i;
712         int filehandle;
713
714         con_printf(DEBUG_LEVEL, "movie: search_movie_lib: %s, %s, %d\n", lib->name, filename, must_have);
715
716         if (lib == NULL)
717                 return -1;
718
719         for (i=0;i<lib->n_movies;i++)
720                 if (!stricmp(filename,lib->movies[i].name)) {   //found the movie in a library 
721                         int from_cd;
722
723                         from_cd = (lib->flags & MLF_ON_CD);
724
725                         if (from_cd)
726                                 songs_stop_redbook();           //ready to read from CD
727
728                         do {            //keep trying until we get the file handle
729
730                                 /* movie_handle = */ filehandle = open(lib->name, O_RDONLY);
731
732                                 if (must_have && from_cd && filehandle == -1) {         //didn't get file!
733
734                                         if (request_cd() == -1)         //ESC from requester
735                                                 break;                                          //bail from here. will get error later
736                                 }
737
738                         } while (must_have && from_cd && filehandle == -1);
739
740                         if (filehandle != -1)
741                                 lseek(filehandle,(/* movie_start = */ lib->movies[i].offset),SEEK_SET);
742
743                         return filehandle;
744                 }
745
746         return -1;
747 }
748
749
750 //returns file handle
751 int open_movie_file(char *filename,int must_have)
752 {
753         int filehandle,i;
754
755         con_printf(DEBUG_LEVEL, "movie: open_movie_file: %s %d\n", filename, must_have);
756
757         for (i=0;i<N_MOVIE_LIBS;i++) {
758                 if ((filehandle = search_movie_lib(movie_libs[i],filename,must_have)) != -1)
759                         return filehandle;
760         }
761
762         return -1;              //couldn't find it
763 }
764
765