/* ScummVM - Graphic Adventure Engine
 *
 * ScummVM is the legal property of its developers, whose names
 * are too numerous to list here. Please refer to the COPYRIGHT
 * file distributed with this source distribution.
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 *
 */

#include "sci/sci.h"
#include "sci/engine/kernel.h"
#include "sci/event.h"
#include "sci/resource.h"
#include "sci/engine/features.h"
#include "sci/engine/kernel_tables.h"
#include "sci/engine/state.h"
#include "sci/engine/workarounds.h"

#include "common/system.h"

namespace Sci {

Kernel::Kernel(ResourceManager *resMan, SegManager *segMan)
	: _resMan(resMan), _segMan(segMan), _invalid("<invalid>") {
}

Kernel::~Kernel() {
	for (KernelFunctionArray::iterator it = _kernelFuncs.begin(); it != _kernelFuncs.end(); ++it) {
		if (it->subFunctionCount) {
			uint16 subFunctionNr = 0;
			while (subFunctionNr < it->subFunctionCount) {
				delete[] it->subFunctions[subFunctionNr].signature;
				subFunctionNr++;
			}
			delete[] it->subFunctions;
		}
		delete[] it->signature;
	}
}

void Kernel::init() {
	loadSelectorNames();
	mapSelectors();      // Map a few special selectors for later use
}

uint Kernel::getSelectorNamesSize() const {
	return _selectorNames.size();
}

const Common::String &Kernel::getSelectorName(uint selector) {
	if (selector >= _selectorNames.size()) {
		// This should only occur in games w/o a selector-table
		//  We need this for proper workaround tables
		// TODO: maybe check, if there is a fixed selector-table and error() out in that case
		for (uint loopSelector = _selectorNames.size(); loopSelector <= selector; ++loopSelector)
			_selectorNames.push_back(Common::String::format("<noname%d>", loopSelector));
	}

	// Ensure that the selector has a name
	if (_selectorNames[selector].empty())
		_selectorNames[selector] = Common::String::format("<noname%d>", selector);

	return _selectorNames[selector];
}

uint Kernel::getKernelNamesSize() const {
	return _kernelNames.size();
}

const Common::String &Kernel::getKernelName(uint number) const {
	assert(number < _kernelFuncs.size());
	return _kernelNames[number];
}

Common::String Kernel::getKernelName(uint number, uint subFunction) const {
	assert(number < _kernelFuncs.size());
	const KernelFunction &kernelCall = _kernelFuncs[number];

	assert(subFunction < kernelCall.subFunctionCount);
	return kernelCall.subFunctions[subFunction].name;
}


int Kernel::findKernelFuncPos(Common::String kernelFuncName) {
	for (uint32 i = 0; i < _kernelNames.size(); i++)
		if (_kernelNames[i] == kernelFuncName)
			return i;

	return -1;
}

int Kernel::findSelector(const char *selectorName) const {
	for (uint pos = 0; pos < _selectorNames.size(); ++pos) {
		if (_selectorNames[pos] == selectorName)
			return pos;
	}

	debugC(kDebugLevelVM, "Could not map '%s' to any selector", selectorName);

	return -1;
}

// used by Script patcher to figure out, if it's okay to initialize signature/patch-table
bool Kernel::selectorNamesAvailable() {
	return !_selectorNames.empty();
}

void Kernel::loadSelectorNames() {
	Resource *r = _resMan->findResource(ResourceId(kResourceTypeVocab, VOCAB_RESOURCE_SELECTORS), 0);
	bool oldScriptHeader = (getSciVersion() == SCI_VERSION_0_EARLY);

#ifdef ENABLE_SCI32_MAC
	// Starting with KQ7, Mac versions have a BE name table. GK1 Mac and earlier (and all
	// other platforms) always use LE.
	const bool isBE = (g_sci->getPlatform() == Common::kPlatformMacintosh && getSciVersion() >= SCI_VERSION_2_1_EARLY
			&& g_sci->getGameId() != GID_GK1);
#else
	const bool isBE = false;
#endif

	if (!r) { // No such resource?
		// Check if we have a table for this game
		// Some demos do not have a selector table
		Common::StringArray staticSelectorTable = checkStaticSelectorNames();

		if (staticSelectorTable.empty())
			error("Kernel: Could not retrieve selector names");
		else
			warning("No selector vocabulary found, using a static one");

		for (uint32 i = 0; i < staticSelectorTable.size(); i++) {
			_selectorNames.push_back(staticSelectorTable[i]);
			if (oldScriptHeader)
				_selectorNames.push_back(staticSelectorTable[i]);
		}

		return;
	}

	int count = (isBE ? r->getUint16BEAt(0) : r->getUint16LEAt(0)) + 1; // Counter is slightly off

	for (int i = 0; i < count; i++) {
		int offset = isBE ? r->getUint16BEAt(2 + i * 2) : r->getUint16LEAt(2 + i * 2);
		int len = isBE ? r->getUint16BEAt(offset) : r->getUint16LEAt(offset);

		Common::String tmp = r->getStringAt(offset + 2, len);
		_selectorNames.push_back(tmp);
		//debug("%s", tmp.c_str());

		// Early SCI versions used the LSB in the selector ID as a read/write
		// toggle. To compensate for that, we add every selector name twice.
		if (oldScriptHeader)
			_selectorNames.push_back(tmp);
	}
}

// this parses a written kernel signature into an internal memory format
// [io] -> either integer or object
// (io) -> optionally integer AND an object
// (i) -> optional integer
// . -> any type
// i* -> at least one integer, more integers may follow after that
// (i*) -> optional multiple integers
// .* -> at least one parameter of any type and more parameters of any type may follow
// (.*) -> any parameters afterwards (or none)
// * -> means "more of the last parameter may follow (or none at all)", must be at the end of a signature. Is not valid anywhere else.
static uint16 *parseKernelSignature(const char *kernelName, const char *writtenSig) {
	const char *curPos;
	char curChar;
	uint16 *result = NULL;
	uint16 *writePos = NULL;
	int size = 0;
	bool validType = false;
	bool optionalType = false;
	bool eitherOr = false;
	bool optional = false;
	bool hadOptional = false;

	// No signature given? no signature out
	if (!writtenSig)
		return NULL;

	// First, we check how many bytes the result will be
	//  we also check, if the written signature makes any sense
	curPos = writtenSig;
	while (*curPos) {
		curChar = *curPos;
		switch (curChar) {
		case '[': // either or
			if (eitherOr)
				error("signature for k%s: '[' used within '[]'", kernelName);
			eitherOr = true;
			validType = false;
			break;
		case ']': // either or end
			if (!eitherOr)
				error("signature for k%s: ']' used without leading '['", kernelName);
			if (!validType)
				error("signature for k%s: '[]' does not surround valid type(s)", kernelName);
			eitherOr = false;
			validType = false;
			size++;
			break;
		case '(': // optional
			if (optional)
				error("signature for k%s: '(' used within '()' brackets", kernelName);
			if (eitherOr)
				error("signature for k%s: '(' used within '[]' brackets", kernelName);
			optional = true;
			validType = false;
			optionalType = false;
			break;
		case ')': // optional end
			if (!optional)
				error("signature for k%s: ')' used without leading '('", kernelName);
			if (!optionalType)
				error("signature for k%s: '()' does not to surround valid type(s)", kernelName);
			optional = false;
			validType = false;
			hadOptional = true;
			break;
		case '0': // allowed types
		case 'i':
		case 'o':
		case 'r':
		case 'l':
		case 'n':
		case '.':
		case '!':
			if ((hadOptional) & (!optional))
				error("signature for k%s: non-optional type may not follow optional type", kernelName);
			validType = true;
			if (optional)
				optionalType = true;
			if (!eitherOr)
				size++;
			break;
		case '*': // accepts more of the same parameter (must be last char)
			if (!validType) {
				if ((writtenSig == curPos) || (*(curPos - 1) != ']'))
					error("signature for k%s: a valid type must be in front of '*'", kernelName);
			}
			if (eitherOr)
				error("signature for k%s: '*' may not be inside '[]'", kernelName);
			if (optional) {
				if ((*(curPos + 1) != ')') || (*(curPos + 2) != 0))
					error("signature for k%s: '*' may only be used for last type", kernelName);
			} else {
				if (*(curPos + 1) != 0)
					error("signature for k%s: '*' may only be used for last type", kernelName);
			}
			break;
		default:
			error("signature for k%s: '%c' unknown", kernelName, *curPos);
		}
		curPos++;
	}

	uint16 signature = 0;

	// Now we allocate buffer with required size and fill it
	result = new uint16[size + 1];
	writePos = result;
	curPos = writtenSig;
	do {
		curChar = *curPos;
		if (!eitherOr) {
			// not within either-or, check if next character forces output
			switch (curChar) {
			case 0:
			case '[':
			case '(':
			case ')':
			case 'i':
			case 'o':
			case 'r':
			case 'l':
			case 'n':
			case '.':
			case '!':
				// and we also got some signature pending?
				if (signature) {
					if (!(signature & SIG_MAYBE_ANY))
						error("signature for k%s: invalid ('!') may only get used in combination with a real type", kernelName);
					if ((signature & SIG_IS_INVALID) && ((signature & SIG_MAYBE_ANY) == (SIG_TYPE_NULL | SIG_TYPE_INTEGER)))
						error("signature for k%s: invalid ('!') should not be used on exclusive null/integer type", kernelName);
					if (optional) {
						signature |= SIG_IS_OPTIONAL;
						if (curChar != ')')
							signature |= SIG_NEEDS_MORE;
					}
					*writePos = signature;
					writePos++;
					signature = 0;
				}
			}
		}
		switch (curChar) {
		case '[': // either or
			eitherOr = true;
			break;
		case ']': // either or end
			eitherOr = false;
			break;
		case '(': // optional
			optional = true;
			break;
		case ')': // optional end
			optional = false;
			break;
		case '0':
			if (signature & SIG_TYPE_NULL)
				error("signature for k%s: NULL ('0') specified more than once", kernelName);
			signature |= SIG_TYPE_NULL;
			break;
		case 'i':
			if (signature & SIG_TYPE_INTEGER)
				error("signature for k%s: integer ('i') specified more than once", kernelName);
			signature |= SIG_TYPE_INTEGER | SIG_TYPE_NULL;
			break;
		case 'o':
			if (signature & SIG_TYPE_OBJECT)
				error("signature for k%s: object ('o') specified more than once", kernelName);
			signature |= SIG_TYPE_OBJECT;
			break;
		case 'r':
			if (signature & SIG_TYPE_REFERENCE)
				error("signature for k%s: reference ('r') specified more than once", kernelName);
			signature |= SIG_TYPE_REFERENCE;
			break;
		case 'l':
			if (signature & SIG_TYPE_LIST)
				error("signature for k%s: list ('l') specified more than once", kernelName);
			signature |= SIG_TYPE_LIST;
			break;
		case 'n':
			if (signature & SIG_TYPE_NODE)
				error("signature for k%s: node ('n') specified more than once", kernelName);
			signature |= SIG_TYPE_NODE;
			break;
		case '.':
			if (signature & SIG_MAYBE_ANY)
				error("signature for k%s: maybe-any ('.') shouldn't get specified with other types in front of it", kernelName);
			signature |= SIG_MAYBE_ANY;
			break;
		case '!':
			if (signature & SIG_IS_INVALID)
				error("signature for k%s: invalid ('!') specified more than once", kernelName);
			signature |= SIG_IS_INVALID;
			break;
		case '*': // accepts more of the same parameter
			signature |= SIG_MORE_MAY_FOLLOW;
			break;
		default:
			break;
		}
		curPos++;
	} while (curChar);

	// Write terminator
	*writePos = 0;

	return result;
}

uint16 Kernel::findRegType(reg_t reg) {
	// No segment? Must be integer
	if (!reg.getSegment())
		return SIG_TYPE_INTEGER | (reg.getOffset() ? 0 : SIG_TYPE_NULL);

	if (reg.getSegment() == kUninitializedSegment)
		return SIG_TYPE_UNINITIALIZED;

	// Otherwise it's an object
	SegmentObj *mobj = _segMan->getSegmentObj(reg.getSegment());
	if (!mobj)
		return SIG_TYPE_ERROR;

	uint16 result = 0;
	if (!mobj->isValidOffset(reg.getOffset()))
		result |= SIG_IS_INVALID;

	switch (mobj->getType()) {
	case SEG_TYPE_SCRIPT:
		if (reg.getOffset() <= (*(Script *)mobj).getBufSize() &&
			reg.getOffset() >= (uint)-SCRIPT_OBJECT_MAGIC_OFFSET &&
			(*(Script *)mobj).offsetIsObject(reg.getOffset())) {
			result |= ((Script *)mobj)->getObject(reg.getOffset()) ? SIG_TYPE_OBJECT : SIG_TYPE_REFERENCE;
		} else
			result |= SIG_TYPE_REFERENCE;
		break;
	case SEG_TYPE_CLONES:
		result |= SIG_TYPE_OBJECT;
		break;
	case SEG_TYPE_LOCALS:
	case SEG_TYPE_STACK:
	case SEG_TYPE_DYNMEM:
	case SEG_TYPE_HUNK:
#ifdef ENABLE_SCI32
	case SEG_TYPE_ARRAY:
	case SEG_TYPE_BITMAP:
#endif
		result |= SIG_TYPE_REFERENCE;
		break;
	case SEG_TYPE_LISTS:
		result |= SIG_TYPE_LIST;
		break;
	case SEG_TYPE_NODES:
		result |= SIG_TYPE_NODE;
		break;
	default:
		return SIG_TYPE_ERROR;
	}
	return result;
}

struct SignatureDebugType {
	uint16 typeCheck;
	const char *text;
};

static const SignatureDebugType signatureDebugTypeList[] = {
	{ SIG_TYPE_NULL,          "null" },
	{ SIG_TYPE_INTEGER,       "integer" },
	{ SIG_TYPE_UNINITIALIZED, "uninitialized" },
	{ SIG_TYPE_OBJECT,        "object" },
	{ SIG_TYPE_REFERENCE,     "reference" },
	{ SIG_TYPE_LIST,          "list" },
	{ SIG_TYPE_NODE,          "node" },
	{ SIG_TYPE_ERROR,         "error" },
	{ SIG_IS_INVALID,         "invalid" },
	{ 0,                      NULL }
};

static void kernelSignatureDebugType(Common::String &signatureDetailsStr, const uint16 type) {
	bool firstPrint = true;

	const SignatureDebugType *list = signatureDebugTypeList;
	while (list->typeCheck) {
		if (type & list->typeCheck) {
			if (!firstPrint)
//				debugN(", ");
				signatureDetailsStr += ", ";
//			debugN("%s", list->text);
//			signatureDetailsStr += signatureDetailsStr.format("%s", list->text);
			signatureDetailsStr += list->text;
			firstPrint = false;
		}
		list++;
	}
}

// Create string, that holds the details of a kernel call signature and current arguments
//  For debugging purposes
void Kernel::signatureDebug(Common::String &signatureDetailsStr, const uint16 *sig, int argc, const reg_t *argv) {
	int argnr = 0;

	// add ERROR: to debug output
	debugN("ERROR:");

	while (*sig || argc) {
		// add leading spaces for additional parameters
		signatureDetailsStr += signatureDetailsStr.format("parameter %d: ", argnr++);
		if (argc) {
			reg_t parameter = *argv;
			signatureDetailsStr += signatureDetailsStr.format("%04x:%04x (", PRINT_REG(parameter));
			int regType = findRegType(parameter);
			if (regType)
				kernelSignatureDebugType(signatureDetailsStr, regType);
			else
				signatureDetailsStr += signatureDetailsStr.format("unknown type of %04x:%04x", PRINT_REG(parameter));
			signatureDetailsStr += ")";
			argv++;
			argc--;
		} else {
			signatureDetailsStr += "not passed";
		}
		if (*sig) {
			const uint16 signature = *sig;
			if ((signature & SIG_MAYBE_ANY) == SIG_MAYBE_ANY) {
				signatureDetailsStr += ", may be any";
			} else {
				signatureDetailsStr += ", should be ";
				kernelSignatureDebugType(signatureDetailsStr, signature);
			}
			if (signature & SIG_IS_OPTIONAL)
				signatureDetailsStr += " (optional)";
			if (signature & SIG_NEEDS_MORE)
				signatureDetailsStr += " (needs more)";
			if (signature & SIG_MORE_MAY_FOLLOW)
				signatureDetailsStr += " (more may follow)";
			sig++;
		}
		signatureDetailsStr += "\n";
	}
}

bool Kernel::signatureMatch(const uint16 *sig, int argc, const reg_t *argv) {
	uint16 nextSig = *sig;
	uint16 curSig = nextSig;
	while (nextSig && argc) {
		curSig = nextSig;
		int type = findRegType(*argv);

		if ((type & SIG_IS_INVALID) && (!(curSig & SIG_IS_INVALID)))
			return false; // pointer is invalid and signature doesn't allow that?

		if (!((type & ~SIG_IS_INVALID) & curSig)) {
			if ((type & ~SIG_IS_INVALID) == SIG_TYPE_ERROR && (curSig & SIG_IS_INVALID)) {
				// Type is unknown (error - usually because of a deallocated object or
				// stale pointer) and the signature allows invalid pointers. In this case,
				// ignore the invalid pointer.
			} else {
				return false; // type mismatch
			}
		}

		if (!(curSig & SIG_MORE_MAY_FOLLOW)) {
			sig++;
			nextSig = *sig;
		} else {
			nextSig |= SIG_IS_OPTIONAL; // more may follow -> assumes followers are optional
		}
		argv++;
		argc--;
	}

	// Too many arguments?
	if (argc)
		return false;
	// Signature end reached?
	if (nextSig == 0)
		return true;
	// current parameter is optional?
	if (curSig & SIG_IS_OPTIONAL) {
		// yes, check if nothing more is required
		if (!(curSig & SIG_NEEDS_MORE))
			return true;
	} else {
		// no, check if next parameter is optional
		if (nextSig & SIG_IS_OPTIONAL)
			return true;
	}
	// Too few arguments or more optional arguments required
	return false;
}

void Kernel::mapFunctions() {
	int mapped = 0;
	int ignored = 0;
	uint functionCount = _kernelNames.size();
	byte platformMask = 0;
	SciVersion myVersion = getSciVersion();

	switch (g_sci->getPlatform()) {
	case Common::kPlatformDOS:
	case Common::kPlatformFMTowns:
		platformMask = SIGFOR_DOS;
		break;
	case Common::kPlatformPC98:
		platformMask = SIGFOR_PC98;
		break;
	case Common::kPlatformWindows:
		platformMask = SIGFOR_WIN;
		break;
	case Common::kPlatformMacintosh:
		platformMask = SIGFOR_MAC;
		break;
	case Common::kPlatformAmiga:
		platformMask = SIGFOR_AMIGA;
		break;
	case Common::kPlatformAtariST:
		platformMask = SIGFOR_ATARI;
		break;
	default:
		break;
	}

	_kernelFuncs.resize(functionCount);

	for (uint id = 0; id < functionCount; id++) {
		// First, get the name, if known, of the kernel function with number functnr
		Common::String kernelName = _kernelNames[id];

		// Reset the table entry
		_kernelFuncs[id].function = NULL;
		_kernelFuncs[id].signature = NULL;
		_kernelFuncs[id].name = NULL;
		_kernelFuncs[id].workarounds = NULL;
		_kernelFuncs[id].subFunctions = NULL;
		_kernelFuncs[id].subFunctionCount = 0;
		if (kernelName.empty()) {
			// No name was given -> must be an unknown opcode
			warning("Kernel function %x unknown", id);
			continue;
		}

		// Don't map dummy functions - they will never be called
		if (kernelName == "Dummy") {
			_kernelFuncs[id].function = kDummy;
			continue;
		}

#ifdef ENABLE_SCI32_MAC
		// HACK: Phantasmagoria Mac uses a modified kDoSound (which *nothing*
		// else seems to use)!
		if (g_sci->getPlatform() == Common::kPlatformMacintosh && g_sci->getGameId() == GID_PHANTASMAGORIA && kernelName == "DoSound") {
			_kernelFuncs[id].function = kDoSoundPhantasmagoriaMac;
			_kernelFuncs[id].signature = parseKernelSignature("DoSoundPhantasmagoriaMac", "i.*");
			_kernelFuncs[id].name = "DoSoundPhantasmagoriaMac";
			continue;
		}
#endif

		// If the name is known, look it up in s_kernelMap. This table
		// maps kernel func names to actual function (pointers).
		SciKernelMapEntry *kernelMap = s_kernelMap;
		bool nameMatch = false;
		while (kernelMap->name) {
			if (kernelName == kernelMap->name) {
				if ((kernelMap->fromVersion == SCI_VERSION_NONE) || (kernelMap->fromVersion <= myVersion))
					if ((kernelMap->toVersion == SCI_VERSION_NONE) || (kernelMap->toVersion >= myVersion))
						if (platformMask & kernelMap->forPlatform)
							break;
				nameMatch = true;
			}
			kernelMap++;
		}

		if (kernelMap->name) {
			// A match was found
			_kernelFuncs[id].function = kernelMap->function;
			_kernelFuncs[id].name = kernelMap->name;
			_kernelFuncs[id].signature = parseKernelSignature(kernelMap->name, kernelMap->signature);
			_kernelFuncs[id].workarounds = kernelMap->workarounds;
			if (kernelMap->subFunctions) {
				// Get version for subfunction identification
				SciVersion mySubVersion = (SciVersion)kernelMap->function(NULL, 0, NULL).getOffset();
				// Now check whats the highest subfunction-id for this version
				const SciKernelMapSubEntry *kernelSubMap = kernelMap->subFunctions;
				uint16 subFunctionCount = 0;
				while (kernelSubMap->function) {
					if ((kernelSubMap->fromVersion == SCI_VERSION_NONE) || (kernelSubMap->fromVersion <= mySubVersion))
						if ((kernelSubMap->toVersion == SCI_VERSION_NONE) || (kernelSubMap->toVersion >= mySubVersion))
							if (subFunctionCount <= kernelSubMap->id)
								subFunctionCount = kernelSubMap->id + 1;
					kernelSubMap++;
				}
				if (!subFunctionCount)
					error("k%s[%x]: no subfunctions found for requested version %s", kernelName.c_str(), id, getSciVersionDesc(mySubVersion));
				// Now allocate required memory and go through it again
				_kernelFuncs[id].subFunctionCount = subFunctionCount;
				KernelSubFunction *subFunctions = new KernelSubFunction[subFunctionCount];
				_kernelFuncs[id].subFunctions = subFunctions;
				memset(subFunctions, 0, sizeof(KernelSubFunction) * subFunctionCount);
				// And fill this info out
				kernelSubMap = kernelMap->subFunctions;
				uint kernelSubNr = 0;
				while (kernelSubMap->function) {
					if ((kernelSubMap->fromVersion == SCI_VERSION_NONE) || (kernelSubMap->fromVersion <= mySubVersion))
						if ((kernelSubMap->toVersion == SCI_VERSION_NONE) || (kernelSubMap->toVersion >= mySubVersion)) {
							uint subId = kernelSubMap->id;
							if (!subFunctions[subId].function) {
								subFunctions[subId].function = kernelSubMap->function;
								subFunctions[subId].name = kernelSubMap->name;
								subFunctions[subId].workarounds = kernelSubMap->workarounds;
								if (kernelSubMap->signature) {
									subFunctions[subId].signature = parseKernelSignature(kernelSubMap->name, kernelSubMap->signature);
								} else {
									// we go back the submap to find the previous signature for that kernel call
									const SciKernelMapSubEntry *kernelSubMapBack = kernelSubMap;
									uint kernelSubLeft = kernelSubNr;
									while (kernelSubLeft) {
										kernelSubLeft--;
										kernelSubMapBack--;
										if (kernelSubMapBack->name == kernelSubMap->name) {
											if (kernelSubMapBack->signature) {
												subFunctions[subId].signature = parseKernelSignature(kernelSubMap->name, kernelSubMapBack->signature);
												break;
											}
										}
									}
									if (!subFunctions[subId].signature)
										error("k%s: no previous signatures", kernelSubMap->name);
								}
							}
						}
					kernelSubMap++;
					kernelSubNr++;
				}
			}
			++mapped;
		} else {
			if (nameMatch)
				error("k%s[%x]: not found for this version/platform", kernelName.c_str(), id);
			// No match but a name was given -> stub
			warning("k%s[%x]: unmapped", kernelName.c_str(), id);
			_kernelFuncs[id].function = kStub;
		}
	} // for all functions requesting to be mapped

	debugC(kDebugLevelVM, "Handled %d/%d kernel functions, mapping %d and ignoring %d.",
				mapped + ignored, _kernelNames.size(), mapped, ignored);

	return;
}

#ifdef ENABLE_SCI32
enum {
	kKernelEntriesSci2 = 0x8b,
	kKernelEntriesGk2Demo = 0xa0,
	kKernelEntriesSci21 = 0x9d,
	kKernelEntriesSci3 = 0xa2
};
#endif

void Kernel::loadKernelNames(GameFeatures *features) {
	_kernelNames.clear();

	if (getSciVersion() <= SCI_VERSION_1_1) {
		_kernelNames = Common::StringArray(s_defaultKernelNames, ARRAYSIZE(s_defaultKernelNames));

		// Some (later) SCI versions replaced CanBeHere by CantBeHere
		// If vocab.999 exists, the kernel function is still named CanBeHere
		if (_selectorCache.cantBeHere != -1)
			_kernelNames[0x4d] = "CantBeHere";
	}

	switch (getSciVersion()) {
	case SCI_VERSION_0_EARLY:
	case SCI_VERSION_0_LATE:
		// Insert SCI0 file functions after SetCursor (0x28)
		_kernelNames.insert_at(0x29, "FOpen");
		_kernelNames.insert_at(0x2A, "FPuts");
		_kernelNames.insert_at(0x2B, "FGets");
		_kernelNames.insert_at(0x2C, "FClose");

		// Function 0x55 is DoAvoider
		_kernelNames[0x55] = "DoAvoider";

		// Cut off unused functions
		_kernelNames.resize(0x72);
		break;

	case SCI_VERSION_01:
		// Multilingual SCI01 games have StrSplit as function 0x78
		_kernelNames[0x78] = "StrSplit";

		// Cut off unused functions
		_kernelNames.resize(0x79);
		break;

	case SCI_VERSION_1_LATE:
		_kernelNames[0x71] = "MoveCursor";
		break;

	case SCI_VERSION_1_1:
		// In SCI1.1, kSetSynonyms is an empty function
		_kernelNames[0x26] = "Empty";

		if (g_sci->getGameId() == GID_KQ6) {
			// In the Windows version of KQ6 CD, the empty kSetSynonyms
			// function has been replaced with kPortrait. In KQ6 Mac,
			// kPlayBack has been replaced by kShowMovie.
			if ((g_sci->getPlatform() == Common::kPlatformWindows) || (g_sci->forceHiresGraphics()))
				_kernelNames[0x26] = "Portrait";
			else if (g_sci->getPlatform() == Common::kPlatformMacintosh)
				_kernelNames[0x84] = "ShowMovie";
		} else if (g_sci->getGameId() == GID_QFG4DEMO) {
			_kernelNames[0x7b] = "RemapColors"; // QFG4 Demo has this SCI2 function instead of StrSplit
		}

		_kernelNames[0x71] = "PalVary";

		// At least EcoQuest 1 demo uses kGetMessage instead of kMessage.
		// Detect which function to use.
		if (features->detectMessageFunctionType() == SCI_VERSION_1_1)
			_kernelNames[0x7c] = "Message";
		break;

#ifdef ENABLE_SCI32
	case SCI_VERSION_2:
		_kernelNames = Common::StringArray(sci2_default_knames, kKernelEntriesSci2);
		break;

	case SCI_VERSION_2_1_EARLY:
	case SCI_VERSION_2_1_MIDDLE:
	case SCI_VERSION_2_1_LATE:
		if (features->detectSci21KernelType() == SCI_VERSION_2) {
			// Some early SCI2.1 games use a modified SCI2 kernel table instead of
			// the SCI2.1 kernel table. We detect which version to use based on
			// how kDoSound is called from Sound::play().
			// Known games that use this:
			// GK2 demo
			// KQ7 1.4/1.51
			// PQ:SWAT demo
			// LSL6
			// PQ4CD
			// QFG4CD

			// This is interesting because they all have the same interpreter
			// version (2.100.002), yet they would not be compatible with other
			// games of the same interpreter.

			_kernelNames = Common::StringArray(sci2_default_knames, kKernelEntriesGk2Demo);
			// OnMe is IsOnMe here, but they should be compatible
			_kernelNames[0x23] = g_sci->getGameId() == GID_LSL6HIRES ? "Empty" : "Robot"; // Graph in SCI2
			_kernelNames[0x2e] = "Priority"; // DisposeTextBitmap in SCI2
		} else {
			// Normal SCI2.1 kernel table
			_kernelNames = Common::StringArray(sci21_default_knames, kKernelEntriesSci21);
		}
		break;

	case SCI_VERSION_3:
		_kernelNames = Common::StringArray(sci21_default_knames, kKernelEntriesSci3);

		// In SCI3, some kernel functions have been removed, and others have been added
		_kernelNames[0x18] = "Dummy";	// AddMagnify in SCI2.1
		_kernelNames[0x19] = "Dummy";	// DeleteMagnify in SCI2.1
		_kernelNames[0x30] = "Dummy";	// SetScroll in SCI2.1
		_kernelNames[0x39] = "Dummy";	// ShowMovie in SCI2.1
		_kernelNames[0x4c] = "Dummy";	// ScrollWindow in SCI2.1
		_kernelNames[0x56] = "Dummy";	// VibrateMouse in SCI2.1 (only used in QFG4 floppy)
		_kernelNames[0x66] = "Dummy";	// MergePoly in SCI2.1
		_kernelNames[0x8d] = "MessageBox";	// Dummy in SCI2.1
		_kernelNames[0x9b] = "Minimize";	// Dummy in SCI2.1

		break;
#endif

	default:
		// Use default table for the other versions
		break;
	}

#ifdef ENABLE_SCI32
	// Reserve a high range of kernel call IDs (0xe0 to 0xef) that can be used
	// by ScummVM to improve integration and fix bugs in games that require
	// more help than can be provided by a simple script patch (e.g. spinloops
	// in Hoyle5).
	// Using a new high range instead of just replacing dummied kernel calls in
	// the normal kernel range is intended to avoid any conflicts with fangames
	// that might try to add their own kernel calls in the same manner. It also
	// helps to separate ScummVM interpreter's kernel calls from SSCI's standard
	// kernel calls.
	if (getSciVersion() >= SCI_VERSION_2) {
		const uint kernelListSize = _kernelNames.size();
		_kernelNames.resize(0xe2);
		for (uint id = kernelListSize; id < 0xe0; ++id) {
			_kernelNames[id] = "Dummy";
		}

		// Used by Hoyle5 script patches to remove CPU spinning on kGetTime
		// (this repurposes the existing SCI16 kWait call that was removed in SCI32)
		_kernelNames[kScummVMWaitId] = "Wait";

		// Used by GuestAdditions to support integrated save/load dialogue
		_kernelNames[kScummVMSaveLoadId] = "ScummVMSaveLoad";
	}
#endif

	mapFunctions();
}

Common::String Kernel::lookupText(reg_t address, int index) {
	if (address.getSegment())
		return _segMan->getString(address);

	Resource *textres = _resMan->findResource(ResourceId(kResourceTypeText, address.getOffset()), false);

	if (!textres) {
		error("text.%03d not found", address.getOffset());
	}

	int textlen = textres->size();
	const char *seeker = (const char *)textres->getUnsafeDataAt(0);

	int _index = index;
	while (index--)
		while (textlen-- && *seeker++)
			;

	if (textlen)
		return seeker;

	error("Index %d out of bounds in text.%03d", _index, address.getOffset());
}

// TODO: script_adjust_opcode_formats should probably be part of the
// constructor (?) of a VirtualMachine or a ScriptManager class.
void script_adjust_opcode_formats() {

	g_sci->_opcode_formats = new opcode_format[128][4];
	memcpy(g_sci->_opcode_formats, g_base_opcode_formats, 128*4*sizeof(opcode_format));

	if (g_sci->_features->detectLofsType() != SCI_VERSION_0_EARLY) {
		g_sci->_opcode_formats[op_lofsa][0] = Script_Offset;
		g_sci->_opcode_formats[op_lofss][0] = Script_Offset;
	}

#ifdef ENABLE_SCI32
	// In SCI32, some arguments are now words instead of bytes
	if (getSciVersion() >= SCI_VERSION_2) {
		g_sci->_opcode_formats[op_calle][2] = Script_Word;
		g_sci->_opcode_formats[op_callk][1] = Script_Word;
		g_sci->_opcode_formats[op_super][1] = Script_Word;
		g_sci->_opcode_formats[op_send][0] = Script_Word;
		g_sci->_opcode_formats[op_self][0] = Script_Word;
		g_sci->_opcode_formats[op_call][1] = Script_Word;
		g_sci->_opcode_formats[op_callb][1] = Script_Word;
	}

	if (getSciVersion() >= SCI_VERSION_3) {
		g_sci->_opcode_formats[op_info][0] = Script_None;
		g_sci->_opcode_formats[op_superP][0] = Script_None;
	}
#endif
}

} // End of namespace Sci
