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