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