Esempio n. 1
0
// SCI1 Early:
// KQ5 - no fastcast, LSL1 (demo) - no fastcast, Mixed Up Fairy Tales - *has fastcast*, XMas Card 1990 - no fastcast,
// SQ4Floppy - no fastcast, Mixed Up Mother Goose - no fastcast
//
// SCI1 Middle:
// LSL5 demo - no fastfast, Conquest of the Longbow demo - no fastcast, LSL1 - no fastcast,
// Astro Chicken II - no fastcast
//
// SCI1 Late:
// Castle of Dr. Brain demo - has fastcast, Castle of Dr. Brain - has fastcast,
// Conquests of the Longbow - has fastcast, Space Quest 1 EGA - has fastcast,
// King's Quest 5 multilingual - *NO* fastcast, Police Quest 3 demo - *NO* fastcast,
// LSL5 multilingual - has fastcast, Police Quest 3 - has fastcast,
// EcoQuest 1 - has fastcast, Mixed Up Fairy Tales demo - has fastcast,
// Space Quest 4 multilingual - *NO* fastcast
//
// SCI1.1
// Quest for Glory 3 demo - has fastcast, Police Quest 1 - hast fastcast, Quest for Glory 1 - has fastcast
// Laura Bow 2 Floppy - has fastcast, Mixed Up Mother Goose - has fastcast, Quest for Glory 3 - has fastcast
// Island of Dr. Brain - has fastcast, King's Quest 6 - has fastcast, Space Quest 5 - has fastcast
// Hoyle 4 - has fastcast, Laura Bow 2 CD - has fastcast, Freddy Pharkas CD - has fastcast
bool GfxAnimate::detectFastCast() {
	SegManager *segMan = _s->_segMan;
	const reg_t gameVMObject = g_sci->getGameObject();
	reg_t gameSuperVMObject = segMan->getObject(gameVMObject)->getSuperClassSelector();
	uint32 magicDWord = 0; // platform-specific BE/LE for performance
	int    magicDWordOffset = 0;

	if (gameSuperVMObject.isNull()) {
		gameSuperVMObject = gameVMObject; // Just in case. According to sci.cpp this may happen in KQ5CD, when loading saved games before r54510
	}

	Script *objectScript = segMan->getScript(gameSuperVMObject.getSegment());
	byte *scriptData = const_cast<byte *>(objectScript->getBuf(0));
	uint32 scriptSize = objectScript->getBufSize();

	_scriptPatcher->calculateMagicDWordAndVerify("fast cast detection", fastCastSignature, true, magicDWord, magicDWordOffset);

	// Signature is found for multilingual King's Quest 5 too, but it looks as if the fast cast global is never set
	// within that game. Which means even though we detect it as having the capability, it's never actually used.
	// The original multilingual KQ5 interpreter did have this feature disabled.
	// Sierra probably used latest system scripts and that's why we detect it.
	if (_scriptPatcher->findSignature(magicDWord, magicDWordOffset, fastCastSignature, "fast cast detection", scriptData, scriptSize) >= 0) {
		// Signature found, game seems to use fast cast for kAnimate
		return true;
	}
	return false;
}
Esempio n. 2
0
File: gc.cpp Progetto: 86400/scummvm
void run_gc(EngineState *s) {
	SegManager *segMan = s->_segMan;

	// Some debug stuff
	debugC(kDebugLevelGC, "[GC] Running...");
#ifdef GC_DEBUG_CODE
	const char *segnames[SEG_TYPE_MAX + 1];
	int segcount[SEG_TYPE_MAX + 1];
	memset(segnames, 0, sizeof(segnames));
	memset(segcount, 0, sizeof(segcount));
#endif

	// Compute the set of all segments references currently in use.
	AddrSet *activeRefs = findAllActiveReferences(s);

	// Iterate over all segments, and check for each whether it
	// contains stuff that can be collected.
	const Common::Array<SegmentObj *> &heap = segMan->getSegments();
	for (uint seg = 1; seg < heap.size(); seg++) {
		SegmentObj *mobj = heap[seg];

		if (mobj != NULL) {
#ifdef GC_DEBUG_CODE
			const SegmentType type = mobj->getType();
			segnames[type] = segmentTypeNames[type];
#endif

			// Get a list of all deallocatable objects in this segment,
			// then free any which are not referenced from somewhere.
			const Common::Array<reg_t> tmp = mobj->listAllDeallocatable(seg);
			for (Common::Array<reg_t>::const_iterator it = tmp.begin(); it != tmp.end(); ++it) {
				const reg_t addr = *it;
				if (!activeRefs->contains(addr)) {
					// Not found -> we can free it
					mobj->freeAtAddress(segMan, addr);
					debugC(kDebugLevelGC, "[GC] Deallocating %04x:%04x", PRINT_REG(addr));
#ifdef GC_DEBUG_CODE
					segcount[type]++;
#endif
				}
			}

		}
	}

	delete activeRefs;

#ifdef GC_DEBUG_CODE
	// Output debug summary of garbage collection
	debugC(kDebugLevelGC, "[GC] Summary:");
	for (int i = 0; i <= SEG_TYPE_MAX; i++)
		if (segcount[i])
			debugC(kDebugLevelGC, "\t%d\t* %s", segcount[i], segnames[i]);
#endif
}
Esempio n. 3
0
reg_t GfxPalette::kernelSave() {
	SegManager *segMan = g_sci->getEngineState()->_segMan;
	reg_t memoryId = segMan->allocateHunkEntry("kPalette(save)", 1024);
	byte *memoryPtr = segMan->getHunkPointer(memoryId);
	if (memoryPtr) {
		for (int colorNr = 0; colorNr < 256; colorNr++) {
			*memoryPtr++ = _sysPalette.colors[colorNr].used;
			*memoryPtr++ = _sysPalette.colors[colorNr].r;
			*memoryPtr++ = _sysPalette.colors[colorNr].g;
			*memoryPtr++ = _sysPalette.colors[colorNr].b;
		}
	}
	return memoryId;
}
Esempio n. 4
0
reg_t kCreateTextBitmap(EngineState *s, int argc, reg_t *argv) {
	SegManager *segMan = s->_segMan;

	int16 subop = argv[0].toUint16();

	int16 width = 0;
	int16 height = 0;
	reg_t object;

	if (subop == 0) {
		width = argv[1].toUint16();
		height = argv[2].toUint16();
		object = argv[3];
	} else if (subop == 1) {
		object = argv[1];
	} else {
		warning("Invalid kCreateTextBitmap subop %d", subop);
		return NULL_REG;
	}

	Common::String text = segMan->getString(readSelector(segMan, object, SELECTOR(text)));
	int16 foreColor = readSelectorValue(segMan, object, SELECTOR(fore));
	int16 backColor = readSelectorValue(segMan, object, SELECTOR(back));
	int16 skipColor = readSelectorValue(segMan, object, SELECTOR(skip));
	GuiResourceId fontId = (GuiResourceId)readSelectorValue(segMan, object, SELECTOR(font));
	int16 borderColor = readSelectorValue(segMan, object, SELECTOR(borderColor));
	int16 dimmed = readSelectorValue(segMan, object, SELECTOR(dimmed));

	Common::Rect rect(
		readSelectorValue(segMan, object, SELECTOR(textLeft)),
		readSelectorValue(segMan, object, SELECTOR(textTop)),
		readSelectorValue(segMan, object, SELECTOR(textRight)) + 1,
		readSelectorValue(segMan, object, SELECTOR(textBottom)) + 1
	);

	if (subop == 0) {
		TextAlign alignment = (TextAlign)readSelectorValue(segMan, object, SELECTOR(mode));
		return g_sci->_gfxText32->createFontBitmap(width, height, rect, text, foreColor, backColor, skipColor, fontId, alignment, borderColor, dimmed, true, true);
	} else {
		CelInfo32 celInfo;
		celInfo.type = kCelTypeView;
		celInfo.resourceId = readSelectorValue(segMan, object, SELECTOR(view));
		celInfo.loopNo = readSelectorValue(segMan, object, SELECTOR(loop));
		celInfo.celNo = readSelectorValue(segMan, object, SELECTOR(cel));
		return g_sci->_gfxText32->createFontBitmap(celInfo, rect, text, foreColor, backColor, fontId, skipColor, borderColor, dimmed, true);
	}
}
Esempio n. 5
0
int main_nanjing()
{
    const char SEGMENTS_CSV_PATH[] = "Data\\Nanjing\\NJ_WAY_SEGMENTS\\data.csv";
    if (false == gSegManager.LoadFromCsvFile(SEGMENTS_CSV_PATH)) {
        printf("Error: cannot read Segments CSV file: %s\n", SEGMENTS_CSV_PATH);
        return 10;
    }
    printf("%s: Found %d segments.\n", ElapsedTimeStr().c_str(), gSegManager.GetSegArrayCount());

    if (false == gTileManager.GenerateTiles(gSegManager)) {
        printf("Error: cannot generate tiles\n");
        return 20;
    }
    printf("%s: Generated %d tiles.\n", ElapsedTimeStr().c_str(), gTileManager.GetTileCount());

    NanjingAssign_UsingTileManager();

    printf("%s: Done!\n", ElapsedTimeStr().c_str());
	return 0;
}
Esempio n. 6
0
void GfxPalette::kernelRestore(reg_t memoryHandle) {
	SegManager *segMan = g_sci->getEngineState()->_segMan;
	if (!memoryHandle.isNull()) {
		byte *memoryPtr = segMan->getHunkPointer(memoryHandle);
		if (!memoryPtr)
			error("Bad handle used for kPalette(restore)");

		Palette restoredPalette;

		restoredPalette.timestamp = 0;
		for (int colorNr = 0; colorNr < 256; colorNr++) {
			restoredPalette.colors[colorNr].used = *memoryPtr++;
			restoredPalette.colors[colorNr].r = *memoryPtr++;
			restoredPalette.colors[colorNr].g = *memoryPtr++;
			restoredPalette.colors[colorNr].b = *memoryPtr++;
		}

		set(&restoredPalette, true);
	}
}
Esempio n. 7
0
void SciEngine::patchGameSaveRestore() {
	SegManager *segMan = _gamestate->_segMan;
	const Object *gameObject = segMan->getObject(_gameObjectAddress);
	const Object *gameSuperObject = segMan->getObject(gameObject->getSuperClassSelector());
	if (!gameSuperObject)
		gameSuperObject = gameObject;	// happens in KQ5CD, when loading saved games before r54510
	byte kernelIdRestore = 0;
	byte kernelIdSave = 0;

	switch (_gameId) {
	case GID_HOYLE1: // gets confused, although the game doesn't support saving/restoring at all
	case GID_HOYLE2: // gets confused, see hoyle1
	case GID_JONES: // gets confused, when we patch us in, the game is only able to save to 1 slot, so hooking is not required
	case GID_KQ7: // has custom save/load code
	case GID_MOTHERGOOSE: // mother goose EGA saves/restores directly and has no save/restore dialogs
	case GID_MOTHERGOOSE256: // mother goose saves/restores directly and has no save/restore dialogs
	case GID_MOTHERGOOSEHIRES: // has custom save/load code
	case GID_PHANTASMAGORIA: // has custom save/load code
	case GID_PQSWAT: // has custom save/load code
	case GID_SHIVERS: // has custom save/load code
		return;
	default:
		break;
	}

	if (ConfMan.getBool("originalsaveload"))
		return;

	uint16 kernelNamesSize = _kernel->getKernelNamesSize();
	for (uint16 kernelNr = 0; kernelNr < kernelNamesSize; kernelNr++) {
		Common::String kernelName = _kernel->getKernelName(kernelNr);
		if (kernelName == "RestoreGame")
			kernelIdRestore = kernelNr;
		if (kernelName == "SaveGame")
			kernelIdSave = kernelNr;
		if (kernelName == "Save")
			kernelIdSave = kernelIdRestore = kernelNr;
	}

	// Search for gameobject superclass ::restore
	uint16 gameSuperObjectMethodCount = gameSuperObject->getMethodCount();
	for (uint16 methodNr = 0; methodNr < gameSuperObjectMethodCount; methodNr++) {
		uint16 selectorId = gameSuperObject->getFuncSelector(methodNr);
		Common::String methodName = _kernel->getSelectorName(selectorId);
		if (methodName == "restore") {
#ifdef ENABLE_SCI32
			if (getSciVersion() >= SCI_VERSION_2) {
				patchGameSaveRestoreCodeSci2(segMan, gameSuperObject->getFunction(methodNr), kernelIdRestore, true);
			} else
#endif
				patchGameSaveRestoreCode(segMan, gameSuperObject->getFunction(methodNr), kernelIdRestore);
		}
		else if (methodName == "save") {
			if (_gameId != GID_FAIRYTALES) {	// Fairy Tales saves automatically without a dialog
#ifdef ENABLE_SCI32
				if (getSciVersion() >= SCI_VERSION_2) {
					patchGameSaveRestoreCodeSci2(segMan, gameSuperObject->getFunction(methodNr), kernelIdSave, false);
				} else
#endif
					patchGameSaveRestoreCode(segMan, gameSuperObject->getFunction(methodNr), kernelIdSave);
			}
		}
	}

	const Object *patchObjectSave = nullptr;

	if (getSciVersion() < SCI_VERSION_2) {
		// Patch gameobject ::save for now for SCI0 - SCI1.1
		// TODO: It seems this was never adjusted to superclass, but adjusting it now may cause
		// issues with some game. Needs to get checked and then possibly changed.
		patchObjectSave = gameObject;
	} else {
		// Patch superclass ::save for SCI32
		patchObjectSave = gameSuperObject;
	}

	// Search for gameobject ::save, if there is one patch that one too
	uint16 patchObjectMethodCount = patchObjectSave->getMethodCount();
	for (uint16 methodNr = 0; methodNr < patchObjectMethodCount; methodNr++) {
		uint16 selectorId = patchObjectSave->getFuncSelector(methodNr);
		Common::String methodName = _kernel->getSelectorName(selectorId);
		if (methodName == "save") {
			if (_gameId != GID_FAIRYTALES) {	// Fairy Tales saves automatically without a dialog
#ifdef ENABLE_SCI32
				if (getSciVersion() >= SCI_VERSION_2) {
					patchGameSaveRestoreCodeSci2(segMan, gameSuperObject->getFunction(methodNr), kernelIdSave, false);
				} else
#endif
					patchGameSaveRestoreCode(segMan, patchObjectSave->getFunction(methodNr), kernelIdSave);
			}
			break;
		}
	}
}
Esempio n. 8
0
Common::Error SciEngine::run() {
	_resMan = new ResourceManager();
	assert(_resMan);
	_resMan->addAppropriateSources();
	_resMan->init();

	// TODO: Add error handling. Check return values of addAppropriateSources
	// and init. We first have to *add* sensible return values, though ;).
/*
	if (!_resMan) {
		warning("No resources found, aborting");
		return Common::kNoGameDataFoundError;
	}
*/

	// Reset, so that error()s before SoundCommandParser is initialized wont cause a crash
	_soundCmd = NULL;

	// Add the after market GM patches for the specified game, if they exist
	_resMan->addNewGMPatch(_gameId);
	_gameObjectAddress = _resMan->findGameObject();

	_scriptPatcher = new ScriptPatcher();
	SegManager *segMan = new SegManager(_resMan, _scriptPatcher);

	// Read user option for forcing hires graphics
	// Only show/selectable for:
	//  - King's Quest 6 CD
	//  - King's Quest 6 CD demo
	//  - Gabriel Knight 1 CD
	//  - Police Quest 4 CD
	// TODO: Check, if Gabriel Knight 1 floppy supports high resolution
	//
	// Gabriel Knight 1 on Mac is hi-res only, so it should NOT get this option.
	// Confirmed by [md5] and originally by clone2727.
	if (Common::checkGameGUIOption(GAMEOPTION_HIGH_RESOLUTION_GRAPHICS, ConfMan.get("guioptions"))) {
		// GAMEOPTION_HIGH_RESOLUTION_GRAPHICS is available for the currently detected game,
		// so read the user option now.
		// We need to do this, because the option's default is "true", but we don't want "true"
		// for any game that does not have this option.
		_forceHiresGraphics = ConfMan.getBool("enable_high_resolution_graphics");
	}

	if (getSciVersion() < SCI_VERSION_2) {
		// Initialize the game screen
		_gfxScreen = new GfxScreen(_resMan);
		_gfxScreen->enableUndithering(ConfMan.getBool("disable_dithering"));
	} else {
		_gfxScreen = nullptr;
	}

	_kernel = new Kernel(_resMan, segMan);
	_kernel->init();

	_features = new GameFeatures(segMan, _kernel);
	// Only SCI0, SCI01 and SCI1 EGA games used a parser
	_vocabulary = (getSciVersion() <= SCI_VERSION_1_EGA_ONLY) ? new Vocabulary(_resMan, false) : NULL;
	// Also, XMAS1990 apparently had a parser too. Refer to http://forums.scummvm.org/viewtopic.php?t=9135
	if (getGameId() == GID_CHRISTMAS1990)
		_vocabulary = new Vocabulary(_resMan, false);

	_gamestate = new EngineState(segMan);
	_eventMan = new EventManager(_resMan->detectFontExtended());
#ifdef ENABLE_SCI32
	if (getSciVersion() >= SCI_VERSION_2_1_EARLY) {
		_audio32 = new Audio32(_resMan);
	} else
#endif
		_audio = new AudioPlayer(_resMan);
#ifdef ENABLE_SCI32
	if (getSciVersion() >= SCI_VERSION_2) {
		_video32 = new Video32(segMan, _eventMan);
	}
#endif
	_sync = new Sync(_resMan, segMan);

	// Create debugger console. It requires GFX and _gamestate to be initialized
	_console = new Console(this);

	// The game needs to be initialized before the graphics system is initialized, as
	// the graphics code checks parts of the seg manager upon initialization (e.g. for
	// the presence of the fastCast object)
	if (!initGame()) { /* Initialize */
		warning("Game initialization failed: Aborting...");
		// TODO: Add an "init failed" error?
		return Common::kUnknownError;
	}

	// we try to find the super class address of the game object, we can't do that earlier
	const Object *gameObject = segMan->getObject(_gameObjectAddress);
	if (!gameObject) {
		warning("Could not get game object, aborting...");
		return Common::kUnknownError;
	}

	script_adjust_opcode_formats();

	// Must be called after game_init(), as they use _features
	_kernel->loadKernelNames(_features);

	// Load our Mac executable here for icon bar palettes and high-res fonts
	loadMacExecutable();

	// Initialize all graphics related subsystems
	initGraphics();

	// Sound must be initialized after graphics because SysEx transfers at the
	// start of the game must pump the event loop to avoid making the OS think
	// that ScummVM is hanged, and pumping the event loop requires GfxCursor to
	// be initialized
	_soundCmd = new SoundCommandParser(_resMan, segMan, _kernel, _audio, _features->detectDoSoundType());

	syncSoundSettings();
	syncIngameAudioOptions();

	// Patch in our save/restore code, so that dialogs are replaced
	patchGameSaveRestore();
	setLauncherLanguage();

	// Check whether loading a savestate was requested
	int directSaveSlotLoading = ConfMan.getInt("save_slot");
	if (directSaveSlotLoading >= 0) {
		_gamestate->_delayedRestoreGame = true;
		_gamestate->_delayedRestoreGameId = directSaveSlotLoading;
		_gamestate->_delayedRestoreFromLauncher = true;

		// Jones only initializes its menus when restarting/restoring, thus set
		// the gameIsRestarting flag here before initializing. Fixes bug #6536.
		if (g_sci->getGameId() == GID_JONES)
			_gamestate->gameIsRestarting = GAMEISRESTARTING_RESTORE;
	}

	// Show any special warnings for buggy scripts with severe game bugs,
	// which have been patched by Sierra
	if (getGameId() == GID_LONGBOW) {
		// Longbow 1.0 has a buggy script which prevents the game
		// from progressing during the Green Man riddle sequence.
		// A patch for this buggy script has been released by Sierra,
		// and is necessary to complete the game without issues.
		// The patched script is included in Longbow 1.1.
		// Refer to bug #3036609.
		Resource *buggyScript = _resMan->findResource(ResourceId(kResourceTypeScript, 180), 0);

		if (buggyScript && (buggyScript->size == 12354 || buggyScript->size == 12362)) {
			showScummVMDialog("A known buggy game script has been detected, which could "
			                  "prevent you from progressing later on in the game, during "
			                  "the sequence with the Green Man's riddles. Please, apply "
			                  "the latest patch for this game by Sierra to avoid possible "
			                  "problems");
		}
	}

	if (getGameId() == GID_KQ7 && ConfMan.getBool("subtitles")) {
		showScummVMDialog("Subtitles are enabled, but subtitling in King's"
						  " Quest 7 was unfinished and disabled in the release"
						  " version of the game. ScummVM allows the subtitles"
						  " to be re-enabled, but because they were removed from"
						  " the original game, they do not always render"
						  " properly or reflect the actual game speech."
						  " This is not a ScummVM bug -- it is a problem with"
						  " the game's assets.");
	}

	// Show a warning if the user has selected a General MIDI device, no GM patch exists
	// (i.e. patch 4) and the game is one of the known 8 SCI1 games that Sierra has provided
	// after market patches for in their "General MIDI Utility".
	if (_soundCmd->getMusicType() == MT_GM && !ConfMan.getBool("native_mt32")) {
		if (!_resMan->findResource(ResourceId(kResourceTypePatch, 4), 0)) {
			switch (getGameId()) {
			case GID_ECOQUEST:
			case GID_HOYLE3:
			case GID_LSL1:
			case GID_LSL5:
			case GID_LONGBOW:
			case GID_SQ1:
			case GID_SQ4:
			case GID_FAIRYTALES:
				showScummVMDialog("You have selected General MIDI as a sound device. Sierra "
				                  "has provided after-market support for General MIDI for this "
				                  "game in their \"General MIDI Utility\". Please, apply this "
				                  "patch in order to enjoy MIDI music with this game. Once you "
				                  "have obtained it, you can unpack all of the included *.PAT "
				                  "files in your ScummVM extras folder and ScummVM will add the "
				                  "appropriate patch automatically. Alternatively, you can follow "
				                  "the instructions in the READ.ME file included in the patch and "
				                  "rename the associated *.PAT file to 4.PAT and place it in the "
				                  "game folder. Without this patch, General MIDI music for this "
				                  "game will sound badly distorted.");
				break;
			default:
				break;
			}
		}
	}

	if (gameHasFanMadePatch()) {
		showScummVMDialog("Your game is patched with a fan made script patch. Such patches have "
		                  "been reported to cause issues, as they modify game scripts extensively. "
		                  "The issues that these patches fix do not occur in ScummVM, so you are "
		                  "advised to remove this patch from your game folder in order to avoid "
		                  "having unexpected errors and/or issues later on.");
	}

	runGame();

	ConfMan.flushToDisk();

	return Common::kNoError;
}
Esempio n. 9
0
void SciEngine::patchGameSaveRestore() {
	SegManager *segMan = _gamestate->_segMan;
	const Object *gameObject = segMan->getObject(_gameObjectAddress);
	const uint16 gameMethodCount = gameObject->getMethodCount();
	const Object *gameSuperObject = segMan->getObject(_gameSuperClassAddress);
	if (!gameSuperObject)
		gameSuperObject = gameObject;	// happens in KQ5CD, when loading saved games before r54510
	const uint16 gameSuperMethodCount = gameSuperObject->getMethodCount();
	reg_t methodAddress;
	const uint16 kernelCount = _kernel->getKernelNamesSize();
	const byte *scriptRestorePtr = NULL;
	byte kernelIdRestore = 0;
	const byte *scriptSavePtr = NULL;
	byte kernelIdSave = 0;

	// this feature is currently not supported on SCI32
	if (getSciVersion() >= SCI_VERSION_2)
		return;

	switch (_gameId) {
	case GID_MOTHERGOOSE256: // mother goose saves/restores directly and has no save/restore dialogs
	case GID_JONES: // gets confused, when we patch us in, the game is only able to save to 1 slot, so hooking is not required
	case GID_HOYLE1: // gets confused, although the game doesnt support saving/restoring at all
	case GID_HOYLE2: // gets confused, see hoyle1
		return;
	default:
		break;
	}

	if (ConfMan.getBool("sci_originalsaveload"))
		return;

	for (uint16 kernelNr = 0; kernelNr < kernelCount; kernelNr++) {
		Common::String kernelName = _kernel->getKernelName(kernelNr);
		if (kernelName == "RestoreGame")
			kernelIdRestore = kernelNr;
		if (kernelName == "SaveGame")
			kernelIdSave = kernelNr;
	}

	// Search for gameobject-superclass ::restore
	for (uint16 methodNr = 0; methodNr < gameSuperMethodCount; methodNr++) {
		uint16 selectorId = gameSuperObject->getFuncSelector(methodNr);
		Common::String methodName = _kernel->getSelectorName(selectorId);
		if (methodName == "restore") {
			methodAddress = gameSuperObject->getFunction(methodNr);
			Script *script = segMan->getScript(methodAddress.segment);
			scriptRestorePtr = script->getBuf(methodAddress.offset);
		}
		if (methodName == "save") {
			methodAddress = gameSuperObject->getFunction(methodNr);
			Script *script = segMan->getScript(methodAddress.segment);
			scriptSavePtr = script->getBuf(methodAddress.offset);
		}
	}

	// Search for gameobject ::save, if there is one patch that one instead
	for (uint16 methodNr = 0; methodNr < gameMethodCount; methodNr++) {
		uint16 selectorId = gameObject->getFuncSelector(methodNr);
		Common::String methodName = _kernel->getSelectorName(selectorId);
		if (methodName == "save") {
			methodAddress = gameObject->getFunction(methodNr);
			Script *script = segMan->getScript(methodAddress.segment);
			scriptSavePtr = script->getBuf(methodAddress.offset);
			break;
		}
	}

	switch (_gameId) {
	case GID_FAIRYTALES: // fairy tales automatically saves w/o dialog
		scriptSavePtr = NULL;
	default:
		break;
	}

	if (scriptRestorePtr) {
		// Now patch in our code
		byte *patchPtr = const_cast<byte *>(scriptRestorePtr);
		memcpy(patchPtr, patchGameRestoreSave, sizeof(patchGameRestoreSave));
		patchPtr[8] = kernelIdRestore;
	}
	if (scriptSavePtr) {
		// Now patch in our code
		byte *patchPtr = const_cast<byte *>(scriptSavePtr);
		memcpy(patchPtr, patchGameRestoreSave, sizeof(patchGameRestoreSave));
		patchPtr[8] = kernelIdSave;
	}
}
Esempio n. 10
0
Common::Error SciEngine::run() {
	g_eventRec.registerRandomSource(_rng, "sci");

	// Assign default values to the config manager, in case settings are missing
	ConfMan.registerDefault("sci_originalsaveload", "false");
	ConfMan.registerDefault("native_fb01", "false");
	ConfMan.registerDefault("windows_cursors", "false");	// Windows cursors for KQ6 Windows

	_resMan = new ResourceManager();
	assert(_resMan);
	_resMan->addAppropriateSources();
	_resMan->init();

	// TODO: Add error handling. Check return values of addAppropriateSources
	// and init. We first have to *add* sensible return values, though ;).
/*
	if (!_resMan) {
		warning("No resources found, aborting");
		return Common::kNoGameDataFoundError;
	}
*/

	// Reset, so that error()s before SoundCommandParser is initialized wont cause a crash
	_soundCmd = NULL;

	// Add the after market GM patches for the specified game, if they exist
	_resMan->addNewGMPatch(_gameId);
	_gameObjectAddress = _resMan->findGameObject();
	_gameSuperClassAddress = NULL_REG;

	SegManager *segMan = new SegManager(_resMan);

	// Initialize the game screen
	_gfxScreen = new GfxScreen(_resMan);
	_gfxScreen->debugUnditherSetState(ConfMan.getBool("disable_dithering"));

	// Create debugger console. It requires GFX to be initialized
	_console = new Console(this);
	_kernel = new Kernel(_resMan, segMan);

	_features = new GameFeatures(segMan, _kernel);
	// Only SCI0, SCI01 and SCI1 EGA games used a parser
	_vocabulary = (getSciVersion() <= SCI_VERSION_1_EGA) ? new Vocabulary(_resMan, false) : NULL;
	// Also, XMAS1990 apparently had a parser too. Refer to http://forums.scummvm.org/viewtopic.php?t=9135
	if (getGameId() == GID_CHRISTMAS1990)
		_vocabulary = new Vocabulary(_resMan, false);
	_audio = new AudioPlayer(_resMan);
	_gamestate = new EngineState(segMan);
	_eventMan = new EventManager(_resMan->detectFontExtended());

	// The game needs to be initialized before the graphics system is initialized, as
	// the graphics code checks parts of the seg manager upon initialization (e.g. for
	// the presence of the fastCast object)
	if (!initGame()) { /* Initialize */
		warning("Game initialization failed: Aborting...");
		// TODO: Add an "init failed" error?
		return Common::kUnknownError;
	}

	// we try to find the super class address of the game object, we can't do that earlier
	const Object *gameObject = segMan->getObject(_gameObjectAddress);
	if (!gameObject) {
		warning("Could not get game object, aborting...");
		return Common::kUnknownError;
	}
	_gameSuperClassAddress = gameObject->getSuperClassSelector();

	script_adjust_opcode_formats();

	// Must be called after game_init(), as they use _features
	_kernel->loadKernelNames(_features);
	_soundCmd = new SoundCommandParser(_resMan, segMan, _kernel, _audio, _features->detectDoSoundType());

	syncSoundSettings();
	syncIngameAudioOptions();

	// Initialize all graphics related subsystems
	initGraphics();

	debug("Emulating SCI version %s\n", getSciVersionDesc(getSciVersion()));

	// Patch in our save/restore code, so that dialogs are replaced
	patchGameSaveRestore();
	setLauncherLanguage();

	// Check whether loading a savestate was requested
	int directSaveSlotLoading = ConfMan.getInt("save_slot");
	if (directSaveSlotLoading >= 0) {
		// call GameObject::play (like normally)
		initStackBaseWithSelector(SELECTOR(play));
		// We set this, so that the game automatically quit right after init
		_gamestate->variables[VAR_GLOBAL][4] = TRUE_REG;

		_gamestate->_executionStackPosChanged = false;
		run_vm(_gamestate);

		// As soon as we get control again, actually restore the game
		reg_t restoreArgv[2] = { NULL_REG, make_reg(0, directSaveSlotLoading) };	// special call (argv[0] is NULL)
		kRestoreGame(_gamestate, 2, restoreArgv);

		// this indirectly calls GameObject::init, which will setup menu, text font/color codes etc.
		//  without this games would be pretty badly broken
	}

	// Show any special warnings for buggy scripts with severe game bugs, 
	// which have been patched by Sierra
	if (getGameId() == GID_LONGBOW) {
		// Longbow 1.0 has a buggy script which prevents the game
		// from progressing during the Green Man riddle sequence.
		// A patch for this buggy script has been released by Sierra,
		// and is necessary to complete the game without issues.
		// The patched script is included in Longbow 1.1.
		// Refer to bug #3036609.
		Resource *buggyScript = _resMan->findResource(ResourceId(kResourceTypeScript, 180), 0);

		if (buggyScript && (buggyScript->size == 12354 || buggyScript->size == 12362)) {
			showScummVMDialog("A known buggy game script has been detected, which could "
			                  "prevent you from progressing later on in the game, during "
			                  "the sequence with the Green Man's riddles. Please, apply "
			                  "the latest patch for this game by Sierra to avoid possible "
			                  "problems");
		}
	}

	// Show a warning if the user has selected a General MIDI device, no GM patch exists
	// (i.e. patch 4) and the game is one of the known 8 SCI1 games that Sierra has provided
	// after market patches for in their "General MIDI Utility".
	if (_soundCmd->getMusicType() == MT_GM && !ConfMan.getBool("native_mt32")) {
		if (!_resMan->findResource(ResourceId(kResourceTypePatch, 4), 0)) {
			switch (getGameId()) {
			case GID_ECOQUEST:
			case GID_HOYLE3:
			case GID_LSL1:
			case GID_LSL5:
			case GID_LONGBOW:
			case GID_SQ1:
			case GID_SQ4:
			case GID_FAIRYTALES:
				showScummVMDialog("You have selected General MIDI as a sound device. Sierra "
				                  "has provided after-market support for General MIDI for this "
				                  "game in their \"General MIDI Utility\". Please, apply this "
				                  "patch in order to enjoy MIDI music with this game. Once you "
				                  "have obtained it, you can unpack all of the included *.PAT "
				                  "files in your ScummVM extras folder and ScummVM will add the "
				                  "appropriate patch automatically. Alternatively, you can follow "
				                  "the instructions in the READ.ME file included in the patch and "
				                  "rename the associated *.PAT file to 4.PAT and place it in the "
				                  "game folder. Without this patch, General MIDI music for this "
				                  "game will sound badly distorted.");
				break;
			default:
				break;
			}
		}
	}

	if (gameHasFanMadePatch()) {
		showScummVMDialog("Your game is patched with a fan made script patch. Such patches have "
		                  "been reported to cause issues, as they modify game scripts extensively. "
		                  "The issues that these patches fix do not occur in ScummVM, so you are "
		                  "advised to remove this patch from your game folder in order to avoid "
		                  "having unexpected errors and/or issues later on.");
	}

	runGame();

	ConfMan.flushToDisk();

	return Common::kNoError;
}
Esempio n. 11
0
void SciEngine::patchGameSaveRestore() {
	SegManager *segMan = _gamestate->_segMan;
	const Object *gameObject = segMan->getObject(_gameObjectAddress);
	const Object *gameSuperObject = segMan->getObject(gameObject->getSuperClassSelector());
	if (!gameSuperObject)
		gameSuperObject = gameObject;	// happens in KQ5CD, when loading saved games before r54510
	byte kernelIdRestore = 0;
	byte kernelIdSave = 0;

	switch (_gameId) {
	case GID_HOYLE1: // gets confused, although the game doesnt support saving/restoring at all
	case GID_HOYLE2: // gets confused, see hoyle1
	case GID_JONES: // gets confused, when we patch us in, the game is only able to save to 1 slot, so hooking is not required
	case GID_MOTHERGOOSE256: // mother goose saves/restores directly and has no save/restore dialogs
	case GID_PHANTASMAGORIA: // has custom save/load code
	case GID_SHIVERS: // has custom save/load code
		return;
	default:
		break;
	}

	if (ConfMan.getBool("originalsaveload"))
		return;

	uint16 kernelNamesSize = _kernel->getKernelNamesSize();
	for (uint16 kernelNr = 0; kernelNr < kernelNamesSize; kernelNr++) {
		Common::String kernelName = _kernel->getKernelName(kernelNr);
		if (kernelName == "RestoreGame")
			kernelIdRestore = kernelNr;
		if (kernelName == "SaveGame")
			kernelIdSave = kernelNr;
		if (kernelName == "Save")
			kernelIdSave = kernelIdRestore = kernelNr;
	}

	// Search for gameobject superclass ::restore
	uint16 gameSuperObjectMethodCount = gameSuperObject->getMethodCount();
	for (uint16 methodNr = 0; methodNr < gameSuperObjectMethodCount; methodNr++) {
		uint16 selectorId = gameSuperObject->getFuncSelector(methodNr);
		Common::String methodName = _kernel->getSelectorName(selectorId);
		if (methodName == "restore") {
			if (kernelIdSave != kernelIdRestore)
				patchGameSaveRestoreCode(segMan, gameSuperObject->getFunction(methodNr), kernelIdRestore);
			else
				patchGameSaveRestoreCodeSci21(segMan, gameSuperObject->getFunction(methodNr), kernelIdRestore, true);
		}
		else if (methodName == "save") {
			if (_gameId != GID_FAIRYTALES) {	// Fairy Tales saves automatically without a dialog
				if (kernelIdSave != kernelIdRestore)
					patchGameSaveRestoreCode(segMan, gameSuperObject->getFunction(methodNr), kernelIdSave);
				else
					patchGameSaveRestoreCodeSci21(segMan, gameSuperObject->getFunction(methodNr), kernelIdSave, false);
			}
		}
	}

	// Search for gameobject ::save, if there is one patch that one too
	uint16 gameObjectMethodCount = gameObject->getMethodCount();
	for (uint16 methodNr = 0; methodNr < gameObjectMethodCount; methodNr++) {
		uint16 selectorId = gameObject->getFuncSelector(methodNr);
		Common::String methodName = _kernel->getSelectorName(selectorId);
		if (methodName == "save") {
			if (_gameId != GID_FAIRYTALES) {	// Fairy Tales saves automatically without a dialog
				if (kernelIdSave != kernelIdRestore)
					patchGameSaveRestoreCode(segMan, gameObject->getFunction(methodNr), kernelIdSave);
				else
					patchGameSaveRestoreCodeSci21(segMan, gameObject->getFunction(methodNr), kernelIdSave, false);
			}
			break;
		}
	}
}