Movie playing kludgey stuff
[btb/d2x.git] / main / movie.c
1 /*
2 THE COMPUTER CODE CONTAINED HEREIN IS THE SOLE PROPERTY OF PARALLAX
3 SOFTWARE CORPORATION ("PARALLAX").  PARALLAX, IN DISTRIBUTING THE CODE TO
4 END-USERS, AND SUBJECT TO ALL OF THE TERMS AND CONDITIONS HEREIN, GRANTS A
5 ROYALTY-FREE, PERPETUAL LICENSE TO SUCH END-USERS FOR USE BY SUCH END-USERS
6 IN USING, DISPLAYING,  AND CREATING DERIVATIVE WORKS THEREOF, SO LONG AS
7 SUCH USE, DISPLAY OR CREATION IS FOR NON-COMMERCIAL, ROYALTY OR REVENUE
8 FREE PURPOSES.  IN NO EVENT SHALL THE END-USER USE THE COMPUTER CODE
9 CONTAINED HEREIN FOR REVENUE-BEARING PURPOSES.  THE END-USER UNDERSTANDS
10 AND AGREES TO THE TERMS HEREIN AND ACCEPTS THE SAME BY USE OF THIS FILE.  
11 COPYRIGHT 1993-1999 PARALLAX SOFTWARE CORPORATION.  ALL RIGHTS RESERVED.
12 */
13
14 /*
15  * $Source: /cvs/cvsroot/d2x/main/movie.c,v $
16  * $Revision: 1.1 $
17  * $Author: bradleyb $
18  * $Date: 2002-01-18 07:26:54 $
19  *
20  * Movie stuff (converts mve's to exe files, and plays them externally (e.g. with wine)
21  *
22  * $Log: not supported by cvs2svn $
23  *
24  */
25
26 #ifdef HAVE_CONFIG_H
27 #include <conf.h>
28 #endif
29
30 #include <string.h>
31 #include <unistd.h>
32 #include <fcntl.h>
33 #include <ctype.h>
34 #include <sys/stat.h>
35 #include <sys/wait.h>
36
37 #include "inferno.h"
38 #include "args.h"
39 #include "movie.h"
40 #include "key.h"
41 #include "songs.h"
42 #include "strutil.h"
43 #include "mono.h"
44 #include "error.h"
45 #include "digi.h"
46 #include "u_mem.h"
47 #include "byteswap.h"
48 #include "cfile.h"
49 #include "gr.h"
50 #include "palette.h"
51 #include "newmenu.h"
52
53 int RoboFile=0,MVEPaletteCalls=0;
54
55 //      Function Prototypes
56 int RunMovie(char *filename, int highres_flag, int allow_abort,int dx,int dy);
57
58 // Subtitle data
59 typedef struct {
60         short first_frame,last_frame;
61         char *msg;
62 } subtitle;
63
64
65 // #define BUFFER_MOVIE 
66
67 #define MAX_SUBTITLES 500
68 subtitle Subtitles[MAX_SUBTITLES];
69 int Num_subtitles;
70
71 int MovieHires = 0;             //default for now is lores
72
73 //filename will actually get modified to be either low-res or high-res
74 //returns status.  see values in movie.h
75 int PlayMovie(const char *filename, int must_have)
76 {
77         char name[FILENAME_LEN],*p;
78         int c, ret;
79
80 #ifndef RELEASE
81         if (FindArg("-nomovies"))
82                 return MOVIE_NOT_PLAYED;
83 #endif
84
85         strcpy(name,filename);
86
87         if ((p=strchr(name,'.')) == NULL)               //add extension, if missing
88                 strcat(name,".mve");
89
90         //check for escape already pressed & abort if so
91         while ((c=key_inkey()) != 0)
92                 if (c == KEY_ESC)
93                         return MOVIE_ABORTED;
94
95         // Stop all digital sounds currently playing.
96         digi_stop_all();
97
98         // Stop all songs
99         songs_stop_all();
100
101         ret = RunMovie(name,MovieHires,must_have,-1,-1);
102
103         return ret;
104 }
105  
106 int open_movie_file(char *filename,int must_have, int *lenp);
107
108 //returns status.  see movie.h
109 int RunMovie(char *filename, int hires_flag, int must_have,int dx,int dy)
110 {
111         int filehndl, size;
112         int aborted=0;
113
114         // Open Movie file.  If it doesn't exist, no movie, just return.
115
116         filehndl = open_movie_file(filename,must_have, &size);
117
118         if (filehndl == -1) {
119 #ifndef EDITOR
120                 if (must_have)
121                         {
122                                 strupr(filename);
123                                 Error("Cannot open movie file <%s>",filename);
124                         }       
125                 else
126                         return MOVIE_NOT_PLAYED;
127 #else
128                 return MOVIE_NOT_PLAYED;
129 #endif
130         }
131
132 #if 0
133         if (hires_flag)
134                 gr_set_mode(SM_640x480V);
135         else
136                 gr_set_mode(SM_320x200C);
137 #endif
138
139         // play!
140         {
141                 char *buf;
142                 int len;
143                 FILE *fil;
144                 struct stat stats;
145                 char *stubfile;
146                 char *execcmd;
147
148                 strcpy(filename+strlen(filename)-4,".exe"); //change extension
149                 if (stat(filename, &stats)) {
150                         stubfile = "fstrailw.stub";
151                         if (stat(stubfile, &stats)) {
152                                 con_printf(CON_NORMAL, "Error loading %s, aborting movie.\n", stubfile);
153                                 return MOVIE_NOT_PLAYED;
154                         }
155                         
156                         len = stats.st_size;
157                         buf = d_malloc(len);
158                         fil = fopen(stubfile, "r");
159                         fread(buf, len, 1, fil);
160                         fclose(fil);
161                         
162                         fil = fopen(filename, "w");
163                         fwrite(buf, len, 1, fil);
164                         d_free(buf);
165                         
166                         len = size;
167                         buf = d_malloc(len);
168                         read(filehndl, buf, len);
169                         fwrite(buf, len, 1, fil);
170                         d_free(buf);
171                         fclose(fil);
172                 }
173                 sprintf(execcmd, "wine %s", filename);
174                 if(system(execcmd) == -1) {
175                         con_printf(CON_NORMAL, "Error executing %s, movie aborted.\n", filename);
176                         return MOVIE_NOT_PLAYED;
177                 }
178                         
179
180         }
181
182         close(filehndl);                           // Close Movie File
183  
184         Screen_mode=-1;         //force reset of screen mode
185             
186         return (aborted?MOVIE_ABORTED:MOVIE_PLAYED_FULL);
187 }
188
189 int InitMovieBriefing ()
190 {
191         return 1;
192 }
193
194 //returns 1 if frame updated ok
195 int RotateRobot ()
196 {
197         return 1;
198 }
199
200 void DeInitRobotMovie()
201 {
202         close(RoboFile);                           // Close Movie File
203 }
204
205 int InitRobotMovie (char *filename)
206 {
207         int len;
208
209         if (FindArg("-nomovies"))
210                 return MOVIE_NOT_PLAYED; 
211
212 //      digi_stop_all();
213
214         mprintf ((0,"RoboFile=%s\n",filename));
215
216         RoboFile = open_movie_file(filename,1, &len);
217
218         if (RoboFile == -1) {
219                 #ifdef RELEASE
220                         Error("Cannot open movie file <%s>",filename);
221                 #else
222                         return MOVIE_NOT_PLAYED;
223                 #endif
224         }
225         
226         return 1;
227 }
228
229 /*
230  *              Subtitle system code
231  */
232
233 ubyte *subtitle_raw_data;
234
235 //search for next field following whitespace 
236 ubyte *next_field(ubyte *p)
237 {
238         while (*p && !isspace(*p))
239                 p++;
240
241         if (!*p)
242                 return NULL;
243
244         while (*p && isspace(*p))
245                 p++;
246
247         if (!*p)
248                 return NULL;
249
250         return p;
251 }
252
253 void change_filename_ext( char *dest, char *src, char *ext );
254 void decode_text_line(char *p);
255
256 int init_subtitles(char *filename)
257 {
258         CFILE *ifile;
259         int size,read_count;
260         ubyte *p;
261         int have_binary = 0;
262
263         Num_subtitles = 0;
264
265         if (! FindArg("-subtitles"))
266                 return 0;
267
268         ifile = cfopen(filename,"rb");          //try text version
269
270         if (!ifile) {                                                           //no text version, try binary version
271                 char filename2[FILENAME_LEN];
272                 change_filename_ext(filename2,filename,".TXB");
273                 ifile = cfopen(filename2,"rb");
274                 if (!ifile)
275                         return 0;
276                 have_binary = 1;
277         }
278
279         size = cfilelength(ifile);
280    
281    MALLOC (subtitle_raw_data, ubyte, size+1);
282
283    read_count = cfread(subtitle_raw_data, 1, size, ifile);
284
285         cfclose(ifile);
286
287         subtitle_raw_data[size] = 0;
288
289         if (read_count != size) {
290                 free(subtitle_raw_data);
291                 return 0;
292         }
293
294         p = subtitle_raw_data;
295
296         while (p && p < subtitle_raw_data+size) {
297                 char *endp;
298
299                 endp = strchr(p,'\n'); 
300                 if (endp) {
301                         if (endp[-1] == '\r')
302                                 endp[-1] = 0;           //handle 0d0a pair
303                         *endp = 0;                      //string termintor
304                 }
305
306                 if (have_binary)
307                         decode_text_line(p);
308
309                 if (*p != ';') {
310                         Subtitles[Num_subtitles].first_frame = atoi(p);
311                         p = next_field(p); if (!p) continue;
312                         Subtitles[Num_subtitles].last_frame = atoi(p);
313                         p = next_field(p); if (!p) continue;
314                         Subtitles[Num_subtitles].msg = p;
315
316                         Assert(Num_subtitles==0 || Subtitles[Num_subtitles].first_frame >= Subtitles[Num_subtitles-1].first_frame);
317                         Assert(Subtitles[Num_subtitles].last_frame >= Subtitles[Num_subtitles].first_frame);
318
319                         Num_subtitles++;
320                 }
321
322                 p = endp+1;
323
324         }
325
326         return 1;
327
328 }
329
330 void close_subtitles()
331 {
332         if (subtitle_raw_data)
333                 free(subtitle_raw_data);
334         subtitle_raw_data = NULL;
335         Num_subtitles = 0;
336 }
337
338 typedef struct {
339         char name[FILENAME_LEN];
340         int offset,len;
341 } ml_entry;
342
343 #define MLF_ON_CD               0
344
345 typedef struct {
346         char            name[100];      //[FILENAME_LEN];
347         int             n_movies;
348         ubyte           flags,pad[3];
349         ml_entry        movies[1];
350 } movielib;
351
352 #define MAX_MOVIES_PER_LIB              50              //determines size of malloc
353
354 movielib *init_new_movie_lib(char *filename,FILE *fp)
355 {
356         int nfiles,offset;
357         int i,n;
358         movielib *table;
359
360         //read movie file header
361
362         fread(&nfiles,4,1,fp);          //get number of files
363
364         table = malloc(sizeof(*table) + sizeof(ml_entry)*nfiles);
365
366         strcpy(table->name,filename);
367         table->n_movies = nfiles;
368
369         offset = 4+4+nfiles*(13+4);     //id + nfiles + nfiles * (filename + size)
370
371         for (i=0;i<nfiles;i++) {
372                 int len;
373
374                 n = fread( table->movies[i].name, 13, 1, fp );
375                 if ( n != 1 )
376                         break;          //end of file (probably)
377
378                 n = fread( &len, 4, 1, fp );
379                 if ( n != 1 )
380                         Error("error reading movie library <%s>",filename);
381
382                 table->movies[i].len = INTEL_INT(len);
383                 table->movies[i].offset = offset;
384
385                 offset += table->movies[i].len;
386
387         }
388
389         fclose(fp);
390
391         table->flags = 0;
392
393         return table;
394
395 }
396
397 movielib *init_old_movie_lib(char *filename,FILE *fp)
398 {
399         int nfiles,size;
400         int i;
401         movielib *table,*table2;
402
403         nfiles = 0;
404
405         //allocate big table
406         table = malloc(sizeof(*table) + sizeof(ml_entry)*MAX_MOVIES_PER_LIB);
407
408         while( 1 ) {
409                 int len;
410
411                 i = fread( table->movies[nfiles].name, 13, 1, fp );
412                 if ( i != 1 )
413                         break;          //end of file (probably)
414
415                 i = fread( &len, 4, 1, fp );
416                 if ( i != 1 )
417                         Error("error reading movie library <%s>",filename);
418
419                 table->movies[nfiles].len = INTEL_INT(len);
420                 table->movies[nfiles].offset = ftell( fp );
421
422                 fseek( fp, INTEL_INT(len), SEEK_CUR );          //skip data
423
424                 nfiles++;
425         }
426
427         //allocate correct-sized table
428         size = sizeof(*table) + sizeof(ml_entry)*nfiles;
429         table2 = malloc(size);
430         memcpy(table2,table,size);
431         free(table);
432         table = table2;
433
434         strcpy(table->name,filename);
435
436         table->n_movies = nfiles;
437
438         fclose(fp);
439
440         table->flags = 0;
441
442         return table;
443
444 }
445
446 //find the specified movie library, and read in list of movies in it   
447 movielib *init_movie_lib(char *filename)
448 {
449         //note: this based on cfile_init_hogfile()
450
451         char id[4];
452         FILE * fp;
453  
454         fp = fopen( filename, "rb" );
455         if ( fp == NULL ) 
456                 return NULL;
457
458         fread( id, 4, 1, fp );
459         if ( !strncmp( id, "DMVL", 4 ) )
460                 return init_new_movie_lib(filename,fp);
461         else if ( !strncmp( id, "DHF", 3 ) ) {
462                 fseek(fp,-1,SEEK_CUR);          //old file had 3 char id
463                 return init_old_movie_lib(filename,fp);
464         }
465         else {
466                 fclose(fp);
467                 return NULL;
468         }
469 }
470
471 #ifdef D2_OEM
472 char *movielib_files[] = {"intro-l.mvl","other-l.mvl","robots-l.mvl","oem-l.mvl"};
473 #else
474 char *movielib_files[] = {"intro-l.mvl","other-l.mvl","robots-l.mvl"};
475 #endif
476
477 #define N_BUILTIN_MOVIE_LIBS (sizeof(movielib_files)/sizeof(*movielib_files))
478 #define N_MOVIE_LIBS (N_BUILTIN_MOVIE_LIBS+1)
479 #define EXTRA_ROBOT_LIB N_BUILTIN_MOVIE_LIBS
480 movielib *movie_libs[N_MOVIE_LIBS];
481
482 void close_movie(int i)
483 {
484         if (movie_libs[i])
485                 free(movie_libs[i]);
486 }
487
488 void close_movies()
489 {
490         int i;
491
492         for (i=0;i<N_MOVIE_LIBS;i++)
493                 close_movie(i);
494 }
495
496 #include "gamepal.h"
497
498 extern char CDROM_dir[];
499 extern int MenuHiresAvailable;
500
501 extern ubyte last_palette_for_color_fonts[];
502
503 extern int force_rb_register;
504
505 //ask user to put the D2 CD in.
506 //returns -1 if ESC pressed, 0 if OK chosen
507 //CD may not have been inserted
508 int request_cd()
509 {
510         ubyte save_pal[256*3];
511         grs_canvas *save_canv,*tcanv;
512         int ret,was_faded=gr_palette_faded_out;
513
514         gr_palette_clear();
515
516         save_canv = grd_curcanv;
517         tcanv = gr_create_canvas(grd_curcanv->cv_w,grd_curcanv->cv_h);
518
519         gr_set_current_canvas(tcanv);
520         gr_ubitmap(0,0,&save_canv->cv_bitmap);
521         gr_set_current_canvas(save_canv);
522
523         gr_clear_canvas(BM_XRGB(0,0,0));
524         
525         memcpy(save_pal,gr_palette,sizeof(save_pal));
526
527         memcpy(gr_palette,last_palette_for_color_fonts,sizeof(gr_palette));
528
529 try_again:;
530
531         ret = nm_messagebox( "CD ERROR", 1, "Ok", "Please insert your Descent II CD");
532
533         if (ret == -1) {
534                 int ret2;
535
536                 ret2 = nm_messagebox( "CD ERROR", 2, "Try Again", "Leave Game", "You must insert your\nDescent II CD to Continue");
537
538                 if (ret2 == -1 || ret2 == 0)
539                         goto try_again;
540         }
541
542         force_rb_register = 1;  //disc has changed; force register new CD    
543         
544         gr_palette_clear();
545
546         memcpy(gr_palette,save_pal,sizeof(save_pal));
547         
548         gr_ubitmap(0,0,&tcanv->cv_bitmap);
549
550         if (!was_faded)
551                 gr_palette_load(gr_palette);
552
553         gr_free_canvas(tcanv);
554
555         return ret;
556 }
557
558 //do we have the robot movies available
559 int robot_movies=0;     //0 means none, 1 means lowres, 2 means hires
560
561 void init_movie(char *filename,int libnum,int is_robots,int required)
562 {
563         int high_res;
564
565         #ifndef RELEASE
566         if (FindArg("-nomovies")) {
567                 movie_libs[libnum] = NULL;
568                 return;
569         }
570         #endif
571
572         //for robots, load highres versions if highres menus set
573         if (is_robots)
574                 high_res = MenuHiresAvailable;
575         else
576                 high_res = MovieHires;
577
578         if (high_res)
579                 strchr(filename,'.')[-1] = 'h';
580
581 #if defined(D2_OEM)
582 try_again:;
583 #endif
584
585         if ((movie_libs[libnum] = init_movie_lib(filename)) == NULL) {
586                 char name2[100];
587                 
588                 strcpy(name2,CDROM_dir);
589                 strcat(name2,filename);
590                 movie_libs[libnum] = init_movie_lib(name2);
591
592                 if (movie_libs[libnum] != NULL)
593                         movie_libs[libnum]->flags |= MLF_ON_CD;
594                 else {
595                         if (required) {
596                                 #if defined(RELEASE) && !defined(D2_OEM)                //allow no movies if not release
597                                         strupr(filename);
598                                         Error("Cannot open movie file <%s>",filename);
599                                 #endif
600                         }
601                         #if defined(D2_OEM)             //if couldn't get higres, try low
602                         if (is_robots == 1) {   //first try, try again with lowres
603                                 strchr(filename,'.')[-1] = 'l';
604                                 high_res = 0;
605                                 is_robots++;
606                                 goto try_again;
607                         }
608                         else if (is_robots == 2) {              //failed twice. bail with error
609                                 strupr(filename);
610                                 Error("Cannot open movie file <%s>",filename);
611                         }
612                         #endif
613                 }
614         }
615
616         if (is_robots && movie_libs[libnum]!=NULL)
617                 robot_movies = high_res?2:1;
618 }
619
620 //find and initialize the movie libraries
621 void init_movies()
622 {
623         int i;
624         int is_robots;
625
626         for (i=0;i<N_BUILTIN_MOVIE_LIBS;i++) {
627
628                 if (!strnicmp(movielib_files[i],"robot",5))
629                         is_robots = 1;
630                 else
631                         is_robots = 0;
632
633                 init_movie(movielib_files[i],i,is_robots,1);
634         }
635
636         movie_libs[EXTRA_ROBOT_LIB] = NULL;
637
638         atexit(close_movies);
639
640 }
641
642 void init_extra_robot_movie(char *filename)
643 {
644         close_movie(EXTRA_ROBOT_LIB);
645         init_movie(filename,EXTRA_ROBOT_LIB,1,0);
646 }
647
648 int movie_handle,movie_start;
649
650 //looks through a movie library for a movie file
651 //returns filehandle, with fileposition at movie, or -1 if can't find
652 int search_movie_lib(movielib *lib,char *filename,int must_have, int *lenp)
653 {
654         int i;
655         int filehandle;
656
657         if (lib == NULL)
658                 return -1;
659
660         for (i=0;i<lib->n_movies;i++)
661                 if (!stricmp(filename,lib->movies[i].name)) {   //found the movie in a library 
662                         int from_cd;
663
664                         *lenp = lib->movies[i].len;
665
666                         from_cd = (lib->flags & MLF_ON_CD);
667
668                         if (from_cd)
669                                 songs_stop_redbook();           //ready to read from CD
670
671                         do {            //keep trying until we get the file handle
672
673                                 movie_handle = filehandle = open(lib->name, O_RDONLY);
674
675                                 if (must_have && from_cd && filehandle == -1) {         //didn't get file!
676
677                                         if (request_cd() == -1)         //ESC from requester
678                                                 break;                                          //bail from here. will get error later
679                                 }
680
681                         } while (must_have && from_cd && filehandle == -1);
682
683                         if (filehandle != -1)
684                                 lseek(filehandle,(movie_start=lib->movies[i].offset),SEEK_SET);
685
686                         return filehandle;
687                 }
688
689         return -1;
690 }
691
692 //returns file handle
693 int open_movie_file(char *filename,int must_have, int *lenp)
694 {
695         int filehandle,i;
696
697         for (i=0;i<N_MOVIE_LIBS;i++) {
698
699                 if ((filehandle = search_movie_lib(movie_libs[i],filename,must_have, lenp)) != -1)
700                         return filehandle;
701         }
702
703         return -1;              //couldn't find it
704 }