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