diff options
| -rw-r--r-- | engines/sci/engine/kernel.h | 5 | ||||
| -rw-r--r-- | engines/sci/engine/kernel_tables.h | 11 | ||||
| -rw-r--r-- | engines/sci/engine/kscripts.cpp | 27 | ||||
| -rw-r--r-- | engines/sci/engine/kvideo.cpp | 71 | ||||
| -rw-r--r-- | engines/sci/graphics/video32.cpp | 258 | ||||
| -rw-r--r-- | engines/sci/graphics/video32.h | 105 | ||||
| -rw-r--r-- | engines/sci/sound/audio32.h | 12 | 
7 files changed, 424 insertions, 65 deletions
diff --git a/engines/sci/engine/kernel.h b/engines/sci/engine/kernel.h index 51f4b5dbcb..ebd7d33655 100644 --- a/engines/sci/engine/kernel.h +++ b/engines/sci/engine/kernel.h @@ -657,6 +657,11 @@ reg_t kDoSoundPhantasmagoriaMac(EngineState *s, int argc, reg_t *argv);  // SCI3 Kernel functions  reg_t kPlayDuck(EngineState *s, int argc, reg_t *argv); +reg_t kPlayDuckPlay(EngineState *s, int argc, reg_t *argv); +reg_t kPlayDuckSetFrameOut(EngineState *s, int argc, reg_t *argv); +reg_t kPlayDuckOpen(EngineState *s, int argc, reg_t *argv); +reg_t kPlayDuckClose(EngineState *s, int argc, reg_t *argv); +reg_t kPlayDuckSetVolume(EngineState *s, int argc, reg_t *argv);  #endif  reg_t kDoSoundInit(EngineState *s, int argc, reg_t *argv); diff --git a/engines/sci/engine/kernel_tables.h b/engines/sci/engine/kernel_tables.h index 211d96bc2f..95f3197896 100644 --- a/engines/sci/engine/kernel_tables.h +++ b/engines/sci/engine/kernel_tables.h @@ -495,6 +495,15 @@ static const SciKernelMapSubEntry kRobot_subops[] = {  };  //    version,         subId, function-mapping,                    signature,              workarounds +static const SciKernelMapSubEntry kPlayDuck_subops[] = { +	{ SIG_SCI3,            1, MAP_CALL(PlayDuckPlay),              "iiiii",                NULL }, +	{ SIG_SCI3,            2, MAP_CALL(PlayDuckSetFrameOut),       "i",                    NULL }, +	{ SIG_SCI3,            5, MAP_CALL(PlayDuckClose),             "",                     NULL }, +	{ SIG_SCI3,            6, MAP_CALL(PlayDuckSetVolume),         "i",                    NULL }, +	SCI_SUBOPENTRY_TERMINATOR +}; + +//    version,         subId, function-mapping,                    signature,              workarounds  static const SciKernelMapSubEntry kRemapColors_subops[] = {  	{ SIG_SCI32,           0, MAP_CALL(RemapColorsOff),            "(i)",                  NULL },  	{ SIG_SCI32,           1, MAP_CALL(RemapColorsByRange),        "iiii(i)",              NULL }, @@ -1008,7 +1017,7 @@ static SciKernelMapEntry s_kernelMap[] = {  	{ MAP_CALL(MorphOn),            SIG_EVERYWHERE,           "",                     NULL,            NULL },  	// SCI3 Kernel Functions -	{ MAP_CALL(PlayDuck),           SIG_EVERYWHERE,           "(.*)",                 NULL,            NULL }, +	{ MAP_CALL(PlayDuck),           SIG_SCI3, SIGFOR_ALL,     "(.*)",                 kPlayDuck_subops,NULL },  #endif  	{ NULL, NULL,                   SIG_EVERYWHERE,           NULL,                   NULL,            NULL } diff --git a/engines/sci/engine/kscripts.cpp b/engines/sci/engine/kscripts.cpp index 77ef92b349..7c4c955824 100644 --- a/engines/sci/engine/kscripts.cpp +++ b/engines/sci/engine/kscripts.cpp @@ -119,11 +119,28 @@ reg_t kResCheck(EngineState *s, int argc, reg_t *argv) {  	}  #ifdef ENABLE_SCI32 -	// GK2 stores some VMDs inside of resource volumes, but usually they are -	// streamed from the filesystem -	if (res == nullptr && restype == kResourceTypeVMD) { -		const Common::String fileName = Common::String::format("%u.vmd", argv[1].toUint16()); -		return make_reg(0, Common::File::exists(fileName)); +	// GK2 stores some VMDs inside of resource volumes, but usually videos are +	// streamed from the filesystem. +	if (res == nullptr) { +		const char *format = nullptr; +		switch (restype) { +		case kResourceTypeRobot: +			format = "%u.rbt"; +			break; +		case kResourceTypeDuck: +			format = "%u.duk"; +			break; +		case kResourceTypeVMD: +			format = "%u.vmd"; +			break; +		default: +			format = nullptr; +		} + +		if (format) { +			const Common::String fileName = Common::String::format(format, argv[1].toUint16()); +			return make_reg(0, Common::File::exists(fileName)); +		}  	}  #endif diff --git a/engines/sci/engine/kvideo.cpp b/engines/sci/engine/kvideo.cpp index 3d689f2b42..e4f2ff7a6d 100644 --- a/engines/sci/engine/kvideo.cpp +++ b/engines/sci/engine/kvideo.cpp @@ -472,52 +472,41 @@ reg_t kPlayVMDRestrictPalette(EngineState *s, int argc, reg_t *argv) {  }  reg_t kPlayDuck(EngineState *s, int argc, reg_t *argv) { -	uint16 operation = argv[0].toUint16(); -	Video::VideoDecoder *videoDecoder = 0; -	bool reshowCursor = g_sci->_gfxCursor->isVisible(); - -	switch (operation) { -	case 1:	// Play -		// 6 params -		s->_videoState.reset(); -		s->_videoState.fileName = Common::String::format("%d.duk", argv[1].toUint16()); - -		videoDecoder = new Video::AVIDecoder(); - -		if (!videoDecoder->loadFile(s->_videoState.fileName)) { -			warning("Could not open Duck %s", s->_videoState.fileName.c_str()); -			break; -		} - -		if (reshowCursor) -			g_sci->_gfxCursor->kernelHide(); - -		{ -		// Duck videos are 16bpp, so we need to change the active pixel format -		int oldWidth = g_system->getWidth(); -		int oldHeight = g_system->getHeight(); -		Common::List<Graphics::PixelFormat> formats; -		formats.push_back(videoDecoder->getPixelFormat()); -		initGraphics(640, 480, true, formats); +	if (!s) +		return make_reg(0, getSciVersion()); +	error("not supposed to call this"); +} -		if (g_system->getScreenFormat().bytesPerPixel != videoDecoder->getPixelFormat().bytesPerPixel) -			error("Could not switch screen format for the duck video"); +reg_t kPlayDuckPlay(EngineState *s, int argc, reg_t *argv) { +	kPlayDuckOpen(s, argc, argv); +	g_sci->_video32->getDuckPlayer().play(-1); +	g_sci->_video32->getDuckPlayer().close(); +	return NULL_REG; +} -		playVideo(videoDecoder, s->_videoState); +reg_t kPlayDuckSetFrameOut(EngineState *s, int argc, reg_t *argv) { +	g_sci->_video32->getDuckPlayer().setDoFrameOut((bool)argv[0].toUint16()); +	return NULL_REG; +} -		// Switch back to 8bpp -		initGraphics(oldWidth, oldHeight, oldWidth > 320); -		} +reg_t kPlayDuckOpen(EngineState *s, int argc, reg_t *argv) { +	const GuiResourceId resourceId = argv[0].toUint16(); +	const int displayMode = argv[1].toSint16(); +	const int16 x = argv[2].toSint16(); +	const int16 y = argv[3].toSint16(); +	// argv[4] is a cache size argument that we do not use +	g_sci->_video32->getDuckPlayer().open(resourceId, displayMode, x, y); +	return NULL_REG; +} -		if (reshowCursor) -			g_sci->_gfxCursor->kernelShow(); -		break; -	default: -		kStub(s, argc, argv); -		break; -	} +reg_t kPlayDuckClose(EngineState *s, int argc, reg_t *argv) { +	g_sci->_video32->getDuckPlayer().close(); +	return NULL_REG; +} -	return s->r_acc; +reg_t kPlayDuckSetVolume(EngineState *s, int argc, reg_t *argv) { +	g_sci->_video32->getDuckPlayer().setVolume(argv[0].toUint16()); +	return NULL_REG;  }  #endif diff --git a/engines/sci/graphics/video32.cpp b/engines/sci/graphics/video32.cpp index f4eb2c2da0..8cd6fc7089 100644 --- a/engines/sci/graphics/video32.cpp +++ b/engines/sci/graphics/video32.cpp @@ -51,6 +51,25 @@ namespace Graphics { struct Surface; }  namespace Sci { +/** + * @returns true if the player should quit + */ +static bool flushEvents(EventManager *eventMan) { +	// Flushing all the keyboard and mouse events out of the event manager +	// keeps events queued from before the start of playback from accidentally +	// activating a video stop flag +	for (;;) { +		const SciEvent event = eventMan->getSciEvent(SCI_EVENT_KEYBOARD | SCI_EVENT_MOUSE_PRESS | SCI_EVENT_MOUSE_RELEASE | SCI_EVENT_HOT_RECTANGLE | SCI_EVENT_QUIT); +		if (event.type == SCI_EVENT_NONE) { +			break; +		} else if (event.type == SCI_EVENT_QUIT) { +			return true; +		} +	} + +	return false; +} +  #pragma mark SEQPlayer  SEQPlayer::SEQPlayer(SegManager *segMan) : @@ -672,16 +691,8 @@ VMDPlayer::EventFlags VMDPlayer::kernelPlayUntilEvent(const EventFlags flags, co  }  VMDPlayer::EventFlags VMDPlayer::playUntilEvent(const EventFlags flags) { -	// Flushing all the keyboard and mouse events out of the event manager -	// keeps events queued from before the start of playback from accidentally -	// activating a video stop flag -	for (;;) { -		const SciEvent event = _eventMan->getSciEvent(SCI_EVENT_KEYBOARD | SCI_EVENT_MOUSE_PRESS | SCI_EVENT_MOUSE_RELEASE | SCI_EVENT_HOT_RECTANGLE | SCI_EVENT_QUIT); -		if (event.type == SCI_EVENT_NONE) { -			break; -		} else if (event.type == SCI_EVENT_QUIT) { -			return kEventFlagEnd; -		} +	if (flushEvents(_eventMan)) { +		return kEventFlagEnd;  	}  	if (flags & kEventFlagReverse) { @@ -914,4 +925,231 @@ void VMDPlayer::restrictPalette(const uint8 startColor, const int16 endColor) {  	_endColor = MIN<int16>(255, endColor);  } +#pragma mark - +#pragma mark DuckPlayer + +DuckPlayer::DuckPlayer(SegManager *segMan, EventManager *eventMan) : +	_segMan(segMan), +	_eventMan(eventMan), +	_decoder(new Video::AVIDecoder(Audio::Mixer::kSFXSoundType)), +	_plane(nullptr), +	_status(kDuckClosed), +	_drawRect(), +	_volume(Audio::Mixer::kMaxChannelVolume), +	_doFrameOut(false), +	_pixelDouble(false), +	_scaleBuffer(nullptr) {} + +DuckPlayer::~DuckPlayer() { +	close(); +	delete _decoder; +} + +void DuckPlayer::open(const GuiResourceId resourceId, const int displayMode, const int16 x, const int16 y) { +	if (_status != kDuckClosed) { +		error("Attempted to play %u.duk, but another video was loaded", resourceId); +	} + +	const Common::String fileName = Common::String::format("%u.duk", resourceId); +	if (!_decoder->loadFile(fileName)) { +		error("Can't open %s", fileName.c_str()); +	} + +	_decoder->setVolume(_volume); +	_pixelDouble = displayMode != 0; + +	const int16 scale = _pixelDouble ? 2 : 1; +	_drawRect = Common::Rect(x, y, +							 (x + _decoder->getWidth()) * scale, +							 (y + _decoder->getHeight()) * scale); + +	g_sci->_gfxCursor32->hide(); + +	if (_doFrameOut) { +		_plane = new Plane(_drawRect, kPlanePicColored); +		g_sci->_gfxFrameout->addPlane(*_plane); +		g_sci->_gfxFrameout->frameOut(true); +	} + +	const Buffer ¤tBuffer = g_sci->_gfxFrameout->getCurrentBuffer(); +	const Graphics::PixelFormat format = _decoder->getPixelFormat(); + +	if (_pixelDouble) { +		assert(_scaleBuffer == nullptr); +		_scaleBuffer = new byte[_drawRect.width() * _drawRect.height() * format.bytesPerPixel]; +	} + +	initGraphics(currentBuffer.screenWidth, currentBuffer.screenHeight, true, &format); + +	_status = kDuckOpen; +} + +void DuckPlayer::play(const int lastFrameNo) { +	flushEvents(_eventMan); + +	if (_status != kDuckPlaying) { +		_status = kDuckPlaying; +		_decoder->start(); +	} + +	while (!g_engine->shouldQuit()) { +		if (_decoder->endOfVideo() || (lastFrameNo != -1 && _decoder->getCurFrame() >= lastFrameNo)) { +			break; +		} + +		g_sci->sleep(_decoder->getTimeToNextFrame()); +		while (_decoder->needsUpdate()) { +			renderFrame(); +		} + +		SciEvent event = _eventMan->getSciEvent(SCI_EVENT_MOUSE_PRESS | SCI_EVENT_PEEK); +		if (event.type == SCI_EVENT_MOUSE_PRESS) { +			flushEvents(_eventMan); +			break; +		} + +		event = _eventMan->getSciEvent(SCI_EVENT_KEYBOARD | SCI_EVENT_PEEK); +		if (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) { +				flushEvents(_eventMan); +				break; +			} +		} +	} +} + +void DuckPlayer::close() { +	if (_status == kDuckClosed) { +		return; +	} + +	_decoder->close(); + +	const Buffer ¤tBuffer = g_sci->_gfxFrameout->getCurrentBuffer(); +	const Graphics::PixelFormat format = Graphics::PixelFormat::createFormatCLUT8(); +	initGraphics(currentBuffer.screenWidth, currentBuffer.screenHeight, true, &format); + +	g_sci->_gfxCursor32->unhide(); + +	if (_doFrameOut) { +		g_sci->_gfxFrameout->deletePlane(*_plane); +		g_sci->_gfxFrameout->frameOut(true); +		_plane = nullptr; +	} + +	_pixelDouble = false; +	delete[] _scaleBuffer; +	_scaleBuffer = nullptr; + +	_status = kDuckClosed; +} + +static inline uint16 interpolate(const Graphics::PixelFormat &format, const uint16 p1, const uint16 p2) { +	uint8 r1, g1, b1, r2, g2, b2; +	format.colorToRGB(p1, r1, g1, b1); +	format.colorToRGB(p2, r2, g2, b2); +	return format.RGBToColor((r1 + r2) >> 1, (g1 + g2) >> 1, (b1 + b2) >> 1); +} + +void DuckPlayer::renderFrame() const { +	const Graphics::Surface *surface = _decoder->decodeNextFrame(); + +	// Audio-only or non-updated frame +	if (surface == nullptr) { +		return; +	} + +	assert(surface->format.bytesPerPixel == 2); + +	if (_pixelDouble) { +		const uint16 *source = (const uint16 *)surface->getPixels(); +		const Graphics::PixelFormat &format = surface->format; +		uint16 *target = (uint16 *)_scaleBuffer; + +#ifndef SCI_DUCK_NO_INTERPOLATION +		// divide by 2 gets pixel pitch instead of byte pitch for source +		const uint16 sourcePitch = surface->pitch >> 1; +#endif + +		const uint16 targetPitch = surface->pitch; +		const bool blackLined = ConfMan.getBool("enable_black_lined_video"); +		for (int y = 0; y < surface->h - 1; ++y) { +			for (int x = 0; x < surface->w - 1; ++x) { +#ifndef SCI_DUCK_NO_INTERPOLATION +				const uint16 a = source[0]; +				const uint16 b = source[1]; +				const uint16 c = source[sourcePitch]; +				const uint16 d = source[sourcePitch + 1]; + +				target[0] = a; +				target[1] = interpolate(format, a, b); +#else +				const uint16 value = *source; +				target[0] = value; +				target[1] = value; +#endif +				if (!blackLined) { +#ifndef SCI_DUCK_NO_INTERPOLATION +					target[targetPitch] = interpolate(format, a, c); +					target[targetPitch + 1] = interpolate(format, target[1], interpolate(format, c, d)); +#else +					target[targetPitch] = value; +					target[targetPitch + 1] = value; +#endif +				} + +				target += 2; +				++source; +			} + +			const uint16 value = *source++; +			target[0] = value; +			target[1] = value; +			if (!blackLined) { +				target[targetPitch] = value; +				target[targetPitch + 1] = value; +			} +			target += 2; + +			if (blackLined) { +				memset(target, 0, targetPitch * format.bytesPerPixel); +			} + +			target += targetPitch; +		} + +		for (int x = 0; x < surface->w; ++x) { +			const uint16 lastValue = *source++; +			target[0] = lastValue; +			target[1] = lastValue; + +			if (!blackLined) { +				target[targetPitch] = lastValue; +				target[targetPitch + 1] = lastValue; +				target += 2; +			} +		} + +		if (blackLined) { +			memset(target, 0, targetPitch); +		} + +		g_system->copyRectToScreen(_scaleBuffer, surface->pitch * 2, _drawRect.left, _drawRect.top, _drawRect.width(), _drawRect.height()); +	} else { +		g_system->copyRectToScreen(surface->getPixels(), surface->pitch, _drawRect.left, _drawRect.top, surface->w, surface->h); +	} + +	g_system->updateScreen(); +	g_sci->getSciDebugger()->onFrame(); +} +  } // End of namespace Sci diff --git a/engines/sci/graphics/video32.h b/engines/sci/graphics/video32.h index 5ed8fd954a..fae5cafbbe 100644 --- a/engines/sci/graphics/video32.h +++ b/engines/sci/graphics/video32.h @@ -28,10 +28,11 @@  #include "common/str.h"           // for String  #include "sci/engine/vm_types.h"  // for reg_t  #include "sci/video/robot_decoder.h" // for RobotDecoder +#include "sci/sound/audio32.h"    // for Audio32::kMaxVolume +#include "video/avi_decoder.h"    // for AVIDecoder::setVolume  namespace Video {  class AdvancedVMDDecoder; -class AVIDecoder;  }  namespace Sci {  class EventManager; @@ -531,6 +532,103 @@ private:  	bool _showCursor;  }; +#pragma mark - +#pragma mark DuckPlayer + +class DuckPlayer { +public: +	enum DuckStatus { +		kDuckClosed  = 0, +		kDuckOpen    = 1, +		kDuckPlaying = 2, +		kDuckPaused  = 3 +	}; + +	DuckPlayer(SegManager *segMan, EventManager *eventMan); + +	~DuckPlayer(); + +	/** +	 * Opens a stream to a Duck resource. +	 */ +	void open(const GuiResourceId resourceId, const int displayMode, const int16 x, const int16 y); + +	/** +	 * Stops playback and closes the currently open Duck stream. +	 */ +	void close(); + +	/** +	 * Begins playback of the current Duck video. +	 */ +	void play(const int lastFrameNo); + +	/** +	 * Sets a flag indicating that an opaque plane should be added +	 * to the graphics manager underneath the video surface during +	 * playback. +	 */ +	void setDoFrameOut(const bool value) { _doFrameOut = value; } + +	/** +	 * Sets the volume of the decoder. +	 */ +	void setVolume(const uint8 value) { +		_volume = (uint)value * Audio::Mixer::kMaxChannelVolume / Audio32::kMaxVolume; +		_decoder->setVolume(_volume); +	} + +private: +	SegManager *_segMan; +	EventManager *_eventMan; +	Video::AVIDecoder *_decoder; + +	/** +	 * An empty plane drawn behind the video when the doFrameOut +	 * flag is true. +	 */ +	Plane *_plane; + +	/** +	 * The player status. +	 */ +	DuckStatus _status; + +	/** +	 * The screen rect where the video should be drawn. +	 */ +	Common::Rect _drawRect; + +	/** +	 * The playback volume for the player. +	 */ +	uint8 _volume; + +	/** +	 * If true, frameOut will be called during Duck video playback to update +	 * other parts of the screen. +	 */ +	bool _doFrameOut; + +	/** +	 * If true, the video will be pixel doubled during playback. +	 */ +	bool _pixelDouble; + +	/** +	 * The buffer used to perform scaling of the video. +	 */ +	byte *_scaleBuffer; + +	/** +	 * Renders the current frame to the system video buffer. +	 */ +	void renderFrame() const; +}; + +#pragma mark - +#pragma mark Video32 +  /**   * Video32 provides facilities for playing back   * video in SCI engine. @@ -541,18 +639,21 @@ public:  	_SEQPlayer(segMan),  	_AVIPlayer(segMan, eventMan),  	_VMDPlayer(segMan, eventMan), -	_robotPlayer(segMan) {} +	_robotPlayer(segMan), +	_duckPlayer(segMan, eventMan) {}  	SEQPlayer &getSEQPlayer() { return _SEQPlayer; }  	AVIPlayer &getAVIPlayer() { return _AVIPlayer; }  	VMDPlayer &getVMDPlayer() { return _VMDPlayer; }  	RobotDecoder &getRobotPlayer() { return _robotPlayer; } +	DuckPlayer &getDuckPlayer() { return _duckPlayer; }  private:  	SEQPlayer _SEQPlayer;  	AVIPlayer _AVIPlayer;  	VMDPlayer _VMDPlayer;  	RobotDecoder _robotPlayer; +	DuckPlayer _duckPlayer;  };  } // End of namespace Sci diff --git a/engines/sci/sound/audio32.h b/engines/sci/sound/audio32.h index a9905ab6bf..9130cbe687 100644 --- a/engines/sci/sound/audio32.h +++ b/engines/sci/sound/audio32.h @@ -160,12 +160,6 @@ public:  	Audio32(ResourceManager *resMan);  	~Audio32(); -private: -	ResourceManager *_resMan; -	Audio::Mixer *_mixer; -	Audio::SoundHandle _handle; -	Common::Mutex _mutex; -  	enum {  		/**  		 * The maximum channel volume. @@ -173,6 +167,12 @@ private:  		kMaxVolume = 127  	}; +private: +	ResourceManager *_resMan; +	Audio::Mixer *_mixer; +	Audio::SoundHandle _handle; +	Common::Mutex _mutex; +  #pragma mark -  #pragma mark AudioStream implementation  public:  | 
