]> icculus.org git repositories - btb/d2x.git/blob - main/movie.c
preserve movie aspect ratio, keep centered vertically
[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  *
16  * Movie Playing Stuff
17  *
18  */
19
20 #ifdef HAVE_CONFIG_H
21 #include <conf.h>
22 #endif
23
24 #include <string.h>
25 #ifndef macintosh
26 # ifndef _WIN32_WCE
27 #  include <sys/types.h>
28 #  include <sys/stat.h>
29 #  include <fcntl.h>
30 # endif
31 # ifndef _MSC_VER
32 #  include <unistd.h>
33 # endif
34 #endif // ! macintosh
35 #include <ctype.h>
36
37 #include "args.h"
38 #include "key.h"
39 #include "inferno.h"
40 #include "strutil.h"
41 #include "error.h"
42 #include "u_mem.h"
43 #include "byteswap.h"
44 #include "gr.h"
45 #include "vid.h"
46 #include "cfile.h"
47 #include "libmve.h"
48 #include "physfsrwops.h"
49
50
51 extern int MenuHiresAvailable;
52
53 // Subtitle data
54 typedef struct {
55         short first_frame,last_frame;
56         char *msg;
57 } subtitle;
58
59 #define MAX_SUBTITLES 500
60 #define MAX_ACTIVE_SUBTITLES 3
61 subtitle Subtitles[MAX_SUBTITLES];
62 int Num_subtitles;
63
64 // Movielib data
65
66 #ifdef D2_OEM
67 char movielib_files[5][FILENAME_LEN] = {"intro","other","robots","oem"};
68 #else
69 char movielib_files[4][FILENAME_LEN] = {"intro","other","robots"};
70 #endif
71
72 #define N_MOVIE_LIBS (sizeof(movielib_files) / sizeof(*movielib_files))
73 #define N_BUILTIN_MOVIE_LIBS (N_MOVIE_LIBS - 1)
74 #define EXTRA_ROBOT_LIB N_BUILTIN_MOVIE_LIBS
75
76 cvar_t MovieHires = { "MovieHires", "1", 1 }; //default is highres
77
78 SDL_RWops *RoboFile;
79 MVE_videoSpec MVESpec;
80
81 // Function Prototypes
82 int RunMovie(char *filename, int highres_flag, int allow_abort,int dx,int dy);
83
84 void decode_text_line(char *p);
85 void draw_subtitles(int frame_num);
86
87
88 // ----------------------------------------------------------------------
89 void* MPlayAlloc(unsigned size)
90 {
91     return d_malloc(size);
92 }
93
94 void MPlayFree(void *p)
95 {
96     d_free(p);
97 }
98
99
100 //-----------------------------------------------------------------------
101
102 unsigned int FileRead(void *handle, void *buf, unsigned int count)
103 {
104     unsigned numread;
105     numread = SDL_RWread((SDL_RWops *)handle, buf, 1, count);
106     return (numread == count);
107 }
108
109
110 //-----------------------------------------------------------------------
111
112
113 //filename will actually get modified to be either low-res or high-res
114 //returns status.  see values in movie.h
115 int PlayMovie(const char *filename, int must_have)
116 {
117         char name[FILENAME_LEN],*p;
118         int c, ret;
119
120         if (FindArg("-nomovies"))
121                 return MOVIE_NOT_PLAYED;
122
123         strcpy(name,filename);
124
125         if ((p=strchr(name,'.')) == NULL)               //add extension, if missing
126                 strcat(name,".mve");
127
128         //check for escape already pressed & abort if so
129         while ((c = newmenu_inkey()) != 0)
130                 if (c == KEY_ESC)
131                         return MOVIE_ABORTED;
132
133         // Stop all digital sounds currently playing.
134         digi_stop_all();
135
136         // Stop all songs
137         songs_stop_all();
138
139         digi_close();
140
141         // Start sound
142         if (!FindArg("-nosound"))
143                 MVE_sndInit(1);
144         else
145                 MVE_sndInit(-1);
146
147         ret = RunMovie(name, MovieHires.intval, must_have, -1, -1);
148
149         if (!FindArg("-nosound"))
150                 digi_init();
151
152         Screen_mode = -1;               //force screen reset
153
154         return ret;
155 }
156
157
158 void MovieShowFrame(ubyte *buf, uint bufw, uint bufh, uint sx, uint sy,
159                                         uint w, uint h, uint dstx, uint dsty)
160 {
161         grs_bitmap source_bm;
162         grs_canvas *dest_canv, *save_canv;
163
164         //mprintf((0,"MovieShowFrame %d,%d  %d,%d  %d,%d  %d,%d\n",bufw,bufh,sx,sy,w,h,dstx,dsty));
165
166         Assert(bufw == w && bufh == h);
167
168         source_bm.bm_x = source_bm.bm_y = 0;
169         source_bm.bm_w = source_bm.bm_rowsize = bufw;
170         source_bm.bm_h = bufh;
171         source_bm.bm_type = BM_LINEAR;
172         source_bm.bm_flags = 0;
173         source_bm.bm_data = buf;
174
175         if (menu_use_game_res.intval) {
176                 float aspect = (float)w / (float)h;
177
178                 w = w * GWIDTH / MVESpec.screenWidth;
179                 h = w / aspect;
180                 dstx = dstx * GWIDTH / MVESpec.screenWidth;
181                 dsty = GHEIGHT / 2 - h / 2;
182
183                 dest_canv = gr_create_sub_canvas(grd_curcanv, dstx, dsty, w, h);
184                 save_canv = grd_curcanv;
185                 gr_set_current_canvas(dest_canv);
186                 show_fullscr(&source_bm);
187                 gr_set_current_canvas(save_canv);
188                 gr_free_sub_canvas(dest_canv);
189         } else
190                 gr_bm_ubitblt(bufw,bufh,dstx,dsty,sx,sy,&source_bm,&grd_curcanv->cv_bitmap);
191 }
192
193 //our routine to set the pallete, called from the movie code
194 void MovieSetPalette(unsigned char *p, unsigned start, unsigned count)
195 {
196         if (count == 0)
197                 return;
198
199         //mprintf((0,"SetPalette p=%x, start=%d, count=%d\n",p,start,count));
200
201         //Color 0 should be black, and we get color 255
202         Assert(start>=1 && start+count-1<=254);
203
204         //Set color 0 to be black
205         gr_palette[0] = gr_palette[1] = gr_palette[2] = 0;
206
207         //Set color 255 to be our subtitle color
208         gr_palette[765] = gr_palette[766] = gr_palette[767] = 50;
209
210         //movie libs palette into our array
211         memcpy(gr_palette+start*3,p+start*3,count*3);
212
213         //finally set the palette in the hardware
214         gr_palette_load(gr_palette);
215
216         //MVE_SetPalette(p, start, count);
217 }
218
219
220 #if 0
221 typedef struct bkg {
222         short x, y, w, h;           // The location of the menu.
223         grs_bitmap * bmp;               // The background under the menu.
224 } bkg;
225
226 bkg movie_bg = {0,0,0,0,NULL};
227 #endif
228
229 #define BOX_BORDER (MenuHires?40:20)
230
231
232 void show_pause_message(char *msg)
233 {
234         int w,h,aw;
235         int x,y;
236
237         gr_set_current_canvas(NULL);
238         gr_set_curfont( SMALL_FONT );
239
240         gr_get_string_size(msg,&w,&h,&aw);
241
242         x = (grd_curscreen->sc_w-w)/2;
243         y = (grd_curscreen->sc_h-h)/2;
244
245 #if 0
246         if (movie_bg.bmp) {
247                 gr_free_bitmap(movie_bg.bmp);
248                 movie_bg.bmp = NULL;
249         }
250
251         // Save the background of the display
252         movie_bg.x=x; movie_bg.y=y; movie_bg.w=w; movie_bg.h=h;
253
254         movie_bg.bmp = gr_create_bitmap( w+BOX_BORDER, h+BOX_BORDER );
255
256         gr_bm_ubitblt(w+BOX_BORDER, h+BOX_BORDER, 0, 0, x-BOX_BORDER/2, y-BOX_BORDER/2, &(grd_curcanv->cv_bitmap), movie_bg.bmp );
257 #endif
258
259         gr_setcolor(0);
260         gr_rect(x-BOX_BORDER/2,y-BOX_BORDER/2,x+w+BOX_BORDER/2-1,y+h+BOX_BORDER/2-1);
261
262         gr_set_fontcolor( 255, -1 );
263
264         gr_ustring( 0x8000, y, msg );
265
266         vid_update();
267 }
268
269 void clear_pause_message()
270 {
271 #if 0
272         if (movie_bg.bmp) {
273
274                 gr_bitmap(movie_bg.x-BOX_BORDER/2, movie_bg.y-BOX_BORDER/2, movie_bg.bmp);
275
276                 gr_free_bitmap(movie_bg.bmp);
277                 movie_bg.bmp = NULL;
278         }
279 #endif
280 }
281
282
283 //returns status.  see movie.h
284 int RunMovie(char *filename, int hires_flag, int must_have,int dx,int dy)
285 {
286         SDL_RWops *filehndl;
287         int result=1,aborted=0;
288         int track = 0;
289         int frame_num;
290         int key;
291
292         result=1;
293
294         // Open Movie file.  If it doesn't exist, no movie, just return.
295
296         filehndl = PHYSFSRWOPS_openRead(filename);
297
298         if (!filehndl)
299         {
300                 if (must_have)
301                         con_printf(CON_URGENT, "Can't open movie <%s>: %s\n", filename, PHYSFS_getLastError());
302                 return MOVIE_NOT_PLAYED;
303         }
304
305         MVE_memCallbacks(MPlayAlloc, MPlayFree);
306         MVE_ioCallbacks(FileRead);
307         MVE_sfCallbacks(MovieShowFrame);
308         MVE_palCallbacks(MovieSetPalette);
309
310         vid_set_mode(MOVIE_SCREEN_MODE);
311 #ifdef OGL
312         set_screen_mode(SCREEN_MENU);
313 #endif
314
315         if (MVE_rmPrepMovie((void *)filehndl, dx, dy, track)) {
316                 Int3();
317                 return MOVIE_NOT_PLAYED;
318         }
319
320         MVE_getVideoSpec(&MVESpec);
321
322         frame_num = 0;
323
324         FontHires = FontHiresAvailable && hires_flag;
325
326         while((result = MVE_rmStepMovie()) == 0) {
327
328                 draw_subtitles(frame_num);
329
330                 vid_update();
331
332                 key = newmenu_inkey();
333
334                 // If ESCAPE pressed, then quit movie.
335                 if (key == KEY_ESC) {
336                         result = aborted = 1;
337                         break;
338                 }
339
340                 // If PAUSE pressed, then pause movie
341                 if (key == KEY_PAUSE) {
342                         MVE_rmHoldMovie();
343                         show_pause_message(TXT_PAUSE);
344                         while (!newmenu_inkey()) ;
345                         clear_pause_message();
346                 }
347
348 #ifdef VID_SUPPORTS_FULLSCREEN_TOGGLE
349                 if ((key == KEY_COMMAND+KEY_SHIFTED+KEY_F) ||
350                         (key == KEY_ALTED+KEY_ENTER) ||
351                     (key == KEY_ALTED+KEY_PADENTER))
352                         vid_toggle_fullscreen();
353 #endif
354
355                 frame_num++;
356         }
357
358         Assert(aborted || result == MVE_ERR_EOF);        ///movie should be over
359
360     MVE_rmEndMovie();
361
362         SDL_RWclose(filehndl); // Close Movie File
363
364         // Restore old graphic state
365
366         Screen_mode=-1;  //force reset of screen mode
367
368         return (aborted?MOVIE_ABORTED:MOVIE_PLAYED_FULL);
369 }
370
371
372 int InitMovieBriefing()
373 {
374 #if 0
375         if (MenuHires)
376                 vid_set_mode(SM(640,480));
377         else
378                 vid_set_mode(SM(320,200));
379
380         gr_init_sub_canvas( &VR_screen_pages[0], &grd_curscreen->sc_canvas, 0, 0, grd_curscreen->sc_w, grd_curscreen->sc_h );
381         gr_init_sub_canvas( &VR_screen_pages[1], &grd_curscreen->sc_canvas, 0, 0, grd_curscreen->sc_w, grd_curscreen->sc_h );
382 #endif
383
384         return 1;
385 }
386
387
388 //returns 1 if frame updated ok
389 int RotateRobot()
390 {
391         int err;
392
393         err = MVE_rmStepMovie();
394
395         if (err == MVE_ERR_EOF)     //end of movie, so reset
396         {
397                 SDL_RWseek(RoboFile, 0, SEEK_SET);
398                 if (MVE_rmPrepMovie(RoboFile, MenuHires?280:140, MenuHires?200:80, 0))
399                 {
400                         Int3();
401                         return 0;
402                 }
403         }
404         else if (err) {
405                 Int3();
406                 return 0;
407         }
408
409         return 1;
410 }
411
412
413 void DeInitRobotMovie(void)
414 {
415         MVE_rmEndMovie();
416         SDL_RWclose(RoboFile); // Close Movie File
417 }
418
419
420 int InitRobotMovie(char *filename)
421 {
422         if (FindArg("-nomovies"))
423                 return 0;
424
425         con_printf(CON_DEBUG, "RoboFile=%s\n", filename);
426
427         MVE_sndInit(-1);        //tell movies to play no sound for robots
428
429         RoboFile = PHYSFSRWOPS_openRead(filename);
430
431         if (!RoboFile)
432         {
433                 con_printf(CON_URGENT, "Can't open movie <%s>: %s\n", filename, PHYSFS_getLastError());
434                 return MOVIE_NOT_PLAYED;
435         }
436
437         if (MVE_rmPrepMovie((void *)RoboFile, MenuHires?280:140, MenuHires?200:80, 0)) {
438                 Int3();
439                 return 0;
440         }
441
442         return 1;
443 }
444
445
446 /*
447  *              Subtitle system code
448  */
449
450 char *subtitle_raw_data;
451
452
453 //search for next field following whitespace 
454 char *next_field (char *p)
455 {
456         while (*p && !isspace(*p))
457                 p++;
458
459         if (!*p)
460                 return NULL;
461
462         while (*p && isspace(*p))
463                 p++;
464
465         if (!*p)
466                 return NULL;
467
468         return p;
469 }
470
471
472 int init_subtitles(char *filename)
473 {
474         CFILE *ifile;
475         int size,read_count;
476         char *p;
477         int have_binary = 0;
478
479         Num_subtitles = 0;
480
481         if (! FindArg("-subtitles"))
482                 return 0;
483
484         ifile = cfopen(filename,"rb");          //try text version
485
486         if (!ifile) {                                                           //no text version, try binary version
487                 char filename2[FILENAME_LEN];
488                 change_filename_extension(filename2, filename, ".TXB");
489                 ifile = cfopen(filename2,"rb");
490                 if (!ifile)
491                         return 0;
492                 have_binary = 1;
493         }
494
495         size = cfilelength(ifile);
496
497         MALLOC (subtitle_raw_data, char, size+1);
498
499         read_count = cfread(subtitle_raw_data, 1, size, ifile);
500
501         cfclose(ifile);
502
503         subtitle_raw_data[size] = 0;
504
505         if (read_count != size) {
506                 d_free(subtitle_raw_data);
507                 return 0;
508         }
509
510         p = subtitle_raw_data;
511
512         while (p && p < subtitle_raw_data+size) {
513                 char *endp;
514
515                 endp = strchr(p,'\n'); 
516                 if (endp) {
517                         if (endp[-1] == '\r')
518                                 endp[-1] = 0;           //handle 0d0a pair
519                         *endp = 0;                      //string termintor
520                 }
521
522                 if (have_binary)
523                         decode_text_line(p);
524
525                 if (*p != ';') {
526                         Subtitles[Num_subtitles].first_frame = atoi(p);
527                         p = next_field(p); if (!p) continue;
528                         Subtitles[Num_subtitles].last_frame = atoi(p);
529                         p = next_field(p); if (!p) continue;
530                         Subtitles[Num_subtitles].msg = p;
531
532                         Assert(Num_subtitles==0 || Subtitles[Num_subtitles].first_frame >= Subtitles[Num_subtitles-1].first_frame);
533                         Assert(Subtitles[Num_subtitles].last_frame >= Subtitles[Num_subtitles].first_frame);
534
535                         Num_subtitles++;
536                 }
537
538                 p = endp+1;
539
540         }
541
542         return 1;
543 }
544
545
546 void close_subtitles()
547 {
548         if (subtitle_raw_data)
549                 d_free(subtitle_raw_data);
550         subtitle_raw_data = NULL;
551         Num_subtitles = 0;
552 }
553
554
555 //draw the subtitles for this frame
556 void draw_subtitles(int frame_num)
557 {
558         static int active_subtitles[MAX_ACTIVE_SUBTITLES];
559         static int num_active_subtitles,next_subtitle,line_spacing;
560         int t,y;
561         int must_erase=0;
562
563         if (frame_num == 0) {
564                 num_active_subtitles = 0;
565                 next_subtitle = 0;
566                 gr_set_curfont( GAME_FONT );
567                 line_spacing = grd_curcanv->cv_font->ft_h + (grd_curcanv->cv_font->ft_h >> 2);
568                 gr_set_fontcolor(255,-1);
569         }
570
571         //get rid of any subtitles that have expired
572         for (t=0;t<num_active_subtitles;)
573                 if (frame_num > Subtitles[active_subtitles[t]].last_frame) {
574                         int t2;
575                         for (t2=t;t2<num_active_subtitles-1;t2++)
576                                 active_subtitles[t2] = active_subtitles[t2+1];
577                         num_active_subtitles--;
578                         must_erase = 1;
579                 }
580                 else
581                         t++;
582
583         //get any subtitles new for this frame 
584         while (next_subtitle < Num_subtitles && frame_num >= Subtitles[next_subtitle].first_frame) {
585                 if (num_active_subtitles >= MAX_ACTIVE_SUBTITLES)
586                         Error("Too many active subtitles!");
587                 active_subtitles[num_active_subtitles++] = next_subtitle;
588                 next_subtitle++;
589         }
590
591         //find y coordinate for first line of subtitles
592         y = grd_curcanv->cv_bitmap.bm_h-((line_spacing+1)*MAX_ACTIVE_SUBTITLES+2);
593
594         //erase old subtitles if necessary
595         if (must_erase) {
596                 gr_setcolor(0);
597                 gr_rect(0,y,grd_curcanv->cv_bitmap.bm_w-1,grd_curcanv->cv_bitmap.bm_h-1);
598         }
599
600         //now draw the current subtitles
601         for (t=0;t<num_active_subtitles;t++)
602                 if (active_subtitles[t] != -1) {
603                         gr_string(0x8000,y,Subtitles[active_subtitles[t]].msg);
604                         y += line_spacing+1;
605                 }
606 }
607
608
609 void close_movie(char *movielib, int is_robots)
610 {
611         int high_res;
612         char filename[FILENAME_LEN];
613
614         if (is_robots)
615                 high_res = MenuHiresAvailable;
616         else
617                 high_res = MovieHires.intval;
618
619         sprintf(filename, "%s-%s.mvl", movielib, high_res?"h":"l");
620
621         if (!cfile_close(filename))
622         {
623                 con_printf(CON_URGENT, "Can't close movielib <%s>: %s\n", filename, PHYSFS_getLastError());
624                 sprintf(filename, "%s-%s.mvl", movielib, high_res?"l":"h");
625
626                 if (!cfile_close(filename))
627                         con_printf(CON_URGENT, "Can't close movielib <%s>: %s\n", filename, PHYSFS_getLastError());
628         }
629 }
630
631 void close_movies()
632 {
633         int i, is_robots;
634
635         for (i = 0 ; i < N_BUILTIN_MOVIE_LIBS ; i++)
636         {
637                 if (!strnicmp(movielib_files[i], "robot", 5))
638                         is_robots = 1;
639                 else
640                         is_robots = 0;
641
642                 close_movie(movielib_files[i], is_robots);
643         }
644 }
645
646
647 void init_movie(char *movielib, int is_robots, int required)
648 {
649         int high_res;
650         char filename[FILENAME_LEN];
651
652         //for robots, load highres versions if highres menus set
653         if (is_robots)
654                 high_res = MenuHiresAvailable;
655         else
656                 high_res = MovieHires.intval;
657
658         sprintf(filename, "%s-%s.mvl", movielib, high_res?"h":"l");
659
660         if (!cfile_init(filename))
661         {
662                 if (required)
663                         con_printf(CON_URGENT, "Can't open movielib <%s>: %s\n", filename, PHYSFS_getLastError());
664
665                 sprintf(filename, "%s-%s.mvl", movielib, high_res?"l":"h");
666
667                 if (!cfile_init(filename))
668                         if (required)
669                                 con_printf(CON_URGENT, "Can't open movielib <%s>: %s\n", filename, PHYSFS_getLastError());
670         }
671 }
672
673
674 //find and initialize the movie libraries
675 void init_movies()
676 {
677         int i;
678         int is_robots;
679
680         if (FindArg("-nomovies"))
681                 return;
682
683         for (i=0;i<N_BUILTIN_MOVIE_LIBS;i++) {
684
685                 if (!strnicmp(movielib_files[i],"robot",5))
686                         is_robots = 1;
687                 else
688                         is_robots = 0;
689
690                 init_movie(movielib_files[i], is_robots, 1);
691         }
692
693         atexit(close_movies);
694 }
695
696
697 void close_extra_robot_movie(void)
698 {
699         if (strlen(movielib_files[EXTRA_ROBOT_LIB]))
700                 if (!cfile_close(movielib_files[EXTRA_ROBOT_LIB]))
701                         con_printf(CON_URGENT, "Can't close robot movielib: %s\n", PHYSFS_getLastError());
702 }
703
704 void init_extra_robot_movie(char *movielib)
705 {
706         if (FindArg("-nomovies"))
707                 return;
708
709         close_extra_robot_movie();
710         init_movie(movielib, 1, 0);
711         strcpy(movielib_files[EXTRA_ROBOT_LIB], movielib);
712         atexit(close_extra_robot_movie);
713 }