Пример #1
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();

	SegManager *segMan = new SegManager(_resMan);

	// Initialize the game screen
	_gfxScreen = new GfxScreen(_resMan);
	_gfxScreen->enableUndithering(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_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);
	_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;
	}

	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();

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

	// Initialize all graphics related subsystems
	initGraphics();

	// 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;
}
Пример #2
0
reg_t_hash_map *find_all_used_references(EngineState *s) {
	SegManager *sm = s->seg_manager;
	reg_t_hash_map *normal_map = NULL;
	WorklistManager wm;
	uint i;

	// Initialise
	// Init: Registers
	wm.push(s->r_acc);
	wm.push(s->r_prev);
	// Init: Value Stack
	// We do this one by hand since the stack doesn't know the current execution stack
	{
		ExecStack &xs = s->_executionStack.back();
		reg_t *pos;

		for (pos = s->stack_base; pos < xs.sp; pos++)
			wm.push(*pos);
	}

	debugC(2, kDebugLevelGC, "[GC] -- Finished adding value stack");

	// Init: Execution Stack
	Common::List<ExecStack>::iterator iter;
	for (iter = s->_executionStack.begin();
	     iter != s->_executionStack.end(); ++iter) {
		ExecStack &es = *iter;

		if (es.type != EXEC_STACK_TYPE_KERNEL) {
			wm.push(es.objp);
			wm.push(es.sendp);
			if (es.type == EXEC_STACK_TYPE_VARSELECTOR)
				wm.push(*(es.getVarPointer(s)));
		}
	}

	debugC(2, kDebugLevelGC, "[GC] -- Finished adding execution stack");

	// Init: Explicitly loaded scripts
	for (i = 1; i < sm->_heap.size(); i++)
		if (sm->_heap[i]
		        && sm->_heap[i]->getType() == MEM_OBJ_SCRIPT) {
			Script *script = (Script *)sm->_heap[i];

			if (script->lockers) { // Explicitly loaded?
				// Locals, if present
				wm.push(make_reg(script->locals_segment, 0));

				// All objects (may be classes, may be indirectly reachable)
				for (uint obj_nr = 0; obj_nr < script->_objects.size(); obj_nr++) {
					wm.push(script->_objects[obj_nr].pos);
				}
			}
		}

	debugC(2, kDebugLevelGC, "[GC] -- Finished explicitly loaded scripts, done with root set\n");

	// Run Worklist Algorithm
	while (!wm._worklist.empty()) {
		reg_t reg = wm._worklist.back();
		wm._worklist.pop_back();
		if (reg.segment != s->stack_segment) { // No need to repeat this one
			debugC(2, kDebugLevelGC, "[GC] Checking %04x:%04x\n", PRINT_REG(reg));
			if (reg.segment < sm->_heap.size() && sm->_heap[reg.segment])
				sm->_heap[reg.segment]->listAllOutgoingReferences(s, reg, &wm, add_outgoing_refs);
		}
	}

	// Normalise
	normal_map = normalise_hashmap_ptrs(sm, wm._map);

	return normal_map;
}
Пример #3
0
int main(int argc,char *argv[]) {
  FILE *fpA;
  FILE *fpB;
  FILE *fpC;
  int c;  
  unsigned int alpha=0xff;

  if (argc !=1) sscanf(argv[1],"%x",&alpha);

  fpA=fopen("paltable.c","w");
  fpB=fopen("stdkey.h","w");
  fprintf(stderr,"making spectra.\n");
  make_reg();
  fprintf(stderr,"making color bars.\n");
  make_root();
  make_out_reg(256,17,256);
  fprintf(stderr,"printing palette table.\n");
  print_reg(fpA,"KeyPalReg",256);
  print_reg_hdr(fpB,"KeyPalReg",256);
  fprintf(stderr,"printing rgb tables.\n");
  make_map(256);
  print_map(fpA,"KeyLinear",256,alpha);
  print_hdr(fpB,"KeyLinear",256);

  fpC=fopen("linear-mag.key","w");
  fprintf(fpC,"# linear magnitude\n");
  fprintf(fpC,"%d\n",256);
  for (c=0;c<256;c++) 
    fprintf(fpC,"%.2x%.2x%.2x%.2x\n",alpha,mag[c][0],mag[c][1],mag[c][2]);
  fclose(fpC);

  fpC=fopen("linear-sgn.key","w");
  fprintf(fpC,"# linear signed\n");
  fprintf(fpC,"%d\n",256);
  for (c=0;c<256;c++) 
    fprintf(fpC,"%.2x%.2x%.2x%.2x\n",alpha,sgn[c][0],sgn[c][1],sgn[c][2]);
  fclose(fpC);


  make_map(20);
  print_map(fpA,"KeyTwentyBlk",20,alpha);
  print_hdr(fpB,"KeyTwentyBlk",20); 

  fpC=fopen("twentyblk-mag.key","w");
  fprintf(fpC,"# twentyblk magnitude\n");
  fprintf(fpC,"%d\n",20);
  for (c=0;c<20;c++) 
    fprintf(fpC,"%.2x%.2x%.2x%.2x\n",alpha,mag[c][0],mag[c][1],mag[c][2]);
  fclose(fpC);

  fpC=fopen("twentyblk-sgn.key","w");
  fprintf(fpC,"# twentyblk signed\n");
  fprintf(fpC,"%d\n",20);
  for (c=0;c<20;c++) 
    fprintf(fpC,"%.2x%.2x%.2x%.2x\n",alpha,sgn[c][0],sgn[c][1],sgn[c][2]);
  fclose(fpC);

  make_map(10);
  print_map(fpA,"KeyTenBlk",10,alpha);
  print_hdr(fpB,"KeyTenBlk",10); 

  fpC=fopen("tenblk-mag.key","w");
  fprintf(fpC,"# tenblk magnitude\n");
  fprintf(fpC,"%d\n",10);
  for (c=0;c<10;c++) 
    fprintf(fpC,"%.2x%.2x%.2x%.2x\n",alpha,mag[c][0],mag[c][1],mag[c][2]);
  fclose(fpC);

  fpC=fopen("tenblk-sgn.key","w");
  fprintf(fpC,"# tenblk signed\n");
  fprintf(fpC,"%d\n",10);
  for (c=0;c<10;c++) 
    fprintf(fpC,"%.2x%.2x%.2x%.2x\n",alpha,sgn[c][0],sgn[c][1],sgn[c][2]);
  fclose(fpC);

  make_map(5);
  print_map(fpA,"KeyFiveBlk",5,alpha);
  print_hdr(fpB,"KeyFiveBlk",5);

  fpC=fopen("fiveblk-mag.key","w");
  fprintf(fpC,"# fiveblk magnitude\n");
  fprintf(fpC,"%d\n",256);
  for (c=0;c<5;c++) 
    fprintf(fpC,"%.2x%.2x%.2x%.2x\n",alpha,mag[c][0],mag[c][1],mag[c][2]);
  fclose(fpC);

  fpC=fopen("fiveblk-sgn.key","w");
  fprintf(fpC,"# fiveblk signed\n");
  fprintf(fpC,"%d\n",256);
  for (c=0;c<5;c++) 
    fprintf(fpC,"%.2x%.2x%.2x%.2x\n",alpha,sgn[c][0],sgn[c][1],sgn[c][2]);
  fclose(fpC);

  make_cmap(256);
  print_cmap(fpA,"KeyLinearC",256);
  print_chdr(fpB,"KeyLinearC",256);
  
  make_cmap(20);
  print_cmap(fpA,"KeyTwentyblkC",20);
  print_chdr(fpB,"KeyTwentyBlkC",20);
  
  make_cmap(10);
  print_cmap(fpA,"KeyTenBlkC",10);
  print_chdr(fpB,"KeyTenBlkC",10); 
  make_cmap(5);
  print_cmap(fpA,"KeyFiveBlkC",5);
  print_chdr(fpB,"KeyFiveBlkC",5);

  fclose(fpA);
  fclose(fpB);
  return 0;
}
Пример #4
0
reg_t SoundCommandParser::kDoSoundGetPolyphony(int argc, reg_t *argv, reg_t acc) {
	return make_reg(0, _music->soundGetVoices());	// Get the number of voices
}
Пример #5
0
reg_t kRespondsTo(EngineState *s, int argc, reg_t *argv) {
	reg_t obj = argv[0];
	int selector = argv[1].toUint16();

	return make_reg(0, s->_segMan->isHeapObject(obj) && lookupSelector(s->_segMan, obj, selector, NULL, NULL) != kSelectorNone);
}
Пример #6
0
reg_t kIsObject(EngineState *s, int argc, reg_t *argv) {
	if (argv[0].offset == SIGNAL_OFFSET) // Treated specially
		return NULL_REG;
	else
		return make_reg(0, s->_segMan->isHeapObject(argv[0]));
}
Пример #7
0
// Returns script dispatch address index in the supplied script
reg_t kScriptID(EngineState *s, int argc, reg_t *argv) {
	int script = argv[0].toUint16();
	uint16 index = (argc > 1) ? argv[1].toUint16() : 0;

	if (argv[0].segment)
		return argv[0];

	SegmentId scriptSeg = s->_segMan->getScriptSegment(script, SCRIPT_GET_LOAD);

	if (!scriptSeg)
		return NULL_REG;

	Script *scr = s->_segMan->getScript(scriptSeg);

	if (!scr->getExportsNr()) {
		// This is normal. Some scripts don't have a dispatch (exports) table,
		// and this call is probably used to load them in memory, ignoring
		// the return value. If only one argument is passed, this call is done
		// only to load the script in memory. Thus, don't show any warning,
		// as no return value is expected. If an export is requested, then
		// it will most certainly fail with OOB access.
		if (argc == 2)
			error("Script 0x%x does not have a dispatch table and export %d "
					"was requested from it", script, index);
		return NULL_REG;
	}

	if (index > scr->getExportsNr()) {
		error("Dispatch index too big: %d > %d", index, scr->getExportsNr());
		return NULL_REG;
	}

	uint16 address = scr->validateExportFunc(index, true);

	// Point to the heap for SCI1.1 - SCI2.1 games
	if (getSciVersion() >= SCI_VERSION_1_1 && getSciVersion() <= SCI_VERSION_2_1)
		address += scr->getScriptSize();

	// Bugfix for the intro speed in PQ2 version 1.002.011.
	// This is taken from the patch by NewRisingSun(NRS) / Belzorash. Global 3
	// is used for timing during the intro, and in the problematic version it's
	// initialized to 0, whereas it's 6 in other versions. Thus, we assign it
	// to 6 here, fixing the speed of the introduction. Refer to bug #3102071.
	if (g_sci->getGameId() == GID_PQ2 && script == 200) {
		if (s->variables[VAR_GLOBAL][3].isNull()) {
			warning("Fixing speed in the intro of PQ2, version 1.002.011");
			s->variables[VAR_GLOBAL][3] = make_reg(0, 6);
		}
	}

	// HACK: Prevent the murderer from getting stuck behind the door in
	// Colonel's Bequest, room 215. A temporary fix for bug #3122075.
	// TODO/FIXME: Add a proper fix for this. There is a regression in this
	// scene with the new kInitBresen and kDoBresen functions (r52467). Using
	// just the "old" kInitBresen works. This hack is added for now because the
	// two functions are quite complex. The "old" versions were created based
	// on observations, and not on the interpreter itself, thus they have a lot
	// of differences in the way they behave and set variables to the mover object.
	// Since this is just a death scene where Laura is supposed to die anyway,
	// figuring out the exact cause of this is just not worth the effort.
	// Differences between the new and the old kInitBresen to the MoveTo object:
	// dy: 1 (new) - 2 (old)
	// b-i1: 20 (new) - 12 (old)
	// b-di: 65526 (new) - 65516 (old)
	// Performing the changes above to MoveTo (0017:033a) allows the killer to
	// move. Note that the actual issue might not be with kInitBresen/kDoBresen,
	// and there might be another underlying problem here.
	if (g_sci->getGameId() == GID_LAURABOW && script == 215) {
		warning("Moving actor position for the shower scene of Colonel's Bequest");
		writeSelectorValue(s->_segMan, s->_segMan->findObjectByName("killer"), SELECTOR(x), 6);
	}

	return make_reg(scriptSeg, address);
}
Пример #8
0
reg_t kGetEvent(EngineState *s, int argc, reg_t *argv) {
	int mask = argv[0].toUint16();
	reg_t obj = argv[1];
	SciEvent curEvent;
	int modifier_mask = getSciVersion() <= SCI_VERSION_01 ? SCI_KEYMOD_ALL : SCI_KEYMOD_NO_FOOLOCK;
	uint16 modifiers = 0;
	SegManager *segMan = s->_segMan;
	Common::Point mousePos;

	// For Mac games with an icon bar, handle possible icon bar events first
	if (g_sci->hasMacIconBar()) {
		reg_t iconObj = g_sci->_gfxMacIconBar->handleEvents();
		if (!iconObj.isNull())
			invokeSelector(s, iconObj, SELECTOR(select), argc, argv, 0, NULL);
	}

	// If there's a simkey pending, and the game wants a keyboard event, use the
	// simkey instead of a normal event
	if (g_debug_simulated_key && (mask & SCI_EVENT_KEYBOARD)) {
		// In case we use a simulated event we query the current mouse position
		mousePos = g_sci->_gfxCursor->getPosition();
#ifdef ENABLE_SCI32
		if (getSciVersion() >= SCI_VERSION_2_1_EARLY)
			g_sci->_gfxCoordAdjuster->fromDisplayToScript(mousePos.y, mousePos.x);
#endif
		// Limit the mouse cursor position, if necessary
		g_sci->_gfxCursor->refreshPosition();

		writeSelectorValue(segMan, obj, SELECTOR(type), SCI_EVENT_KEYBOARD); // Keyboard event
		writeSelectorValue(segMan, obj, SELECTOR(message), g_debug_simulated_key);
		writeSelectorValue(segMan, obj, SELECTOR(modifiers), SCI_KEYMOD_NUMLOCK); // Numlock on
		writeSelectorValue(segMan, obj, SELECTOR(x), mousePos.x);
		writeSelectorValue(segMan, obj, SELECTOR(y), mousePos.y);
		g_debug_simulated_key = 0;
		return make_reg(0, 1);
	}
	
	curEvent = g_sci->getEventManager()->getSciEvent(mask);

	if (s->_delayedRestoreGame) {
		// delayed restore game from ScummVM menu got triggered
		gamestate_delayedrestore(s);
		return NULL_REG;
	}

	// For a real event we use its associated mouse position
	mousePos = curEvent.mousePos;
#ifdef ENABLE_SCI32
	if (getSciVersion() >= SCI_VERSION_2_1_EARLY)
		g_sci->_gfxCoordAdjuster->fromDisplayToScript(mousePos.y, mousePos.x);
#endif
	// Limit the mouse cursor position, if necessary
	g_sci->_gfxCursor->refreshPosition();

	if (g_sci->getVocabulary())
		g_sci->getVocabulary()->parser_event = NULL_REG; // Invalidate parser event

	if (s->_cursorWorkaroundActive) {
		// We check if the actual cursor position is inside specific rectangles
		// where the cursor itself should be moved to. If this is the case, we
		// set the mouse cursor's position to be within the rectangle in
		// question. Check GfxCursor::setPosition(), for a more detailed
		// explanation and a list of cursor position workarounds.
		if (s->_cursorWorkaroundRect.contains(mousePos.x, mousePos.y)) {
			// For OpenPandora and possibly other platforms, that support analog-stick control + touch screen
			// control at the same time: in case the cursor is currently at the coordinate set by the scripts,
			// we will count down instead of immediately disabling the workaround.
			// On OpenPandora the cursor position is set, but it's overwritten shortly afterwards by the
			// touch screen. In this case we would sometimes disable the workaround, simply because the touch
			// screen hasn't yet overwritten the position and thus the workaround would not work anymore.
			// On OpenPandora it would sometimes work and sometimes not without this.
			if (s->_cursorWorkaroundPoint == mousePos) {
				// Cursor is still at the same spot as set by the scripts
				if (s->_cursorWorkaroundPosCount > 0) {
					s->_cursorWorkaroundPosCount--;
				} else {
					// Was for quite a bit of time at that spot, so disable workaround now
					s->_cursorWorkaroundActive = false;
				}
			} else {
				// Cursor has moved, but is within the rect -> disable workaround immediately
				s->_cursorWorkaroundActive = false;
			}
		} else {
			mousePos.x = s->_cursorWorkaroundPoint.x;
			mousePos.y = s->_cursorWorkaroundPoint.y;
		}
	}

	writeSelectorValue(segMan, obj, SELECTOR(x), mousePos.x);
	writeSelectorValue(segMan, obj, SELECTOR(y), mousePos.y);

	// Get current keyboard modifiers, only keep relevant bits
	modifiers = curEvent.modifiers & modifier_mask;
	if (g_sci->getPlatform() == Common::kPlatformDOS) {
		// We are supposed to emulate SCI running in DOS

		// We set the higher byte of the modifiers to 02h
		// Original SCI also did that indirectly, because it asked BIOS for shift status
		// via AH=0x02 INT16, which then sets the shift flags in AL
		// AH is supposed to be destroyed in that case and it's not defined that 0x02
		// is still in it on return. The value of AX was then set into the modifiers selector.
		// At least one fan-made game (Betrayed Alliance) requires 0x02 to be in the upper byte,
		// otherwise the darts game (script 111) will not work properly.

		// It seems Sierra fixed this behaviour (effectively bug) in the SCI1 keyboard driver.
		// SCI32 also resets the upper byte.

		// This was verified in SSCI itself by creating a SCI game and checking behavior.
		if (getSciVersion() <= SCI_VERSION_01) {
			modifiers |= 0x0200;
		}
	}

	switch (curEvent.type) {
	case SCI_EVENT_QUIT:
		s->abortScriptProcessing = kAbortQuitGame; // Terminate VM
		g_sci->_debugState.seeking = kDebugSeekNothing;
		g_sci->_debugState.runningStep = 0;
		break;

	case SCI_EVENT_KEYBOARD:
		writeSelectorValue(segMan, obj, SELECTOR(type), SCI_EVENT_KEYBOARD); // Keyboard event
		s->r_acc = make_reg(0, 1);

		writeSelectorValue(segMan, obj, SELECTOR(message), curEvent.character);
		// We only care about the translated character
		writeSelectorValue(segMan, obj, SELECTOR(modifiers), modifiers);
		break;

	case SCI_EVENT_MOUSE_RELEASE:
	case SCI_EVENT_MOUSE_PRESS:
		// track left buttton clicks, if requested
		if (curEvent.type == SCI_EVENT_MOUSE_PRESS && curEvent.modifiers == 0 && g_debug_track_mouse_clicks) {
			g_sci->getSciDebugger()->debugPrintf("Mouse clicked at %d, %d\n",
						mousePos.x, mousePos.y);
		}

		if (mask & curEvent.type) {
			writeSelectorValue(segMan, obj, SELECTOR(type), curEvent.type);
			writeSelectorValue(segMan, obj, SELECTOR(message), 0);
			writeSelectorValue(segMan, obj, SELECTOR(modifiers), modifiers);
			s->r_acc = make_reg(0, 1);
		}
		break;

	default:
		// Return a null event
		writeSelectorValue(segMan, obj, SELECTOR(type), SCI_EVENT_NONE);
		writeSelectorValue(segMan, obj, SELECTOR(message), 0);
		writeSelectorValue(segMan, obj, SELECTOR(modifiers), modifiers);
		s->r_acc = NULL_REG;
	}

	if ((s->r_acc.getOffset()) && (g_sci->_debugState.stopOnEvent)) {
		g_sci->_debugState.stopOnEvent = false;

		// A SCI event occurred, and we have been asked to stop, so open the debug console
		Console *con = g_sci->getSciDebugger();
		con->debugPrintf("SCI event occurred: ");
		switch (curEvent.type) {
		case SCI_EVENT_QUIT:
			con->debugPrintf("quit event\n");
			break;
		case SCI_EVENT_KEYBOARD:
			con->debugPrintf("keyboard event\n");
			break;
		case SCI_EVENT_MOUSE_RELEASE:
		case SCI_EVENT_MOUSE_PRESS:
			con->debugPrintf("mouse click event\n");
			break;
		default:
			con->debugPrintf("unknown or no event (event type %d)\n", curEvent.type);
		}

		con->attach();
		con->onFrame();
	}

	if (g_sci->_features->detectDoSoundType() <= SCI_VERSION_0_LATE) {
		// If we're running a sound-SCI0 game, update the sound cues, to
		// compensate for the fact that sound-SCI0 does not poll to update
		// the sound cues itself, like sound-SCI1 and later do with
		// cmdUpdateSoundCues. kGetEvent is called quite often, so emulate
		// the sound-SCI1 behavior of cmdUpdateSoundCues with this call
		g_sci->_soundCmd->updateSci0Cues();
	}

	// Wait a bit here, so that the CPU isn't maxed out when the game
	// is waiting for user input (e.g. when showing text boxes) - bug
	// #3037874. Make sure that we're not delaying while the game is
	// benchmarking, as that will affect the final benchmarked result -
	// check bugs #3058865 and #3127824
	if (s->_gameIsBenchmarking) {
		// Game is benchmarking, don't add a delay
	} else {
		g_system->delayMillis(10);
	}

	return s->r_acc;
}