This enables using -Wimplicit-fallthrough with ccache+distcc, which strip the fallthrough comments, but do not strip the attribute annotationns.
726 lines
17 KiB
C++
726 lines
17 KiB
C++
/*
|
|
* Portions of this file are copyright Rebirth contributors and licensed as
|
|
* described in COPYING.txt.
|
|
* Portions of this file are copyright Parallax Software and licensed
|
|
* according to the Parallax license below.
|
|
* See COPYING.txt for license details.
|
|
|
|
THE COMPUTER CODE CONTAINED HEREIN IS THE SOLE PROPERTY OF PARALLAX
|
|
SOFTWARE CORPORATION ("PARALLAX"). PARALLAX, IN DISTRIBUTING THE CODE TO
|
|
END-USERS, AND SUBJECT TO ALL OF THE TERMS AND CONDITIONS HEREIN, GRANTS A
|
|
ROYALTY-FREE, PERPETUAL LICENSE TO SUCH END-USERS FOR USE BY SUCH END-USERS
|
|
IN USING, DISPLAYING, AND CREATING DERIVATIVE WORKS THEREOF, SO LONG AS
|
|
SUCH USE, DISPLAY OR CREATION IS FOR NON-COMMERCIAL, ROYALTY OR REVENUE
|
|
FREE PURPOSES. IN NO EVENT SHALL THE END-USER USE THE COMPUTER CODE
|
|
CONTAINED HEREIN FOR REVENUE-BEARING PURPOSES. THE END-USER UNDERSTANDS
|
|
AND AGREES TO THE TERMS HEREIN AND ACCEPTS THE SAME BY USE OF THIS FILE.
|
|
COPYRIGHT 1993-1999 PARALLAX SOFTWARE CORPORATION. ALL RIGHTS RESERVED.
|
|
*/
|
|
|
|
/*
|
|
*
|
|
* Movie Playing Stuff
|
|
*
|
|
*/
|
|
|
|
#include <string.h>
|
|
#ifndef macintosh
|
|
# include <sys/types.h>
|
|
# include <sys/stat.h>
|
|
# include <fcntl.h>
|
|
# ifndef _MSC_VER
|
|
# include <unistd.h>
|
|
# endif
|
|
#endif // ! macintosh
|
|
#include <ctype.h>
|
|
|
|
#include "movie.h"
|
|
#include "window.h"
|
|
#include "console.h"
|
|
#include "config.h"
|
|
#include "physfsx.h"
|
|
#include "key.h"
|
|
#include "mouse.h"
|
|
#include "digi.h"
|
|
#include "songs.h"
|
|
#include "inferno.h"
|
|
#include "palette.h"
|
|
#include "strutil.h"
|
|
#include "dxxerror.h"
|
|
#include "u_mem.h"
|
|
#include "gr.h"
|
|
#include "gamefont.h"
|
|
#include "menu.h"
|
|
#include "libmve.h"
|
|
#include "text.h"
|
|
#include "screens.h"
|
|
#include "physfsrwops.h"
|
|
#if DXX_USE_OGL
|
|
#include "ogl_init.h"
|
|
#endif
|
|
#include "args.h"
|
|
|
|
#include "compiler-range_for.h"
|
|
|
|
namespace {
|
|
|
|
// Subtitle data
|
|
struct subtitle {
|
|
typename std::conditional<sizeof(char *) == sizeof(uint32_t), uint16_t, uint32_t>::type first_frame, last_frame;
|
|
const char *msg;
|
|
};
|
|
|
|
#define MAX_ACTIVE_SUBTITLES 3
|
|
|
|
struct d_subtitle_state
|
|
{
|
|
unsigned Num_subtitles = 0;
|
|
std::unique_ptr<char[]> subtitle_raw_data;
|
|
array<subtitle, 500> Subtitles;
|
|
};
|
|
|
|
static int init_subtitles(d_subtitle_state &SubtitleState, const char *filename);
|
|
|
|
// Movielib data
|
|
|
|
constexpr array<array<char, 8>, 3> movielib_files{{
|
|
{"intro"}, {"other"}, {"robots"}
|
|
}};
|
|
|
|
struct loaded_movie_t
|
|
{
|
|
array<char, FILENAME_LEN + 2> filename;
|
|
};
|
|
|
|
static loaded_movie_t extra_robot_movie_mission;
|
|
|
|
static RWops_ptr RoboFile;
|
|
|
|
// Function Prototypes
|
|
static int RunMovie(const char *filename, const char *subtitles, int highres_flag, int allow_abort,int dx,int dy);
|
|
|
|
static void draw_subtitles(const d_subtitle_state &, int frame_num);
|
|
|
|
// ----------------------------------------------------------------------
|
|
static void* MPlayAlloc(size_t size)
|
|
{
|
|
return d_malloc(size);
|
|
}
|
|
|
|
static void MPlayFree(void *p)
|
|
{
|
|
d_free(p);
|
|
}
|
|
|
|
//-----------------------------------------------------------------------
|
|
|
|
static unsigned int FileRead(void *handle, void *buf, unsigned int count)
|
|
{
|
|
unsigned numread;
|
|
numread = SDL_RWread(reinterpret_cast<SDL_RWops *>(handle), buf, 1, count);
|
|
return (numread == count);
|
|
}
|
|
|
|
}
|
|
|
|
//-----------------------------------------------------------------------
|
|
|
|
|
|
//filename will actually get modified to be either low-res or high-res
|
|
//returns status. see values in movie.h
|
|
int PlayMovie(const char *subtitles, const char *filename, int must_have)
|
|
{
|
|
char name[FILENAME_LEN],*p;
|
|
int ret;
|
|
|
|
if (GameArg.SysNoMovies)
|
|
return MOVIE_NOT_PLAYED;
|
|
|
|
strcpy(name,filename);
|
|
|
|
if ((p=strchr(name,'.')) == NULL) //add extension, if missing
|
|
strcat(name,".MVE");
|
|
|
|
// Stop all digital sounds currently playing.
|
|
digi_stop_digi_sounds();
|
|
|
|
// Stop all songs
|
|
songs_stop_all();
|
|
|
|
// MD2211: if using SDL_Mixer, we never reinit the sound system
|
|
if (CGameArg.SndDisableSdlMixer)
|
|
digi_close();
|
|
|
|
// Start sound
|
|
MVE_sndInit(!CGameArg.SndNoSound ? 1 : -1);
|
|
|
|
ret = RunMovie(name, subtitles, !GameArg.GfxSkipHiresMovie, must_have, -1, -1);
|
|
|
|
// MD2211: if using SDL_Mixer, we never reinit the sound system
|
|
if (!CGameArg.SndNoSound
|
|
&& CGameArg.SndDisableSdlMixer
|
|
)
|
|
digi_init();
|
|
|
|
Screen_mode = -1; //force screen reset
|
|
|
|
return ret;
|
|
}
|
|
|
|
namespace {
|
|
|
|
static void MovieShowFrame(ubyte *buf, int dstx, int dsty, int bufw, int bufh, int sw, int sh)
|
|
{
|
|
grs_bitmap source_bm;
|
|
static palette_array_t old_pal;
|
|
float scale = 1.0;
|
|
|
|
if (old_pal != gr_palette)
|
|
{
|
|
old_pal = gr_palette;
|
|
return;
|
|
}
|
|
old_pal = gr_palette;
|
|
|
|
source_bm.bm_x = source_bm.bm_y = 0;
|
|
source_bm.bm_w = source_bm.bm_rowsize = bufw;
|
|
source_bm.bm_h = bufh;
|
|
source_bm.set_type(bm_mode::linear);
|
|
source_bm.clear_flags();
|
|
source_bm.bm_data = buf;
|
|
|
|
if (dstx == -1 && dsty == -1) // Fullscreen movie so set scale to fit the actual screen size
|
|
{
|
|
if ((static_cast<float>(SWIDTH)/SHEIGHT) < (static_cast<float>(sw)/bufh))
|
|
scale = (static_cast<float>(SWIDTH)/sw);
|
|
else
|
|
scale = (static_cast<float>(SHEIGHT)/bufh);
|
|
}
|
|
else // Other (robot) movie so set scale to min. screen dimension
|
|
{
|
|
if ((static_cast<float>(SWIDTH)/bufw) < (static_cast<float>(SHEIGHT)/bufh))
|
|
scale = (static_cast<float>(SWIDTH)/sw);
|
|
else
|
|
scale = (static_cast<float>(SHEIGHT)/sh);
|
|
}
|
|
|
|
if (dstx == -1) // center it
|
|
dstx = (SWIDTH/2)-((bufw*scale)/2);
|
|
if (dsty == -1) // center it
|
|
dsty = (SHEIGHT/2)-((bufh*scale)/2);
|
|
|
|
#if DXX_USE_OGL
|
|
glDisable (GL_BLEND);
|
|
|
|
ogl_ubitblt_i(
|
|
bufw*scale, bufh*scale,
|
|
dstx, dsty,
|
|
bufw, bufh, 0, 0, source_bm, grd_curcanv->cv_bitmap, (GameCfg.MovieTexFilt)?OGL_TEXFILT_TRLINEAR:OGL_TEXFILT_CLASSIC);
|
|
|
|
glEnable (GL_BLEND);
|
|
#else
|
|
gr_bm_ubitbltm(*grd_curcanv, bufw, bufh, dstx, dsty, 0, 0, source_bm);
|
|
#endif
|
|
}
|
|
|
|
//our routine to set the pallete, called from the movie code
|
|
static void MovieSetPalette(const unsigned char *p, unsigned start, unsigned count)
|
|
{
|
|
if (count == 0)
|
|
return;
|
|
|
|
//Color 0 should be black, and we get color 255
|
|
Assert(start>=1 && start+count-1<=254);
|
|
|
|
//Set color 0 to be black
|
|
gr_palette[0].r = gr_palette[0].g = gr_palette[0].b = 0;
|
|
|
|
//Set color 255 to be our subtitle color
|
|
gr_palette[255].r = gr_palette[255].g = gr_palette[255].b = 50;
|
|
|
|
//movie libs palette into our array
|
|
memcpy(&gr_palette[start],p+start*3,count*3);
|
|
}
|
|
|
|
struct movie : ignore_window_pointer_t
|
|
{
|
|
MVE_StepStatus result;
|
|
int aborted;
|
|
int frame_num;
|
|
int paused;
|
|
MVESTREAM_ptr_t pMovie;
|
|
d_subtitle_state SubtitleState;
|
|
};
|
|
|
|
static window_event_result show_pause_message(window *, const d_event &event, const unused_window_userdata_t *)
|
|
{
|
|
window_event_result result;
|
|
|
|
switch (event.type)
|
|
{
|
|
case EVENT_MOUSE_BUTTON_DOWN:
|
|
if (event_mouse_get_button(event) != 0)
|
|
return window_event_result::ignored;
|
|
DXX_BOOST_FALLTHROUGH;
|
|
case EVENT_KEY_COMMAND:
|
|
if ((result = call_default_handler(event)) == window_event_result::ignored)
|
|
{
|
|
return window_event_result::close;
|
|
}
|
|
return result;
|
|
|
|
case EVENT_WINDOW_DRAW:
|
|
{
|
|
const char *msg = TXT_PAUSE;
|
|
int h;
|
|
int y;
|
|
|
|
gr_set_default_canvas();
|
|
auto &canvas = *grd_curcanv;
|
|
const auto &game_font = *GAME_FONT;
|
|
gr_get_string_size(game_font, msg, nullptr, &h, nullptr);
|
|
|
|
y = (grd_curscreen->get_screen_height() - h) / 2;
|
|
|
|
gr_set_fontcolor(canvas, 255, -1);
|
|
|
|
gr_ustring(canvas, game_font, 0x8000, y, msg);
|
|
break;
|
|
}
|
|
|
|
default:
|
|
break;
|
|
}
|
|
return window_event_result::ignored;
|
|
}
|
|
|
|
static window_event_result MovieHandler(window *, const d_event &event, movie *m)
|
|
{
|
|
int key;
|
|
|
|
switch (event.type)
|
|
{
|
|
case EVENT_WINDOW_ACTIVATED:
|
|
m->paused = 0;
|
|
break;
|
|
|
|
case EVENT_WINDOW_DEACTIVATED:
|
|
m->paused = 1;
|
|
MVE_rmHoldMovie();
|
|
break;
|
|
|
|
case EVENT_KEY_COMMAND:
|
|
key = event_key_get(event);
|
|
|
|
// If ESCAPE pressed, then quit movie.
|
|
if (key == KEY_ESC) {
|
|
m->result = MVE_StepStatus::EndOfFile;
|
|
m->aborted = 1;
|
|
return window_event_result::close;
|
|
}
|
|
|
|
// If PAUSE pressed, then pause movie
|
|
if ((key == KEY_PAUSE) || (key == KEY_COMMAND + KEY_P))
|
|
{
|
|
if (window_create(grd_curscreen->sc_canvas, 0, 0, SWIDTH, SHEIGHT, show_pause_message, unused_window_userdata))
|
|
MVE_rmHoldMovie();
|
|
return window_event_result::handled;
|
|
}
|
|
break;
|
|
|
|
case EVENT_WINDOW_DRAW:
|
|
if (!m->paused)
|
|
{
|
|
m->result = MVE_rmStepMovie(*m->pMovie.get());
|
|
if (m->result != MVE_StepStatus::Continue)
|
|
{
|
|
return window_event_result::close;
|
|
}
|
|
}
|
|
|
|
draw_subtitles(m->SubtitleState, m->frame_num);
|
|
|
|
gr_palette_load(gr_palette);
|
|
|
|
if (!m->paused)
|
|
m->frame_num++;
|
|
break;
|
|
|
|
case EVENT_WINDOW_CLOSE:
|
|
if (Quitting)
|
|
{
|
|
m->result = MVE_StepStatus::EndOfFile;
|
|
m->aborted = 1;
|
|
}
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
return window_event_result::ignored;
|
|
}
|
|
|
|
//returns status. see movie.h
|
|
int RunMovie(const char *const filename, const char *const subtitles, const int hires_flag, const int must_have, const int dx, const int dy)
|
|
{
|
|
movie m;
|
|
int track = 0;
|
|
int aborted = 0;
|
|
#if DXX_USE_OGL
|
|
palette_array_t pal_save;
|
|
#endif
|
|
|
|
init_subtitles(m.SubtitleState, subtitles);
|
|
|
|
m.result = MVE_StepStatus::EndOfFile;
|
|
m.aborted = 0;
|
|
m.frame_num = 0;
|
|
m.paused = 0;
|
|
|
|
// Open Movie file. If it doesn't exist, no movie, just return.
|
|
|
|
auto filehndl = PHYSFSRWOPS_openRead(filename);
|
|
if (!filehndl)
|
|
{
|
|
con_printf(must_have ? CON_URGENT : CON_VERBOSE, "Failed to open movie <%s>: %s", filename, PHYSFS_getLastError());
|
|
return MOVIE_NOT_PLAYED;
|
|
}
|
|
const auto reshow = hide_menus();
|
|
const auto wind = window_create(grd_curscreen->sc_canvas, 0, 0, SWIDTH, SHEIGHT, MovieHandler, &m);
|
|
if (!wind)
|
|
{
|
|
if (reshow)
|
|
show_menus();
|
|
return MOVIE_NOT_PLAYED;
|
|
}
|
|
|
|
MVE_memCallbacks(MPlayAlloc, MPlayFree);
|
|
MVE_ioCallbacks(FileRead);
|
|
|
|
#if DXX_USE_OGL
|
|
set_screen_mode(SCREEN_MOVIE);
|
|
gr_copy_palette(pal_save, gr_palette);
|
|
gr_palette_load(gr_palette);
|
|
(void)hires_flag;
|
|
#else
|
|
gr_set_mode(hires_flag ? screen_mode{640, 480} : screen_mode{320, 200});
|
|
#endif
|
|
MVE_sfCallbacks(MovieShowFrame);
|
|
MVE_palCallbacks(MovieSetPalette);
|
|
|
|
if (MVE_rmPrepMovie(m.pMovie, filehndl.get(), dx, dy, track)) {
|
|
Int3();
|
|
window_close(wind);
|
|
if (reshow)
|
|
show_menus();
|
|
return MOVIE_NOT_PLAYED;
|
|
}
|
|
|
|
MVE_sfCallbacks(MovieShowFrame);
|
|
MVE_palCallbacks(MovieSetPalette);
|
|
|
|
do {
|
|
event_process_all();
|
|
} while(window_get_front() == wind);
|
|
|
|
assert(m.aborted || m.result == MVE_StepStatus::EndOfFile); ///movie should be over
|
|
|
|
m.pMovie.reset();
|
|
|
|
filehndl.reset(); // Close Movie File
|
|
if (reshow)
|
|
show_menus();
|
|
aborted = m.aborted;
|
|
|
|
// Restore old graphic state
|
|
|
|
Screen_mode=-1; //force reset of screen mode
|
|
#if DXX_USE_OGL
|
|
gr_copy_palette(gr_palette, pal_save);
|
|
gr_palette_load(pal_save);
|
|
#endif
|
|
|
|
return (aborted?MOVIE_ABORTED:MOVIE_PLAYED_FULL);
|
|
}
|
|
|
|
}
|
|
|
|
//returns 1 if frame updated ok
|
|
int RotateRobot(MVESTREAM_ptr_t &pMovie)
|
|
{
|
|
auto err = MVE_rmStepMovie(*pMovie.get());
|
|
gr_palette_load(gr_palette);
|
|
|
|
if (err == MVE_StepStatus::EndOfFile) //end of movie, so reset
|
|
{
|
|
SDL_RWseek(RoboFile.get(), 0, SEEK_SET);
|
|
if (MVE_rmPrepMovie(pMovie, RoboFile.get(), SWIDTH/2.3, SHEIGHT/2.3, 0))
|
|
{
|
|
Int3();
|
|
return 0;
|
|
}
|
|
err = MVE_rmStepMovie(*pMovie.get());
|
|
}
|
|
if (err != MVE_StepStatus::Continue)
|
|
{
|
|
Int3();
|
|
return 0;
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
|
|
void DeInitRobotMovie(MVESTREAM_ptr_t &pMovie)
|
|
{
|
|
pMovie.reset();
|
|
RoboFile.reset(); // Close Movie File
|
|
}
|
|
|
|
|
|
int InitRobotMovie(const char *filename, MVESTREAM_ptr_t &pMovie)
|
|
{
|
|
if (GameArg.SysNoMovies)
|
|
return 0;
|
|
|
|
con_printf(CON_DEBUG, "RoboFile=%s", filename);
|
|
|
|
MVE_memCallbacks(MPlayAlloc, MPlayFree);
|
|
MVE_ioCallbacks(FileRead);
|
|
MVE_sfCallbacks(MovieShowFrame);
|
|
MVE_palCallbacks(MovieSetPalette);
|
|
MVE_sndInit(-1); //tell movies to play no sound for robots
|
|
|
|
RoboFile = PHYSFSRWOPS_openRead(filename);
|
|
|
|
if (!RoboFile)
|
|
{
|
|
con_printf(CON_URGENT, "Can't open movie <%s>: %s", filename, PHYSFS_getLastError());
|
|
return MOVIE_NOT_PLAYED;
|
|
}
|
|
if (MVE_rmPrepMovie(pMovie, RoboFile.get(), SWIDTH/2.3, SHEIGHT/2.3, 0)) {
|
|
Int3();
|
|
return 0;
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
namespace {
|
|
|
|
/*
|
|
* Subtitle system code
|
|
*/
|
|
|
|
//search for next field following whitespace
|
|
static char *next_field (char *p)
|
|
{
|
|
while (*p && !isspace(*p))
|
|
p++;
|
|
|
|
if (!*p)
|
|
return NULL;
|
|
|
|
while (*p && isspace(*p))
|
|
p++;
|
|
|
|
if (!*p)
|
|
return NULL;
|
|
|
|
return p;
|
|
}
|
|
|
|
static int init_subtitles(d_subtitle_state &SubtitleState, const char *const filename)
|
|
{
|
|
if (!filename)
|
|
return 0;
|
|
int size,read_count;
|
|
char *p;
|
|
int have_binary = 0;
|
|
|
|
SubtitleState.Num_subtitles = 0;
|
|
|
|
if (!GameCfg.MovieSubtitles)
|
|
{
|
|
con_puts(CON_VERBOSE, "Rebirth: movie subtitles are disabled");
|
|
return 0;
|
|
}
|
|
|
|
auto ifile = PHYSFSX_openReadBuffered(filename); //try text version
|
|
|
|
if (!ifile) { //no text version, try binary version
|
|
char filename2[FILENAME_LEN];
|
|
change_filename_extension(filename2, filename, ".txb");
|
|
ifile = PHYSFSX_openReadBuffered(filename2);
|
|
if (!ifile)
|
|
{
|
|
con_printf(CON_VERBOSE, "Rebirth: skipping subtitles because cannot open \"%s\" or \"%s\"", filename, filename2);
|
|
return 0;
|
|
}
|
|
have_binary = 1;
|
|
con_printf(CON_VERBOSE, "Rebirth: found encoded subtitles in \"%s\"", filename2);
|
|
}
|
|
else
|
|
con_printf(CON_VERBOSE, "Rebirth: found text subtitles in \"%s\"", filename);
|
|
|
|
size = PHYSFS_fileLength(ifile);
|
|
|
|
const auto subtitle_raw_data = (SubtitleState.subtitle_raw_data = make_unique<char[]>(size + 1)).get();
|
|
read_count = PHYSFS_read(ifile, subtitle_raw_data, 1, size);
|
|
ifile.reset();
|
|
|
|
if (read_count != size) {
|
|
con_puts(CON_VERBOSE, "Rebirth: skipping subtitles because cannot read full subtitle file");
|
|
return 0;
|
|
}
|
|
|
|
subtitle_raw_data[size] = 0;
|
|
p = subtitle_raw_data;
|
|
|
|
while (p && p < subtitle_raw_data+size) {
|
|
char *endp;
|
|
|
|
endp = strchr(p,'\n');
|
|
if (endp) {
|
|
if (endp[-1] == '\r')
|
|
endp[-1] = 0; //handle 0d0a pair
|
|
*endp = 0; //string termintor
|
|
}
|
|
|
|
if (have_binary)
|
|
decode_text_line(p);
|
|
|
|
if (*p != ';') {
|
|
const auto Num_subtitles = SubtitleState.Num_subtitles;
|
|
auto &Subtitles = SubtitleState.Subtitles;
|
|
auto &s = Subtitles[SubtitleState.Num_subtitles++];
|
|
s.first_frame = atoi(p);
|
|
p = next_field(p); if (!p) continue;
|
|
s.last_frame = atoi(p);
|
|
p = next_field(p); if (!p) continue;
|
|
s.msg = p;
|
|
|
|
if (Num_subtitles)
|
|
{
|
|
assert(s.first_frame >= Subtitles[Num_subtitles - 1].first_frame);
|
|
}
|
|
assert(s.last_frame >= s.first_frame);
|
|
}
|
|
|
|
p = endp+1;
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
//draw the subtitles for this frame
|
|
static void draw_subtitles(const d_subtitle_state &SubtitleState, const int frame_num)
|
|
{
|
|
static int active_subtitles[MAX_ACTIVE_SUBTITLES];
|
|
static int next_subtitle;
|
|
static unsigned num_active_subtitles;
|
|
int y;
|
|
int must_erase=0;
|
|
|
|
if (frame_num == 0) {
|
|
num_active_subtitles = 0;
|
|
next_subtitle = 0;
|
|
gr_set_curfont(*grd_curcanv, GAME_FONT);
|
|
gr_set_fontcolor(*grd_curcanv, 255, -1);
|
|
}
|
|
|
|
//get rid of any subtitles that have expired
|
|
auto &Subtitles = SubtitleState.Subtitles;
|
|
for (int t=0;t<num_active_subtitles;)
|
|
if (frame_num > Subtitles[active_subtitles[t]].last_frame) {
|
|
int t2;
|
|
for (t2=t;t2<num_active_subtitles-1;t2++)
|
|
active_subtitles[t2] = active_subtitles[t2+1];
|
|
num_active_subtitles--;
|
|
must_erase = 1;
|
|
}
|
|
else
|
|
t++;
|
|
|
|
//get any subtitles new for this frame
|
|
while (next_subtitle < SubtitleState.Num_subtitles && frame_num >= Subtitles[next_subtitle].first_frame) {
|
|
if (num_active_subtitles >= MAX_ACTIVE_SUBTITLES)
|
|
Error("Too many active subtitles!");
|
|
active_subtitles[num_active_subtitles++] = next_subtitle;
|
|
next_subtitle++;
|
|
}
|
|
|
|
//find y coordinate for first line of subtitles
|
|
const auto &&line_spacing = LINE_SPACING(*grd_curcanv->cv_font, *GAME_FONT);
|
|
y = grd_curcanv->cv_bitmap.bm_h - (line_spacing * (MAX_ACTIVE_SUBTITLES + 2));
|
|
|
|
//erase old subtitles if necessary
|
|
if (must_erase) {
|
|
gr_rect(*grd_curcanv, 0,y,grd_curcanv->cv_bitmap.bm_w-1,grd_curcanv->cv_bitmap.bm_h-1, 0);
|
|
}
|
|
|
|
//now draw the current subtitles
|
|
range_for (const auto &t, partial_range(active_subtitles, num_active_subtitles))
|
|
if (t != -1)
|
|
{
|
|
gr_string(*grd_curcanv, *grd_curcanv->cv_font, 0x8000, y, Subtitles[t].msg);
|
|
y += line_spacing;
|
|
}
|
|
}
|
|
|
|
static int init_movie(const char *movielib, char resolution, int required, loaded_movie_t &movie)
|
|
{
|
|
snprintf(&movie.filename[0], movie.filename.size(), "%s-%c.mvl", movielib, resolution);
|
|
auto r = PHYSFSX_contfile_init(&movie.filename[0], 0);
|
|
if (!r)
|
|
{
|
|
if (required || CGameArg.DbgVerbose)
|
|
con_printf(CON_URGENT, "Can't open movielib <%s>: %s", &movie.filename[0], PHYSFS_getLastError());
|
|
movie.filename[0] = 0;
|
|
}
|
|
return r;
|
|
}
|
|
|
|
static void init_movie(const char *movielib, int required, loaded_movie_t &movie)
|
|
{
|
|
if (!GameArg.GfxSkipHiresMovie)
|
|
{
|
|
if (init_movie(movielib, 'h', required, movie))
|
|
return;
|
|
}
|
|
init_movie(movielib, 'l', required, movie);
|
|
}
|
|
|
|
}
|
|
|
|
//find and initialize the movie libraries
|
|
void init_movies()
|
|
{
|
|
if (GameArg.SysNoMovies)
|
|
return;
|
|
|
|
range_for (auto &i, movielib_files)
|
|
{
|
|
loaded_movie_t m;
|
|
init_movie(&i[0], 1, m);
|
|
}
|
|
}
|
|
|
|
void close_extra_robot_movie()
|
|
{
|
|
const auto movielib = &extra_robot_movie_mission.filename[0];
|
|
if (!*movielib)
|
|
return;
|
|
if (!PHYSFSX_contfile_close(movielib))
|
|
{
|
|
con_printf(CON_URGENT, "Can't close movielib <%s>: %s", movielib, PHYSFS_getLastError());
|
|
}
|
|
*movielib = 0;
|
|
}
|
|
|
|
void init_extra_robot_movie(const char *movielib)
|
|
{
|
|
if (GameArg.SysNoMovies)
|
|
return;
|
|
init_movie(movielib, 0, extra_robot_movie_mission);
|
|
}
|