diff options
| author | Colin Snover | 2016-07-27 21:00:37 -0500 | 
|---|---|---|
| committer | Colin Snover | 2016-08-11 20:50:33 -0500 | 
| commit | 13297c19298c5ad73c9e996c5c31ca91de124911 (patch) | |
| tree | 0e5525f85a47e3343d05770c96a2fcc0ac85926c | |
| parent | c28a5733e0911f071f02f3d14aec0dfe53f1aa05 (diff) | |
| download | scummvm-rg350-13297c19298c5ad73c9e996c5c31ca91de124911.tar.gz scummvm-rg350-13297c19298c5ad73c9e996c5c31ca91de124911.tar.bz2 scummvm-rg350-13297c19298c5ad73c9e996c5c31ca91de124911.zip  | |
SCI32: Implement kShowMovie
| -rw-r--r-- | engines/sci/engine/kernel.h | 11 | ||||
| -rw-r--r-- | engines/sci/engine/kernel_tables.h | 29 | ||||
| -rw-r--r-- | engines/sci/engine/kmisc.cpp | 16 | ||||
| -rw-r--r-- | engines/sci/engine/kvideo.cpp | 166 | ||||
| -rw-r--r-- | engines/sci/engine/vm.cpp | 15 | ||||
| -rw-r--r-- | engines/sci/graphics/video32.cpp | 390 | ||||
| -rw-r--r-- | engines/sci/graphics/video32.h | 204 | 
7 files changed, 744 insertions, 87 deletions
diff --git a/engines/sci/engine/kernel.h b/engines/sci/engine/kernel.h index 5ff4f932be..b02a7e545a 100644 --- a/engines/sci/engine/kernel.h +++ b/engines/sci/engine/kernel.h @@ -450,6 +450,17 @@ reg_t kPlayVMDShowCursor(EngineState *s, int argc, reg_t *argv);  reg_t kPlayVMDSetBlackoutArea(EngineState *s, int argc, reg_t *argv);  reg_t kPlayVMDRestrictPalette(EngineState *s, int argc, reg_t *argv); +reg_t kShowMovie32(EngineState *s, int argc, reg_t *argv); +reg_t kShowMovieWin(EngineState *s, int argc, reg_t *argv); +reg_t kShowMovieWinOpen(EngineState *s, int argc, reg_t *argv); +reg_t kShowMovieWinInit(EngineState *s, int argc, reg_t *argv); +reg_t kShowMovieWinPlay(EngineState *s, int argc, reg_t *argv); +reg_t kShowMovieWinClose(EngineState *s, int argc, reg_t *argv); +reg_t kShowMovieWinCue(EngineState *s, int argc, reg_t *argv); +reg_t kShowMovieWinGetDuration(EngineState *s, int argc, reg_t *argv); +reg_t kShowMovieWinPlayUntilEvent(EngineState *s, int argc, reg_t *argv); +reg_t kShowMovieWinInitDouble(EngineState *s, int argc, reg_t *argv); +  reg_t kIsHiRes(EngineState *s, int argc, reg_t *argv);  reg_t kArray(EngineState *s, int argc, reg_t *argv);  reg_t kListAt(EngineState *s, int argc, reg_t *argv); diff --git a/engines/sci/engine/kernel_tables.h b/engines/sci/engine/kernel_tables.h index e0e4dcc233..76c24b09e3 100644 --- a/engines/sci/engine/kernel_tables.h +++ b/engines/sci/engine/kernel_tables.h @@ -423,6 +423,27 @@ static const SciKernelMapSubEntry kList_subops[] = {  	SCI_SUBOPENTRY_TERMINATOR  }; +//    version,         subId, function-mapping,                    signature,              workarounds +static const SciKernelMapSubEntry kShowMovieWin_subops[] = { +	{ SIG_SCI2,            0, MAP_CALL(ShowMovieWinOpen),          "r",                    NULL }, +	{ SIG_SCI2,            1, MAP_CALL(ShowMovieWinInit),          "ii(ii)",               NULL }, +	{ SIG_SCI2,            2, MAP_CALL(ShowMovieWinPlay),          "i",                    NULL }, +	{ SIG_SCI2,            6, MAP_CALL(ShowMovieWinClose),         "",                     NULL }, +	{ SIG_SINCE_SCI21,     0, MAP_CALL(ShowMovieWinOpen),          "ir",                   NULL }, +	{ SIG_SINCE_SCI21,     1, MAP_CALL(ShowMovieWinInit),          "iii(ii)",              NULL }, +	{ SIG_SINCE_SCI21,     2, MAP_CALL(ShowMovieWinPlay),          "i(ii)(i)(i)",          NULL }, +	{ SIG_SINCE_SCI21,     6, MAP_CALL(ShowMovieWinClose),         "i",                    NULL }, +	// Since movies are rendered within the graphics engine in ScummVM, +	// it is not necessary to copy the palette from SCI to MCI, so this +	// can be a no-op +	{ SIG_SINCE_SCI21,     7, MAP_EMPTY(ShowMovieWinSetPalette),   "i",                    NULL }, +	{ SIG_SINCE_SCI21,     8, MAP_CALL(ShowMovieWinGetDuration),   "i",                    NULL }, +	{ SIG_SINCE_SCI21,    11, MAP_CALL(ShowMovieWinCue),           "ii",                   NULL }, +	{ SIG_SINCE_SCI21,    14, MAP_CALL(ShowMovieWinPlayUntilEvent), "i(i)",                NULL }, +	{ SIG_SINCE_SCI21,    15, MAP_CALL(ShowMovieWinInitDouble),    "iii",                  NULL }, +	SCI_SUBOPENTRY_TERMINATOR +}; +  // There are a lot of subops to PlayVMD, but only a few of them are ever  // actually used by games  //    version,         subId, function-mapping,                    signature,              workarounds @@ -688,7 +709,11 @@ static SciKernelMapEntry s_kernelMap[] = {  	{ MAP_CALL(SetSynonyms),       SIG_EVERYWHERE,           "o",                     NULL,            NULL },  	{ MAP_CALL(SetVideoMode),      SIG_EVERYWHERE,           "i",                     NULL,            NULL },  	{ MAP_CALL(ShakeScreen),       SIG_EVERYWHERE,           "(i)(i)",                NULL,            NULL }, -	{ MAP_CALL(ShowMovie),         SIG_EVERYWHERE,           "(.*)",                  NULL,            NULL }, +	{ MAP_CALL(ShowMovie),         SIG_SCI16, SIGFOR_ALL,    "(.*)",                  NULL,            NULL }, +#ifdef ENABLE_SCI32 +	{ "ShowMovie", kShowMovie32,   SIG_SCI32, SIGFOR_DOS,    "ri(i)(i)",              NULL,            NULL }, +	{ "ShowMovie", kShowMovieWin,  SIG_SCI32, SIGFOR_WIN,    "(.*)",                  kShowMovieWin_subops, NULL }, +#endif  	{ MAP_CALL(Show),              SIG_EVERYWHERE,           "i",                     NULL,            NULL },  	{ MAP_CALL(SinDiv),            SIG_EVERYWHERE,           "ii",                    NULL,            NULL },  	{ MAP_CALL(Sort),              SIG_EVERYWHERE,           "ooo",                   NULL,            NULL }, @@ -820,7 +845,7 @@ static SciKernelMapEntry s_kernelMap[] = {  	{ MAP_CALL(List),              SIG_SINCE_SCI21, SIGFOR_ALL, "(.*)",               kList_subops,    NULL },  	{ MAP_CALL(MulDiv),            SIG_EVERYWHERE,           "iii",                   NULL,            NULL },  	{ MAP_CALL(PlayVMD),           SIG_EVERYWHERE,           "(.*)",                  kPlayVMD_subops, NULL }, -	{ MAP_CALL(Robot),             SIG_EVERYWHERE,           "(.*)",                  NULL,            NULL }, +	{ MAP_EMPTY(Robot),            SIG_EVERYWHERE,           "(.*)",                  NULL,            NULL },  	{ MAP_CALL(Save),              SIG_EVERYWHERE,           "i(.*)",                 kSave_subops,    NULL },  	{ MAP_CALL(Text),              SIG_SINCE_SCI21MID, SIGFOR_ALL, "i(.*)",           kText_subops,    NULL },  	{ MAP_CALL(AddPicAt),          SIG_EVERYWHERE,           "oiii(i)(i)",            NULL,            NULL }, diff --git a/engines/sci/engine/kmisc.cpp b/engines/sci/engine/kmisc.cpp index 1924848717..c99540967c 100644 --- a/engines/sci/engine/kmisc.cpp +++ b/engines/sci/engine/kmisc.cpp @@ -20,6 +20,7 @@   *   */ +#include "common/config-manager.h"  #include "common/system.h"  #include "sci/sci.h" @@ -542,7 +543,7 @@ enum kSciPlatforms {  enum kPlatformOps {  	kPlatformUnk0 = 0,  	kPlatformCDSpeed = 1, -	kPlatformUnk2 = 2, +	kPlatformColorDepth = 2,  	kPlatformCDCheck = 3,  	kPlatformGetPlatform = 4,  	kPlatformUnk5 = 5, @@ -563,11 +564,6 @@ reg_t kPlatform(EngineState *s, int argc, reg_t *argv) {  		return NULL_REG;  	} -	if (g_sci->forceHiresGraphics()) { -		// force Windows platform, so that hires-graphics are enabled -		isWindows = true; -	} -  	uint16 operation = (argc == 0) ? 0 : argv[0].toUint16();  	switch (operation) { @@ -575,9 +571,9 @@ reg_t kPlatform(EngineState *s, int argc, reg_t *argv) {  		// TODO: Returns CD Speed?  		warning("STUB: kPlatform(CDSpeed)");  		break; -	case kPlatformUnk2: +	case kPlatformColorDepth:  		// Always returns 2 -		return make_reg(0, 2); +		return make_reg(0, /* 256-color */ 2);  	case kPlatformCDCheck:  		// TODO: Some sort of CD check?  		warning("STUB: kPlatform(CDCheck)"); @@ -591,9 +587,9 @@ reg_t kPlatform(EngineState *s, int argc, reg_t *argv) {  		return make_reg(0, (isWindows) ? kSciPlatformWindows : kSciPlatformDOS);  	case kPlatformUnk5:  		// This case needs to return the opposite of case 6 to get hires graphics -		return make_reg(0, !isWindows); +		return make_reg(0, !ConfMan.getBool("enable_high_resolution_graphics"));  	case kPlatformIsHiRes: -		return make_reg(0, isWindows); +		return make_reg(0, ConfMan.getBool("enable_high_resolution_graphics"));  	case kPlatformIsItWindows:  		return make_reg(0, isWindows);  	default: diff --git a/engines/sci/engine/kvideo.cpp b/engines/sci/engine/kvideo.cpp index de4d4a282c..86d8a4b817 100644 --- a/engines/sci/engine/kvideo.cpp +++ b/engines/sci/engine/kvideo.cpp @@ -61,33 +61,15 @@ void playVideo(Video::VideoDecoder *videoDecoder, VideoState videoState) {  	uint16 screenWidth = g_sci->_gfxScreen->getDisplayWidth();  	uint16 screenHeight = g_sci->_gfxScreen->getDisplayHeight(); -	videoState.fileName.toLowercase(); -	bool isVMD = videoState.fileName.hasSuffix(".vmd"); - -	if (screenWidth == 640 && width <= 320 && height <= 240 && ((videoState.flags & kDoubled) || !isVMD)) { +	if (screenWidth == 640 && width <= 320 && height <= 240) {  		width *= 2;  		height *= 2;  		pitch *= 2;  		scaleBuffer = new byte[width * height * bytesPerPixel];  	} -	uint16 x, y; - -	// Sanity check... -	if (videoState.x > 0 && videoState.y > 0 && isVMD) { -		x = videoState.x; -		y = videoState.y; - -		if (x + width > screenWidth || y + height > screenHeight) { -			// Happens in the Lighthouse demo -			warning("VMD video won't fit on screen, centering it instead"); -			x = (screenWidth - width) / 2; -			y = (screenHeight - height) / 2; -		} -	} else { -		x = (screenWidth - width) / 2; -		y = (screenHeight - height) / 2; -	} +	uint16 x = (screenWidth - width) / 2; +	uint16 y = (screenHeight - height) / 2;  	bool skipVideo = false;  	EngineState *s = g_sci->getEngineState(); @@ -181,16 +163,6 @@ reg_t kShowMovie(EngineState *s, int argc, reg_t *argv) {  		// TODO: This appears to be some sort of subop. case 0 contains the string  		// for the video, so we'll just play it from there for now. -#ifdef ENABLE_SCI32 -		if (getSciVersion() >= SCI_VERSION_2_1_EARLY) { -			// SCI2.1 always has argv[0] as 1, the rest of the arguments seem to -			// follow SCI1.1/2. -			if (argv[0].toUint16() != 1) -				error("SCI2.1 kShowMovie argv[0] not 1"); -			argv++; -			argc--; -		} -#endif  		switch (argv[0].toUint16()) {  		case 0: {  			Common::String filename = s->_segMan->getString(argv[1]); @@ -243,52 +215,102 @@ reg_t kShowMovie(EngineState *s, int argc, reg_t *argv) {  }  #ifdef ENABLE_SCI32 +reg_t kShowMovie32(EngineState *s, int argc, reg_t *argv) { +	Common::String fileName = s->_segMan->getString(argv[0]); +	const int16 numTicks = argv[1].toSint16(); +	const int16 x = argc > 3 ? argv[2].toSint16() : 0; +	const int16 y = argc > 3 ? argv[3].toSint16() : 0; -reg_t kRobot(EngineState *s, int argc, reg_t *argv) { -	int16 subop = argv[0].toUint16(); - -	switch (subop) { -	case 0: { // init -		int id = argv[1].toUint16(); -		reg_t obj = argv[2]; -		int16 flag = argv[3].toSint16(); -		int16 x = argv[4].toUint16(); -		int16 y = argv[5].toUint16(); -		warning("kRobot(init), id %d, obj %04x:%04x, flag %d, x=%d, y=%d", id, PRINT_REG(obj), flag, x, y); -		g_sci->_robotDecoder->load(id); -		g_sci->_robotDecoder->start(); -		g_sci->_robotDecoder->setPos(x, y); -		} -		break; -	case 1:	// LSL6 hires (startup) -		// TODO -		return NULL_REG;	// an integer is expected -	case 4: {	// start - we don't really have a use for this one -			//int id = argv[1].toUint16(); -			//warning("kRobot(start), id %d", id); -		} -		break; -	case 7:	// unknown, called e.g. by Phantasmagoria -		warning("kRobot(%d)", subop); -		break; -	case 8: // sync -		//if (true) {	// debug: automatically skip all robot videos -		if (g_sci->_robotDecoder->endOfVideo()) { -			g_sci->_robotDecoder->close(); -			// Signal the engine scripts that the video is done -			writeSelector(s->_segMan, argv[1], SELECTOR(signal), SIGNAL_REG); -		} else { -			writeSelector(s->_segMan, argv[1], SELECTOR(signal), NULL_REG); -		} -		break; -	default: -		warning("kRobot(%d)", subop); -		break; -	} +	g_sci->_video32->getSEQPlayer().play(fileName, numTicks, x, y);  	return s->r_acc;  } +reg_t kShowMovieWin(EngineState *s, int argc, reg_t *argv) { +	if (!s) +		return make_reg(0, getSciVersion()); +	error("not supposed to call this"); +} + +reg_t kShowMovieWinOpen(EngineState *s, int argc, reg_t *argv) { +	// SCI2.1 adds a movie ID to the call, but the movie ID is broken, +	// so just ignore it +	if (getSciVersion() > SCI_VERSION_2) { +		++argv; +		--argc; +	} + +	const Common::String fileName = s->_segMan->getString(argv[0]); +	return make_reg(0, g_sci->_video32->getAVIPlayer().open(fileName)); +} + +reg_t kShowMovieWinInit(EngineState *s, int argc, reg_t *argv) { +	// SCI2.1 adds a movie ID to the call, but the movie ID is broken, +	// so just ignore it +	if (getSciVersion() > SCI_VERSION_2) { +		++argv; +		--argc; +	} + +	const int16 x = argv[0].toSint16(); +	const int16 y = argv[1].toSint16(); +	const int16 width = argc > 3 ? argv[2].toSint16() : 0; +	const int16 height = argc > 3 ? argv[3].toSint16() : 0; +	return make_reg(0, g_sci->_video32->getAVIPlayer().init1x(x, y, width, height)); +} + +reg_t kShowMovieWinPlay(EngineState *s, int argc, reg_t *argv) { +	if (getSciVersion() == SCI_VERSION_2) { +		AVIPlayer::EventFlags flags = (AVIPlayer::EventFlags)argv[0].toUint16(); +		return make_reg(0, g_sci->_video32->getAVIPlayer().playUntilEvent(flags)); +	} else { +		// argv[0] is a broken movie ID +		const int16 from = argc > 2 ? argv[1].toSint16() : 0; +		const int16 to = argc > 2 ? argv[2].toSint16() : 0; +		const int16 showStyle = argc > 3 ? argv[3].toSint16() : 0; +		const bool cue = argc > 4 ? (bool)argv[4].toSint16() : false; +		return make_reg(0, g_sci->_video32->getAVIPlayer().play(from, to, showStyle, cue)); +	} +} + +reg_t kShowMovieWinClose(EngineState *s, int argc, reg_t *argv) { +	return make_reg(0, g_sci->_video32->getAVIPlayer().close()); +} + +reg_t kShowMovieWinGetDuration(EngineState *s, int argc, reg_t *argv) { +	return make_reg(0, g_sci->_video32->getAVIPlayer().getDuration()); +} + +reg_t kShowMovieWinCue(EngineState *s, int argc, reg_t *argv) { +	// SCI2.1 adds a movie ID to the call, but the movie ID is broken, +	// so just ignore it +	if (getSciVersion() > SCI_VERSION_2) { +		++argv; +		--argc; +	} + +	const uint16 frameNo = argv[0].toUint16(); +	return make_reg(0, g_sci->_video32->getAVIPlayer().cue(frameNo)); +} + +reg_t kShowMovieWinPlayUntilEvent(EngineState *s, int argc, reg_t *argv) { +	const int defaultFlags = +		AVIPlayer::kEventFlagEnd | +		AVIPlayer::kEventFlagEscapeKey; + +	// argv[0] is the movie number, which is not used by this method +	const AVIPlayer::EventFlags flags = (AVIPlayer::EventFlags)(argc > 1 ? argv[1].toUint16() : defaultFlags); + +	return make_reg(0, g_sci->_video32->getAVIPlayer().playUntilEvent(flags)); +} + +reg_t kShowMovieWinInitDouble(EngineState *s, int argc, reg_t *argv) { +	// argv[0] is a broken movie ID +	const int16 x = argv[1].toSint16(); +	const int16 y = argv[2].toSint16(); +	return make_reg(0, g_sci->_video32->getAVIPlayer().init2x(x, y)); +} +  reg_t kPlayVMD(EngineState *s, int argc, reg_t *argv) {  	if (!s)  		return make_reg(0, getSciVersion()); diff --git a/engines/sci/engine/vm.cpp b/engines/sci/engine/vm.cpp index 3e12084ed6..548fd477bf 100644 --- a/engines/sci/engine/vm.cpp +++ b/engines/sci/engine/vm.cpp @@ -405,6 +405,21 @@ static void callKernelFunc(EngineState *s, int kernelCallNr, int argc) {  			error("[VM] k%s[%x]: no subfunction ID parameter given", kernelCall.name, kernelCallNr);  		if (argv[0].isPointer())  			error("[VM] k%s[%x]: given subfunction ID is actually a pointer", kernelCall.name, kernelCallNr); + +#ifdef ENABLE_SCI32 +		// The Windows version of kShowMovie has subops, but the subop number +		// is put in the second parameter in SCI2.1+, even though every other +		// kcall with subops puts the subop in the first parameter. To allow use +		// of the normal subops system, we swap the arguments so the subop +		// number is in the usual place. +		if (getSciVersion() > SCI_VERSION_2 && +			g_sci->getPlatform() == Common::kPlatformWindows && +			strcmp(kernelCall.name, "ShowMovie") == 0) { +			assert(argc > 1); +			SWAP(argv[0], argv[1]); +		} +#endif +  		const uint16 subId = argv[0].toUint16();  		// Skip over subfunction-id  		argc--; diff --git a/engines/sci/graphics/video32.cpp b/engines/sci/graphics/video32.cpp index dc2641c92a..ac4522c1a0 100644 --- a/engines/sci/graphics/video32.cpp +++ b/engines/sci/graphics/video32.cpp @@ -29,11 +29,397 @@  #include "sci/graphics/palette32.h"  #include "sci/graphics/text32.h"  #include "sci/graphics/video32.h" -#include "sci/sci.h" +#include "audio/mixer.h"                 // for Audio::Mixer::kSFXSoundType +#include "common/config-manager.h"       // for ConfMan +#include "common/textconsole.h"          // for error, warning +#include "common/system.h" +#include "engine.h"                      // for Engine, g_engine +#include "engines/util.h" +#include "graphics/scaler/scalebit.h" +#include "sci/console.h"                 // for Console +#include "sci/engine/state.h"            // for EngineState +#include "sci/engine/vm_types.h"         // for reg_t +#include "sci/event.h"                   // for SciEvent, EventManager, SCI_... +#include "sci/graphics/celobj32.h"       // for CelInfo32 +#include "sci/graphics/cursor.h"         // for GfxCursor +#include "sci/graphics/helpers.h"        // for Color, Palette +#include "sci/graphics/palette32.h"      // for GfxPalette32 +#include "sci/graphics/plane32.h"        // for Plane, PlanePictureCodes::kP... +#include "sci/graphics/screen_item32.h"  // for ScaleInfo, ScreenItem, Scale... +#include "sci/graphics/text32.h"         // for BitmapResource +#include "sci/video/seq_decoder.h" +#include "sci/sci.h"                     // for SciEngine, g_sci, getSciVersion +#include "video/avi_decoder.h"  #include "video/coktel_decoder.h"  namespace Sci { +#pragma mark SEQPlayer + +SEQPlayer::SEQPlayer(SegManager *segMan) : +	_segMan(segMan), +	_decoder(nullptr), +	_plane(nullptr), +	_screenItem(nullptr) {} + +void SEQPlayer::play(const Common::String &fileName, const int16 numTicks, const int16 x, const int16 y) { +	delete _decoder; +	_decoder = new SEQDecoder(numTicks); +	_decoder->loadFile(fileName); + +	// NOTE: In the original engine, video was output directly to the hardware, +	// bypassing the game's rendering engine. Instead of doing this, we use a +	// mechanism that is very similar to that used by the VMD player, which +	// allows the SEQ to be drawn into a bitmap ScreenItem and displayed using +	// the normal graphics system. +	_segMan->allocateBitmap(&_bitmap, _decoder->getWidth(), _decoder->getHeight(), kDefaultSkipColor, 0, 0, kLowResX, kLowResY, 0, false, false); + +	CelInfo32 celInfo; +	celInfo.type = kCelTypeMem; +	celInfo.bitmap = _bitmap; + +	_plane = new Plane(Common::Rect(kLowResX, kLowResY), kPlanePicColored); +	g_sci->_gfxFrameout->addPlane(*_plane); + +	// Normally we would use the x, y coordinates passed into the play function +	// to position the screen item, but because the video frame bitmap is +	// drawn in low-resolution coordinates, it gets automatically scaled up by +	// the engine (pixel doubling with aspect ratio correction). As a result, +	// the animation does not need the extra offsets from the game in order to +	// be correctly positioned in the middle of the window, so we ignore them. +	_screenItem = new ScreenItem(_plane->_object, celInfo, Common::Point(0, 0), ScaleInfo()); +	g_sci->_gfxFrameout->addScreenItem(*_screenItem); +	g_sci->_gfxFrameout->frameOut(true); +	_decoder->start(); + +	while (!g_engine->shouldQuit() && !_decoder->endOfVideo()) { +		renderFrame(); +		g_sci->getEngineState()->speedThrottler(_decoder->getTimeToNextFrame()); +		g_sci->getEngineState()->_throttleTrigger = true; +	} + +	_segMan->freeBitmap(_screenItem->_celInfo.bitmap); +	g_sci->_gfxFrameout->deletePlane(*_plane); +	g_sci->_gfxFrameout->frameOut(true); +	_screenItem = nullptr; +	_plane = nullptr; +} + +void SEQPlayer::renderFrame() const { +	const Graphics::Surface *surface = _decoder->decodeNextFrame(); + +	SciBitmap &bitmap = *_segMan->lookupBitmap(_bitmap); +	bitmap.getBuffer().copyRectToSurface(*surface, 0, 0, Common::Rect(surface->w, surface->h)); + +	const bool dirtyPalette = _decoder->hasDirtyPalette(); +	if (dirtyPalette) { +		Palette palette; +		const byte *rawPalette = _decoder->getPalette(); +		for (int i = 0; i < ARRAYSIZE(palette.colors); ++i) { +			palette.colors[i].r = *rawPalette++; +			palette.colors[i].g = *rawPalette++; +			palette.colors[i].b = *rawPalette++; +			palette.colors[i].used = true; +		} + +		g_sci->_gfxPalette32->submit(palette); +	} + +	g_sci->_gfxFrameout->updateScreenItem(*_screenItem); +	g_sci->getSciDebugger()->onFrame(); +	g_sci->_gfxFrameout->frameOut(true); +} + +#pragma mark - +#pragma mark AVIPlayer + +AVIPlayer::AVIPlayer(SegManager *segMan, EventManager *eventMan) : +	_segMan(segMan), +	_eventMan(eventMan), +	_decoder(new Video::AVIDecoder(Audio::Mixer::kSFXSoundType)), +	_scaleBuffer(nullptr), +	_plane(nullptr), +	_screenItem(nullptr), +	_status(kAVINotOpen) {} + +AVIPlayer::~AVIPlayer() { +	close(); +	delete _decoder; +} + +AVIPlayer::IOStatus AVIPlayer::open(const Common::String &fileName) { +	if (_status != kAVINotOpen) { +		close(); +	} + +	if (!_decoder->loadFile(fileName)) { +		return kIOFileNotFound; +	} + +	_status = kAVIOpen; +	return kIOSuccess; +} + +AVIPlayer::IOStatus AVIPlayer::init1x(const int16 x, const int16 y, int16 width, int16 height) { +	if (!width || !height) { +		width = _decoder->getWidth(); +		height = _decoder->getHeight(); +	} + +	// QFG4CD gives non-multiple-of-2 values for width and height, +	// which would normally be OK except the source video is a pixel bigger +	// in each dimension +	width = (width + 1) & ~1; +	height = (height + 1) & ~1; + +	_drawRect.left = x; +	_drawRect.top = y; +	_drawRect.right = x + width; +	_drawRect.bottom = y + height; + +	// SCI2.1 uses init2x to draw a pixel-doubled AVI, but SCI2 has only the +	// one play routine which automatically pixel-doubles in hi-res mode +	if (getSciVersion() == SCI_VERSION_2) { +		// NOTE: This is somewhat of a hack; credits.avi from GK1 is not +		// rendered correctly in SSCI because it is a 640x480 video, but the +		// game script gives the wrong dimensions. Since this is the only +		// high-resolution AVI ever used, just set the draw rectangle to draw +		// the entire screen +		if (_decoder->getWidth() > 320) { +			_drawRect.left = 0; +			_drawRect.top = 0; +			_drawRect.right = 320; +			_drawRect.bottom = 200; +		} + +		// In hi-res mode, video will be pixel doubled, so the origin (which +		// corresponds to the correct position without pixel doubling) needs to +		// be corrected +		if (g_sci->_gfxFrameout->_isHiRes && _decoder->getWidth() <= 320) { +			_drawRect.left /= 2; +			_drawRect.top /= 2; +		} +	} + +	_pixelDouble = false; +	init(); + +	return kIOSuccess; +} + +AVIPlayer::IOStatus AVIPlayer::init2x(const int16 x, const int16 y) { +	_drawRect.left = x; +	_drawRect.top = y; +	_drawRect.right = x + _decoder->getWidth() * 2; +	_drawRect.bottom = y + _decoder->getHeight() * 2; + +	_pixelDouble = true; +	init(); + +	return kIOSuccess; +} + +void AVIPlayer::init() { +	int16 xRes; +	int16 yRes; + +	if (g_sci->_gfxFrameout->_isHiRes && _decoder->getWidth() > 320) { +		xRes = g_sci->_gfxFrameout->getCurrentBuffer().screenWidth; +		yRes = g_sci->_gfxFrameout->getCurrentBuffer().screenHeight; +	} else { +		xRes = g_sci->_gfxFrameout->getCurrentBuffer().scriptWidth; +		yRes = g_sci->_gfxFrameout->getCurrentBuffer().scriptHeight; +	} + +	_plane = new Plane(_drawRect); +	g_sci->_gfxFrameout->addPlane(*_plane); + +	if (_decoder->getPixelFormat().bytesPerPixel == 1) { +		_segMan->allocateBitmap(&_bitmap, _decoder->getWidth(), _decoder->getHeight(), kDefaultSkipColor, 0, 0, xRes, yRes, 0, false, false); + +		CelInfo32 celInfo; +		celInfo.type = kCelTypeMem; +		celInfo.bitmap = _bitmap; + +		_screenItem = new ScreenItem(_plane->_object, celInfo, Common::Point(_drawRect.left, _drawRect.top), ScaleInfo()); +		g_sci->_gfxFrameout->addScreenItem(*_screenItem); +		g_sci->_gfxFrameout->frameOut(true); +	} else { +		const Buffer ¤tBuffer = g_sci->_gfxFrameout->getCurrentBuffer(); +		const Graphics::PixelFormat format = _decoder->getPixelFormat(); +		initGraphics(currentBuffer.screenWidth, currentBuffer.screenHeight, g_sci->_gfxFrameout->_isHiRes, &format); + +		if (_pixelDouble) { +			const int16 width = _drawRect.width(); +			const int16 height = _drawRect.height(); +			_scaleBuffer = calloc(1, width * height * format.bytesPerPixel); +		} +	} +} + +AVIPlayer::IOStatus AVIPlayer::play(const int16 from, const int16 to, const int16, const bool async) { +	if (from >= 0 && to > 0 && from <= to) { +		_decoder->seekToFrame(from); +		_decoder->setEndFrame(to); +	} + +	if (!async) { +		renderVideo(); +	} + +	_status = kAVIPlaying; +	return kIOSuccess; +} + +void AVIPlayer::renderVideo() const { +	_decoder->start(); +	while (!g_engine->shouldQuit() && !_decoder->endOfVideo()) { +		g_sci->getEngineState()->speedThrottler(_decoder->getTimeToNextFrame()); +		g_sci->getEngineState()->_throttleTrigger = true; +		if (_decoder->needsUpdate()) { +			renderFrame(); +		} +	} +} + +AVIPlayer::IOStatus AVIPlayer::close() { +	if (_status == kAVINotOpen) { +		return kIOSuccess; +	} + +	free(_scaleBuffer); +	_scaleBuffer = nullptr; + +	if (_decoder->getPixelFormat().bytesPerPixel != 1) { +		const bool isHiRes = g_sci->_gfxFrameout->_isHiRes; +		const Buffer ¤tBuffer = g_sci->_gfxFrameout->getCurrentBuffer(); +		const Graphics::PixelFormat format = Graphics::PixelFormat::createFormatCLUT8(); +		initGraphics(currentBuffer.screenWidth, currentBuffer.screenHeight, isHiRes, &format); +	} + +	_decoder->close(); +	_status = kAVINotOpen; +	g_sci->_gfxFrameout->deletePlane(*_plane); +	_plane = nullptr; +	_screenItem = nullptr; +	return kIOSuccess; +} + +AVIPlayer::IOStatus AVIPlayer::cue(const uint16 frameNo) { +	if (!_decoder->seekToFrame(frameNo)) { +		return kIOSeekFailed; +	} + +	_status = kAVIPaused; +	return kIOSuccess; +} + +uint16 AVIPlayer::getDuration() const { +	if (_status == kAVINotOpen) { +		return 0; +	} + +	return _decoder->getFrameCount(); +} + +void AVIPlayer::renderFrame() const { +	const Graphics::Surface *surface = _decoder->decodeNextFrame(); + +	if (surface->format.bytesPerPixel == 1) { +		SciBitmap &bitmap = *_segMan->lookupBitmap(_bitmap); +		if (surface->w > bitmap.getWidth() || surface->h > bitmap.getHeight()) { +			warning("Attempted to draw a video frame larger than the destination bitmap"); +			return; +		} + +		bitmap.getBuffer().copyRectToSurface(*surface, 0, 0, Common::Rect(surface->w, surface->h)); + +		const bool dirtyPalette = _decoder->hasDirtyPalette(); +		if (dirtyPalette) { +			Palette palette; +			const byte *rawPalette = _decoder->getPalette(); +			for (int i = 0; i < ARRAYSIZE(palette.colors); ++i) { +				palette.colors[i].r = *rawPalette++; +				palette.colors[i].g = *rawPalette++; +				palette.colors[i].b = *rawPalette++; +				palette.colors[i].used = true; +			} + +			g_sci->_gfxPalette32->submit(palette); +		} + +		g_sci->_gfxFrameout->updateScreenItem(*_screenItem); +		g_sci->getSciDebugger()->onFrame(); +		g_sci->_gfxFrameout->frameOut(true); +	} else { +		Common::Rect drawRect(_drawRect); +		const int16 screenWidth = g_sci->_gfxFrameout->getCurrentBuffer().screenWidth; +		const int16 screenHeight = g_sci->_gfxFrameout->getCurrentBuffer().screenHeight; +		const int16 scriptWidth = g_sci->_gfxFrameout->getCurrentBuffer().scriptWidth; +		const int16 scriptHeight = g_sci->_gfxFrameout->getCurrentBuffer().scriptHeight; + +		if (_pixelDouble) { +			scale(2, _scaleBuffer, surface->pitch * 2, surface->getPixels(), surface->pitch, surface->format.bytesPerPixel, surface->w, surface->h); +			g_system->copyRectToScreen(_scaleBuffer, surface->pitch * 2, _drawRect.left, _drawRect.top, _drawRect.width(), _drawRect.height()); +		} else { +			mulinc(drawRect, Ratio(screenWidth, scriptWidth), Ratio(screenHeight, scriptHeight)); + +			g_system->copyRectToScreen(surface->getPixels(), surface->pitch, drawRect.left, drawRect.top, surface->w, surface->h); +		} +	} +} + +AVIPlayer::EventFlags AVIPlayer::playUntilEvent(EventFlags flags) { +	_decoder->start(); + +	EventFlags stopFlag = kEventFlagNone; +	while (!g_engine->shouldQuit()) { +		if (_decoder->endOfVideo()) { +			stopFlag = kEventFlagEnd; +			break; +		} + +		g_sci->getEngineState()->speedThrottler(_decoder->getTimeToNextFrame()); +		g_sci->getEngineState()->_throttleTrigger = true; +		if (_decoder->needsUpdate()) { +			renderFrame(); +		} + +		SciEvent event = _eventMan->getSciEvent(SCI_EVENT_MOUSE_PRESS | SCI_EVENT_PEEK); +		if ((flags & kEventFlagMouseDown) && event.type == SCI_EVENT_MOUSE_PRESS) { +			stopFlag = kEventFlagMouseDown; +			break; +		} + +		event = _eventMan->getSciEvent(SCI_EVENT_KEYBOARD | SCI_EVENT_PEEK); +		if ((flags & kEventFlagEscapeKey) && event.type == SCI_EVENT_KEYBOARD) { +			bool stop = false; +			while ((event = _eventMan->getSciEvent(SCI_EVENT_KEYBOARD)), +				   event.type != SCI_EVENT_NONE) { +				if (event.character == SCI_KEY_ESC) { +					stop = true; +					break; +				} +			} + +			if (stop) { +				stopFlag = kEventFlagEscapeKey; +				break; +			} +		} + +		// TODO: Hot rectangles +		if ((flags & kEventFlagHotRectangle) /* && event.type == SCI_EVENT_HOT_RECTANGLE */) { +			warning("Hot rectangles not implemented in VMD player"); +			stopFlag = kEventFlagHotRectangle; +			break; +		} +	} + +	return stopFlag; +} + +#pragma mark -  #pragma mark VMDPlayer  VMDPlayer::VMDPlayer(SegManager *segMan, EventManager *eventMan) : @@ -117,7 +503,7 @@ VMDPlayer::IOStatus VMDPlayer::close() {  	if (!_planeIsOwned && _screenItem != nullptr) {  		g_sci->_gfxFrameout->deleteScreenItem(*_screenItem); -		g_sci->getEngineState()->_segMan->freeBitmap(_screenItem->_celInfo.bitmap); +		_segMan->freeBitmap(_screenItem->_celInfo.bitmap);  		_screenItem = nullptr;  	} else if (_plane != nullptr) {  		g_sci->_gfxFrameout->deletePlane(*_plane); diff --git a/engines/sci/graphics/video32.h b/engines/sci/graphics/video32.h index 7033f7c647..b0deba148a 100644 --- a/engines/sci/graphics/video32.h +++ b/engines/sci/graphics/video32.h @@ -23,12 +23,208 @@  #ifndef SCI_GRAPHICS_VIDEO32_H  #define SCI_GRAPHICS_VIDEO32_H -namespace Video { class AdvancedVMDDecoder; } +#include "common/rect.h"              // for Rect +#include "common/scummsys.h"          // for int16, uint8, int32 +#include "common/str.h"               // for String +#include "sci/video/robot_decoder.h"  // for RobotDecoder + +namespace Video { +class AdvancedVMDDecoder; +class AVIDecoder; +}  namespace Sci {  class Plane;  class ScreenItem;  class SegManager; +class SEQDecoder; +struct Palette; +#pragma mark SEQPlayer + +/** + * SEQPlayer is used to play SEQ animations. + * Used by DOS versions of GK1 and QFG4CD. + */ +class SEQPlayer { +public: +	SEQPlayer(SegManager *segMan); + +	/** +	 * Plays a SEQ animation with the given +	 * file name, with each frame being displayed +	 * for `numTicks` ticks. +	 */ +	void play(const Common::String &fileName, const int16 numTicks, const int16 x, const int16 y); + +private: +	SegManager *_segMan; +	SEQDecoder *_decoder; + +	/** +	 * The plane where the SEQ will be drawn. +	 */ +	Plane *_plane; + +	/** +	 * The screen item representing the SEQ surface. +	 */ +	ScreenItem *_screenItem; + +	/** +	 * The bitmap used to render video output. +	 */ +	reg_t _bitmap; + +	/** +	 * Renders a single frame of video. +	 */ +	void renderFrame() const; +}; + +#pragma mark - +#pragma mark AVIPlayer + +/** + * AVIPlayer is used to play AVI videos. Used by + * Windows versions of GK1CD, KQ7, and QFG4CD. + */ +class AVIPlayer { +public: +	enum IOStatus { +		kIOSuccess      = 0, +		kIOFileNotFound = 2, +		kIOSeekFailed   = 12 +	}; + +	enum AVIStatus { +		kAVINotOpen  = 0, +		kAVIOpen     = 1, +		kAVIPlaying  = 2, +		kAVIPaused   = 3 +	}; + +	enum EventFlags { +		kEventFlagNone         = 0, +		kEventFlagEnd          = 1, +		kEventFlagEscapeKey    = 2, +		kEventFlagMouseDown    = 4, +		kEventFlagHotRectangle = 8 +	}; + +	AVIPlayer(SegManager *segMan, EventManager *eventMan); +	~AVIPlayer(); + +	/** +	 * Opens a stream to an AVI resource. +	 */ +	IOStatus open(const Common::String &fileName); + +	/** +	 * Initializes the AVI rendering parameters for the +	 * current AVI. This must be called after `open`. +	 */ +	IOStatus init1x(const int16 x, const int16 y, const int16 width, const int16 height); + +	/** +	 * Initializes the AVI rendering parameters for the +	 * current AVI, in pixel-doubling mode. This must +	 * be called after `open`. +	 */ +	IOStatus init2x(const int16 x, const int16 y); + +	/** +	 * Begins playback of the current AVI. +	 */ +	IOStatus play(const int16 from, const int16 to, const int16 showStyle, const bool cue); + +	/** +	 * Stops playback and closes the currently open AVI stream. +	 */ +	IOStatus close(); + +	/** +	 * Seeks the currently open AVI stream to the given frame. +	 */ +	IOStatus cue(const uint16 frameNo); + +	/** +	 * Returns the duration of the current video. +	 */ +	uint16 getDuration() const; + +	/** +	 * Plays the AVI until an event occurs (e.g. user +	 * presses escape, clicks, etc.). +	 */ +	EventFlags playUntilEvent(const EventFlags flags); + +private: +	typedef Common::HashMap<uint16, AVIStatus> StatusMap; + +	SegManager *_segMan; +	EventManager *_eventMan; +	Video::AVIDecoder *_decoder; + +	/** +	 * Playback status of the player. +	 */ +	AVIStatus _status; + +	/** +	 * The plane where the AVI will be drawn. +	 */ +	Plane *_plane; + +	/** +	 * The screen item representing the AVI surface, +	 * in 8bpp mode. In 24bpp mode, video is drawn +	 * directly to the screen. +	 */ +	ScreenItem *_screenItem; + +	/** +	 * The bitmap used to render video output in +	 * 8bpp mode. +	 */ +	reg_t _bitmap; + +	/** +	 * The rectangle where the video will be drawn, +	 * in game script coordinates. +	 */ +	Common::Rect _drawRect; + +	/** +	 * The scale buffer for pixel-doubled videos +	 * drawn in 24bpp mode. +	 */ +	void *_scaleBuffer; + +	/** +	 * In SCI2.1, whether or not the video should +	 * be pixel doubled for playback. +	 */ +	bool _pixelDouble; + +	/** +	 * Performs common initialisation for both +	 * scaled and unscaled videos. +	 */ +	void init(); + +	/** +	 * Renders video without event input until the +	 * video is complete. +	 */ +	void renderVideo() const; + +	/** +	 * Renders a single frame of video. +	 */ +	void renderFrame() const; +}; + +#pragma mark -  #pragma mark VMDPlayer  /** @@ -300,11 +496,17 @@ private:  class Video32 {  public:  	Video32(SegManager *segMan, EventManager *eventMan) : +	_SEQPlayer(segMan), +	_AVIPlayer(segMan, eventMan),  	_VMDPlayer(segMan, eventMan) {} +	SEQPlayer &getSEQPlayer() { return _SEQPlayer; } +	AVIPlayer &getAVIPlayer() { return _AVIPlayer; }  	VMDPlayer &getVMDPlayer() { return _VMDPlayer; }  private: +	SEQPlayer _SEQPlayer; +	AVIPlayer _AVIPlayer;  	VMDPlayer _VMDPlayer;  };  } // End of namespace Sci  | 
