(svn r13193) [NoAI] -Add: allow AIs to be packed in a .tar. one AI per tar, always in a subdir. It looks for main.nut in the first dir it finds in the archive noai
authortruebrain
Tue, 20 May 2008 13:09:34 +0000
branchnoai
changeset 10649 9034b80fdbdb
parent 10648 1ebb27f31a71
child 10650 30fc5395b1b8
(svn r13193) [NoAI] -Add: allow AIs to be packed in a .tar. one AI per tar, always in a subdir. It looks for main.nut in the first dir it finds in the archive
[NoAI] -Add: search for AIs in all searchpaths of OpenTTD
[NoAI] -Fix: only do the initial search once at start-up, not at some random times over time
[NoAI] -Fix: don't compile the sqstdlib files that are never used; this to speed up compilation time
[NoAI] -Fix: *grrmmmm* (very silent talking) forgot svn-props and stuff on ai_info.[ch]pp
bin/ai/regression/regression.nut
bin/ai/regression/regression.txt
bin/ai/regression/require.nut
projects/openttd_vs80.vcproj
projects/openttd_vs90.vcproj
source.list
src/ai/ai.cpp
src/ai/ai_info.cpp
src/ai/ai_info.hpp
src/ai/ai_squirrel.cpp
src/ai/ai_squirrel.hpp
src/fileio.cpp
src/fileio.h
src/misc.cpp
src/openttd.cpp
src/squirrel.cpp
src/squirrel.hpp
src/squirrel_std.cpp
--- a/bin/ai/regression/regression.nut	Mon May 19 22:28:27 2008 +0000
+++ b/bin/ai/regression/regression.nut	Tue May 20 13:09:34 2008 +0000
@@ -15,6 +15,7 @@
 	this.Sleep(1);
 	print(" TickTest: " + this.GetTick());
 	print(" SetCommandDelay: " + AISettings.SetCommandDelay(1));
+	require("require.nut");
 }
 
 function Regression::Std()
--- a/bin/ai/regression/regression.txt	Mon May 19 22:28:27 2008 +0000
+++ b/bin/ai/regression/regression.txt	Tue May 20 13:09:34 2008 +0000
@@ -3,6 +3,7 @@
  TickTest: 0
  TickTest: 1
  SetCommandDelay: (null : 0x00000000)
+  Required this file
 
 --Std--
  abs(-21): 21
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/bin/ai/regression/require.nut	Tue May 20 13:09:34 2008 +0000
@@ -0,0 +1,3 @@
+
+print("  Required this file");
+
--- a/projects/openttd_vs80.vcproj	Mon May 19 22:28:27 2008 +0000
+++ b/projects/openttd_vs80.vcproj	Tue May 20 13:09:34 2008 +0000
@@ -2079,34 +2079,6 @@
 				RelativePath=".\..\src\3rdparty\squirrel\sqstdlib\sqstdaux.cpp"
 				>
 			</File>
-			<File
-				RelativePath=".\..\src\3rdparty\squirrel\sqstdlib\sqstdblob.cpp"
-				>
-			</File>
-			<File
-				RelativePath=".\..\src\3rdparty\squirrel\sqstdlib\sqstdio.cpp"
-				>
-			</File>
-			<File
-				RelativePath=".\..\src\3rdparty\squirrel\sqstdlib\sqstdmath.cpp"
-				>
-			</File>
-			<File
-				RelativePath=".\..\src\3rdparty\squirrel\sqstdlib\sqstdrex.cpp"
-				>
-			</File>
-			<File
-				RelativePath=".\..\src\3rdparty\squirrel\sqstdlib\sqstdstream.cpp"
-				>
-			</File>
-			<File
-				RelativePath=".\..\src\3rdparty\squirrel\sqstdlib\sqstdstring.cpp"
-				>
-			</File>
-			<File
-				RelativePath=".\..\src\3rdparty\squirrel\sqstdlib\sqstdsystem.cpp"
-				>
-			</File>
 		</Filter>
 		<Filter
 			Name="Squirrel headers"
@@ -2132,34 +2104,6 @@
 				>
 			</File>
 			<File
-				RelativePath=".\..\src\3rdparty\squirrel\include\sqstdblob.h"
-				>
-			</File>
-			<File
-				RelativePath=".\..\src\3rdparty\squirrel\sqstdlib\sqstdblobimpl.h"
-				>
-			</File>
-			<File
-				RelativePath=".\..\src\3rdparty\squirrel\include\sqstdio.h"
-				>
-			</File>
-			<File
-				RelativePath=".\..\src\3rdparty\squirrel\include\sqstdmath.h"
-				>
-			</File>
-			<File
-				RelativePath=".\..\src\3rdparty\squirrel\sqstdlib\sqstdstream.h"
-				>
-			</File>
-			<File
-				RelativePath=".\..\src\3rdparty\squirrel\include\sqstdstring.h"
-				>
-			</File>
-			<File
-				RelativePath=".\..\src\3rdparty\squirrel\include\sqstdsystem.h"
-				>
-			</File>
-			<File
 				RelativePath=".\..\src\3rdparty\squirrel\squirrel\sqfuncproto.h"
 				>
 			</File>
--- a/projects/openttd_vs90.vcproj	Mon May 19 22:28:27 2008 +0000
+++ b/projects/openttd_vs90.vcproj	Tue May 20 13:09:34 2008 +0000
@@ -2076,34 +2076,6 @@
 				RelativePath=".\..\src\3rdparty\squirrel\sqstdlib\sqstdaux.cpp"
 				>
 			</File>
-			<File
-				RelativePath=".\..\src\3rdparty\squirrel\sqstdlib\sqstdblob.cpp"
-				>
-			</File>
-			<File
-				RelativePath=".\..\src\3rdparty\squirrel\sqstdlib\sqstdio.cpp"
-				>
-			</File>
-			<File
-				RelativePath=".\..\src\3rdparty\squirrel\sqstdlib\sqstdmath.cpp"
-				>
-			</File>
-			<File
-				RelativePath=".\..\src\3rdparty\squirrel\sqstdlib\sqstdrex.cpp"
-				>
-			</File>
-			<File
-				RelativePath=".\..\src\3rdparty\squirrel\sqstdlib\sqstdstream.cpp"
-				>
-			</File>
-			<File
-				RelativePath=".\..\src\3rdparty\squirrel\sqstdlib\sqstdstring.cpp"
-				>
-			</File>
-			<File
-				RelativePath=".\..\src\3rdparty\squirrel\sqstdlib\sqstdsystem.cpp"
-				>
-			</File>
 		</Filter>
 		<Filter
 			Name="Squirrel headers"
@@ -2129,34 +2101,6 @@
 				>
 			</File>
 			<File
-				RelativePath=".\..\src\3rdparty\squirrel\include\sqstdblob.h"
-				>
-			</File>
-			<File
-				RelativePath=".\..\src\3rdparty\squirrel\sqstdlib\sqstdblobimpl.h"
-				>
-			</File>
-			<File
-				RelativePath=".\..\src\3rdparty\squirrel\include\sqstdio.h"
-				>
-			</File>
-			<File
-				RelativePath=".\..\src\3rdparty\squirrel\include\sqstdmath.h"
-				>
-			</File>
-			<File
-				RelativePath=".\..\src\3rdparty\squirrel\sqstdlib\sqstdstream.h"
-				>
-			</File>
-			<File
-				RelativePath=".\..\src\3rdparty\squirrel\include\sqstdstring.h"
-				>
-			</File>
-			<File
-				RelativePath=".\..\src\3rdparty\squirrel\include\sqstdsystem.h"
-				>
-			</File>
-			<File
 				RelativePath=".\..\src\3rdparty\squirrel\squirrel\sqfuncproto.h"
 				>
 			</File>
--- a/source.list	Mon May 19 22:28:27 2008 +0000
+++ b/source.list	Tue May 20 13:09:34 2008 +0000
@@ -463,13 +463,6 @@
 3rdparty/squirrel/squirrel/sqtable.cpp
 3rdparty/squirrel/squirrel/sqvm.cpp
 3rdparty/squirrel/sqstdlib/sqstdaux.cpp
-3rdparty/squirrel/sqstdlib/sqstdblob.cpp
-3rdparty/squirrel/sqstdlib/sqstdio.cpp
-3rdparty/squirrel/sqstdlib/sqstdmath.cpp
-3rdparty/squirrel/sqstdlib/sqstdrex.cpp
-3rdparty/squirrel/sqstdlib/sqstdstream.cpp
-3rdparty/squirrel/sqstdlib/sqstdstring.cpp
-3rdparty/squirrel/sqstdlib/sqstdsystem.cpp
 
 # Squirrel headers
 3rdparty/squirrel/squirrel/sqarray.h
@@ -477,13 +470,6 @@
 3rdparty/squirrel/squirrel/sqclosure.h
 3rdparty/squirrel/squirrel/sqcompiler.h
 3rdparty/squirrel/include/sqstdaux.h
-3rdparty/squirrel/include/sqstdblob.h
-3rdparty/squirrel/sqstdlib/sqstdblobimpl.h
-3rdparty/squirrel/include/sqstdio.h
-3rdparty/squirrel/include/sqstdmath.h
-3rdparty/squirrel/sqstdlib/sqstdstream.h
-3rdparty/squirrel/include/sqstdstring.h
-3rdparty/squirrel/include/sqstdsystem.h
 3rdparty/squirrel/squirrel/sqfuncproto.h
 3rdparty/squirrel/squirrel/sqfuncstate.h
 3rdparty/squirrel/squirrel/sqlexer.h
--- a/src/ai/ai.cpp	Mon May 19 22:28:27 2008 +0000
+++ b/src/ai/ai.cpp	Tue May 20 13:09:34 2008 +0000
@@ -29,7 +29,7 @@
 
 static AIController *_ai_player[MAX_PLAYERS];
 static AIInfo *_ai_info[MAX_PLAYERS];
-static AISquirrel *_ai_squirrel;
+static AISquirrel *_ai_squirrel = NULL;
 static uint _ai_frame_counter;
 static bool _ai_enabled;
 static char *_forced_ai_name = NULL;
@@ -143,6 +143,7 @@
 	_ai_enabled = true;
 
 	/* Create the Squirrel collector, which scans for all AIs available */
+	assert(_ai_squirrel == NULL);
 	_ai_squirrel = new AISquirrel();
 }
 
@@ -179,6 +180,7 @@
 	}
 
 	delete _ai_squirrel;
+	_ai_squirrel = NULL;
 }
 
 /**
--- a/src/ai/ai_info.cpp	Mon May 19 22:28:27 2008 +0000
+++ b/src/ai/ai_info.cpp	Tue May 20 13:09:34 2008 +0000
@@ -1,3 +1,7 @@
+/* $Id$ */
+
+/** @file ai_info.cpp Implementation of AIInfo */
+
 #include "../stdafx.h"
 #include "../core/alloc_func.hpp"
 
@@ -59,6 +63,11 @@
 	return this->dir_name;
 }
 
+const char *AIInfo::GetScriptName()
+{
+	return this->script_name;
+}
+
 void AIInfo::CheckMethods(SQInteger *res, const char *name)
 {
 	if (!this->engine->MethodExists(*this->SQ_instance, name)) {
--- a/src/ai/ai_info.hpp	Mon May 19 22:28:27 2008 +0000
+++ b/src/ai/ai_info.hpp	Tue May 20 13:09:34 2008 +0000
@@ -1,3 +1,7 @@
+/* $Id$ */
+
+/** @file ai_info.hpp AIInfo keeps track of all information of an AI, like Author, Description, ... */
+
 #ifndef AI_INFO
 #define AI_INFO
 
@@ -46,6 +50,11 @@
 	const char *GetDirName();
 
 	/**
+	 * Get the complete script name of this AI.
+	 */
+	const char *GetScriptName();
+
+	/**
 	 * Check if a given method exists.
 	 */
 	void CheckMethods(SQInteger *res, const char *name);
--- a/src/ai/ai_squirrel.cpp	Mon May 19 22:28:27 2008 +0000
+++ b/src/ai/ai_squirrel.cpp	Tue May 20 13:09:34 2008 +0000
@@ -36,23 +36,41 @@
 	while ((dirent = readdir(dir)) != NULL) {
 		ttd_strlcpy(d_name, FS2OTTD(dirent->d_name), sizeof(d_name));
 
-		/* Found file must be directory, but not '.' or '..' */
-		if (FiosIsValidFile("ai/", dirent, &sb) && (sb.st_mode & S_IFDIR) &&
-				(!FiosIsHiddenFile(dirent) || strncasecmp(d_name, PERSONAL_DIR, strlen(d_name)) == 0) &&
-				strcmp(d_name, ".") != 0 && strcmp(d_name, "..") != 0) {
+		/* Valid file, not '.' or '..', not hidden */
+		if (!FiosIsValidFile(dirname, dirent, &sb)) continue;
+		if (strcmp(d_name, ".") == 0 || strcmp(d_name, "..") == 0) continue;
+		if (FiosIsHiddenFile(dirent) && strncasecmp(d_name, PERSONAL_DIR, strlen(d_name)) != 0) continue;
+
+		if (sb.st_mode & S_IFDIR) {
 			/* Create the full-length script-name */
 			ttd_strlcpy(script_name, dirname, sizeof(script_name));
-			ttd_strlcat(script_name, PATHSEP, sizeof(script_name));
 			ttd_strlcat(script_name, d_name, sizeof(script_name));
 			ttd_strlcat(script_name, PATHSEP, sizeof(script_name));
-			ttd_strlcat(script_name, "main.nut", sizeof(script_name));
-			/* If it exists, load it up */
-			if (FileExists(script_name)) {
-				DEBUG(ai, 6, "[squirrel] Loading script '%s' for AI handling", script_name);
-				this->current_script = script_name;
-				this->current_dir = d_name;
-				this->engine->LoadScript(this->current_script);
-			}
+		}
+		if (sb.st_mode & S_IFREG) {
+			char *ext = strrchr(d_name, '.');
+			if (ext != NULL && strcasecmp(ext, ".tar") != 0) continue;
+
+			/* Create the full path to the tarfile */
+			char tarname[MAX_PATH];
+			ttd_strlcpy(tarname, dirname, sizeof(tarname));
+			ttd_strlcat(tarname, d_name, sizeof(tarname));
+
+			/* Now the script-name starts with the first dir in the tar */
+			assert(FioTarFirstDir(tarname) != NULL);
+			ttd_strlcpy(script_name, FioTarFirstDir(tarname), sizeof(script_name));
+
+			/* The name of the AI is the name of the tar minus the .tar */
+			*ext = '\0';
+		}
+
+		/* We look for the file 'main.nut' inside the AI dir.. if it doesn't exists, it isn't an AI */
+		ttd_strlcat(script_name, "main.nut", sizeof(script_name));
+		if (FioCheckFileExists(script_name, AI_DIR)) {
+			DEBUG(ai, 6, "[squirrel] Loading script '%s' for AI handling", script_name);
+			this->current_script = script_name;
+			this->current_dir = d_name;
+			this->engine->LoadScript(this->current_script);
 		}
 	}
 	closedir(dir);
@@ -75,7 +93,13 @@
 	this->engine->SetGlobalPointer(this);
 
 	/* Scan the AI dir for scripts */
-	this->ScanDir("ai");
+	char buf[MAX_PATH];
+	Searchpath sp;
+
+	FOR_ALL_SEARCHPATHS(sp) {
+		FioAppendDirectory(buf, MAX_PATH, sp, AI_DIR);
+		if (FileExists(buf)) this->ScanDir(buf);
+	}
 }
 
 AISquirrel::~AISquirrel()
@@ -85,7 +109,21 @@
 
 void AISquirrel::RegisterAI(AIInfo *info)
 {
-	this->info_list.insert(AIInfoList::value_type(info->GetDirName(), info));
+	const char *ai_name = info->GetDirName();
+
+	/* Check if we register twice; than the latest always wins */
+	if (this->info_list.find(ai_name) != this->info_list.end()) {
+		/* In case they are not the same dir, give a warning */
+		if (strcasecmp(info->GetScriptName(), this->info_list[ai_name]->GetScriptName()) != 0) {
+			DEBUG(ai, 0, "Registering two AIs with the same name");
+			DEBUG(ai, 0, "  1: %s", this->info_list[ai_name]->GetScriptName());
+			DEBUG(ai, 0, "  2: %s", info->GetScriptName());
+			DEBUG(ai, 0, "The latter is taking precedence");
+		}
+		this->info_list[ai_name] = info;
+		return;
+	}
+	this->info_list[ai_name] = info;
 }
 
 void AISquirrel::UnregisterAI(AIInfo *info)
--- a/src/ai/ai_squirrel.hpp	Mon May 19 22:28:27 2008 +0000
+++ b/src/ai/ai_squirrel.hpp	Tue May 20 13:09:34 2008 +0000
@@ -6,6 +6,7 @@
 #define AI_SQUIRREL_HPP
 
 #include <map>
+struct ltstr { bool operator()(const char *s1, const char *s2) const { return strcmp(s1, s2) < 0; } };
 
 class AISquirrel {
 public:
@@ -53,7 +54,7 @@
 	const char *GetCurrentDirName() { return this->current_dir; }
 
 private:
-	typedef std::map<const char *, class AIInfo *> AIInfoList;
+	typedef std::map<const char *, class AIInfo *, ltstr> AIInfoList;
 
 	/**
 	 * Scan a dir for AIs. If found, AIInfo is created, and the AI is registered to the central system.
--- a/src/fileio.cpp	Mon May 19 22:28:27 2008 +0000
+++ b/src/fileio.cpp	Tue May 20 13:09:34 2008 +0000
@@ -215,7 +215,8 @@
 	"scenario" PATHSEP "heightmap" PATHSEP,
 	"gm" PATHSEP,
 	"data" PATHSEP,
-	"lang" PATHSEP
+	"lang" PATHSEP,
+	"ai" PATHSEP,
 };
 
 const char *_searchpaths[NUM_SEARCHPATHS];
@@ -355,7 +356,7 @@
 	}
 
 	/* We can only use .tar in case of data-dir, and read-mode */
-	if (f == NULL && subdir == DATA_DIR && mode[0] == 'r') {
+	if (f == NULL && (subdir == DATA_DIR || subdir == AI_DIR) && mode[0] == 'r') {
 		/* Filenames in tars are always forced to be lowercase */
 		char *lcfilename = strdup(filename);
 		strtolower(lcfilename);
@@ -442,6 +443,13 @@
 	return dest;
 }
 
+const char *FioTarFirstDir(const char *tarname)
+{
+	TarList::iterator it = _tar_list.find(tarname);
+	if (it == _tar_list.end()) return NULL;
+	return (*it).second->dirname;
+}
+
 static bool TarListAddFile(const char *filename)
 {
 	/* The TAR-header, repeated for every file */
@@ -475,6 +483,7 @@
 
 	TarListEntry *tar_entry = MallocT<TarListEntry>(1);
 	tar_entry->filename = strdup(filename);
+	tar_entry->dirname = NULL;
 	_tar_list.insert(TarList::value_type(filename, tar_entry));
 
 	TarHeader th;
@@ -513,12 +522,21 @@
 		/* Copy the name of the file in a safe way at the end of 'name' */
 		memcpy(&name[len], th.name, sizeof(th.name));
 		name[len + sizeof(th.name)] = '\0';
+		/* Tar-files always have '/' path-seperator, but we want our PATHSEPCHAR */
+#if (PATHSEPCHAR != '/')
+		for (char *n = name; *n != '\0'; n++) if (*n == '/') *n = PATHSEPCHAR;
+#endif
+		/* Force lowercase */
+		strtolower(name);
 
 		/* Calculate the size of the file.. for some strange reason this is stored as a string */
 		memcpy(buf, th.size, sizeof(th.size));
 		buf[sizeof(th.size)] = '\0';
 		int skip = strtol(buf, &end, 8);
 
+		/* Store the first directory name we detect */
+		if (skip == 0 && tar_entry->dirname == NULL) tar_entry->dirname = strdup(name);
+
 		/* 0 byte sized files can be skipped (dirs, symlinks, ..) */
 		if (skip == 0) continue;
 
@@ -527,13 +545,6 @@
 		entry.tar      = tar_entry;
 		entry.size     = skip;
 		entry.position = pos;
-		/* Force lowercase */
-		strtolower(name);
-
-		/* Tar-files always have '/' path-seperator, but we want our PATHSEPCHAR */
-#if (PATHSEPCHAR != '/')
-		for (char *n = name; *n != '\0'; n++) if (*n == '/') *n = PATHSEPCHAR;
-#endif
 
 		DEBUG(misc, 6, "Found file in tar: %s (%d bytes, %d offset)", name, skip, pos);
 		if (_tar_filelist.insert(TarFileList::value_type(name, entry)).second) num++;
@@ -600,6 +611,8 @@
 	FOR_ALL_SEARCHPATHS(sp) {
 		FioAppendDirectory(path, MAX_PATH, sp, DATA_DIR);
 		num += ScanPathForTarFiles(path, strlen(path));
+		FioAppendDirectory(path, MAX_PATH, sp, AI_DIR);
+		num += ScanPathForTarFiles(path, strlen(path));
 	}
 	DEBUG(misc, 1, "Scan complete, found %d files", num);
 }
--- a/src/fileio.h	Mon May 19 22:28:27 2008 +0000
+++ b/src/fileio.h	Tue May 20 13:09:34 2008 +0000
@@ -34,6 +34,7 @@
 	GM_DIR,        ///< Subdirectory for all music
 	DATA_DIR,      ///< Subdirectory for all data (GRFs, sample.cat, intro game)
 	LANG_DIR,      ///< Subdirectory for all translation files
+	AI_DIR,        ///< Subdirectory for all AI files
 	NUM_SUBDIRS,   ///< Number of subdirectories
 	NO_DIRECTORY,  ///< A path without any base directory
 };
@@ -67,6 +68,7 @@
  */
 struct TarListEntry {
 	const char *filename;
+	const char *dirname;
 };
 struct TarFileListEntry {
 	TarListEntry *tar;
@@ -115,6 +117,7 @@
 void DeterminePaths(const char *exe);
 void *ReadFileToMem(const char *filename, size_t *lenp, size_t maxsize);
 bool FileExists(const char *filename);
+const char *FioTarFirstDir(const char *tarname);
 
 extern char *_personal_dir; ///< custom directory for personal settings, saves, newgrf, etc.
 
--- a/src/misc.cpp	Mon May 19 22:28:27 2008 +0000
+++ b/src/misc.cpp	Tue May 20 13:09:34 2008 +0000
@@ -99,7 +99,6 @@
 	InitializeTrains();
 	InitializeNPF();
 
-	AI_Initialize();
 	InitializePlayers();
 	InitializeCheats();
 
--- a/src/openttd.cpp	Mon May 19 22:28:27 2008 +0000
+++ b/src/openttd.cpp	Tue May 20 13:09:34 2008 +0000
@@ -465,6 +465,7 @@
 		case 'x': save_config = false; break;
 		case -2:
 		case 'h':
+			DeterminePaths(argv[0]);
 			ShowHelp();
 			return 0;
 		}
--- a/src/squirrel.cpp	Mon May 19 22:28:27 2008 +0000
+++ b/src/squirrel.cpp	Tue May 20 13:09:34 2008 +0000
@@ -3,12 +3,12 @@
 /** @file squirrel.cpp the implementation of the Squirrel class. It handles all Squirrel-stuff and gives a nice API back to work with. */
 
 #include <squirrel.h>
-#include <sqstdio.h>
 #include <stdarg.h>
 #include "stdafx.h"
 #include "debug.h"
 #include "squirrel.hpp"
 #include "squirrel_std.hpp"
+#include "fileio.h"
 #include <sqstdaux.h>
 
 void Squirrel::CompileError(HSQUIRRELVM vm, const SQChar *desc, const SQChar *source, SQInteger line, SQInteger column)
@@ -246,18 +246,140 @@
 	squirrel_register_global_std(this);
 }
 
-bool Squirrel::LoadScript(const char *script)
+static SQInteger _io_file_lexfeed_ASCII(SQUserPointer file)
+{
+	SQInteger ret;
+	char c;
+	if ((ret = fread(&c, sizeof(c), 1, (FILE *)file) > 0)) return c;
+	return 0;
+}
+
+static SQInteger _io_file_lexfeed_UTF8(SQUserPointer file)
+{
+	static const SQInteger utf8_lengths[16] =
+	{
+		1, 1, 1, 1, 1, 1, 1, 1, /* 0000 to 0111 : 1 byte (plain ASCII) */
+		0, 0, 0, 0,             /* 1000 to 1011 : not valid */
+		2, 2,                   /* 1100, 1101 : 2 bytes */
+		3,                      /* 1110 : 3 bytes */
+		4                       /* 1111 : 4 bytes */
+	};
+	static unsigned char byte_masks[5] = {0, 0, 0x1F, 0x0F, 0x07};
+	unsigned char inchar;
+	SQInteger c = 0;
+	if (fread(&inchar, sizeof(inchar), 1, (FILE *)file) != 1) return 0;
+	c = inchar;
+
+	if (c >= 0x80) {
+		SQInteger tmp;
+		SQInteger codelen = utf8_lengths[c >> 4];
+		if (codelen == 0) return 0;
+
+		tmp = c & byte_masks[codelen];
+		for (SQInteger n = 0; n < codelen - 1; n++) {
+			tmp <<= 6;
+			if (fread(&inchar, sizeof(inchar), 1, (FILE *)file) != 1) return 0;
+			tmp |= inchar & 0x3F;
+		}
+		c = tmp;
+	}
+	return c;
+}
+
+static SQInteger _io_file_lexfeed_UCS2_LE(SQUserPointer file)
+{
+	SQInteger ret;
+	wchar_t c;
+	if ((ret = fread(&c, sizeof(c), 1, (FILE *)file) > 0)) return (SQChar)c;
+	return 0;
+}
+
+static SQInteger _io_file_lexfeed_UCS2_BE(SQUserPointer file)
+{
+	SQInteger ret;
+	unsigned short c;
+	if ((ret = fread(&c, sizeof(c), 1, (FILE *)file) > 0)) {
+		c = ((c >> 8) & 0x00FF)| ((c << 8) & 0xFF00);
+		return (SQChar)c;
+	}
+	return 0;
+}
+
+static SQInteger _io_file_read(SQUserPointer file, SQUserPointer buf, SQInteger size)
+{
+	SQInteger ret = fread(buf, 1, size, (FILE *)file);
+	if (ret == 0) return -1;
+	return ret;
+}
+
+/* static */ SQRESULT Squirrel::LoadFile(HSQUIRRELVM vm, const char *filename, SQBool printerror)
+{
+	FILE *file = FioFOpenFile(filename, "rb", AI_DIR);
+	SQInteger ret;
+	unsigned short us;
+	unsigned char uc;
+	SQLEXREADFUNC func;
+
+	if (file != NULL) {
+		ret = fread(&us, 1, sizeof(us), file);
+		/* Most likely an empty file */
+		if (ret != 2) us = 0;
+
+		switch (us) {
+			case SQ_BYTECODE_STREAM_TAG: // BYTECODE
+				fseek(file, -2, SEEK_CUR);
+				if (SQ_SUCCEEDED(sq_readclosure(vm, _io_file_read, file))) {
+					FioFCloseFile(file);
+					return SQ_OK;
+				}
+				return sq_throwerror(vm, _SC("Couldn't read bytecode"));
+			case 0xFFFE: func = _io_file_lexfeed_UCS2_BE; break; // UTF-16 little endian
+			case 0xFEFF: func = _io_file_lexfeed_UCS2_LE; break; // UTF-16 big endian
+			case 0xBBEF: // UTF-8
+				if (fread(&uc, 1, sizeof(uc), file) == 0) {
+					FioFCloseFile(file);
+					return sq_throwerror(vm, _SC("I/O error"));
+				}
+				if (uc != 0xBF) {
+					FioFCloseFile(file);
+					return sq_throwerror(vm, _SC("Unrecognized encoding"));
+				}
+				func = _io_file_lexfeed_UTF8;
+				break;
+			default: func = _io_file_lexfeed_ASCII; fseek(file, -2, SEEK_CUR); break; // ASCII
+		}
+
+		if (SQ_SUCCEEDED(sq_compile(vm, func, file, OTTD2FS(filename), printerror))) {
+			FioFCloseFile(file);
+			return SQ_OK;
+		}
+		FioFCloseFile(file);
+		return SQ_ERROR;
+	}
+	return sq_throwerror(vm, _SC("cannot open the file"));
+}
+
+/* static */ bool Squirrel::LoadScript(HSQUIRRELVM vm, const char *script)
 {
 	/* Make sure we are always in the root-table */
-	sq_pushroottable(this->vm);
+	sq_pushroottable(vm);
 
 	/* Load and run the script */
-	if (SQ_FAILED(sqstd_dofile(this->vm, OTTD2FS(script), SQFalse, SQTrue))) {
-		DEBUG(misc, 0, "[squirrel] Failed to compile '%s'", script);
-		return false;
+	if (SQ_SUCCEEDED(LoadFile(vm, script, SQTrue))) {
+		sq_push(vm, -2);
+		if (SQ_SUCCEEDED(sq_call(vm, 1, SQFalse, SQTrue))) {
+			sq_remove(vm, -1);
+			return true;
+		}
 	}
 
-	return true;
+	DEBUG(misc, 0, "[squirrel] Failed to compile '%s'", script);
+	return false;
+}
+
+bool Squirrel::LoadScript(const char *script)
+{
+	return LoadScript(this->vm, script);
 }
 
 Squirrel::~Squirrel()
--- a/src/squirrel.hpp	Mon May 19 22:28:27 2008 +0000
+++ b/src/squirrel.hpp	Tue May 20 13:09:34 2008 +0000
@@ -49,6 +49,12 @@
 	 * @return False if loading failed.
 	 */
 	bool LoadScript(const char *script);
+	static bool LoadScript(HSQUIRRELVM vm, const char *script);
+
+	/**
+	 * Load a file to a given VM.
+	 */
+	static SQRESULT LoadFile(HSQUIRRELVM vm, const char *filename, SQBool printerror);
 
 	/**
 	 * Adds a function to the stack. Depending on the current state this means
--- a/src/squirrel_std.cpp	Mon May 19 22:28:27 2008 +0000
+++ b/src/squirrel_std.cpp	Tue May 20 13:09:34 2008 +0000
@@ -1,6 +1,5 @@
 
 #include <squirrel.h>
-#include <sqstdio.h>
 #include "stdafx.h"
 #include "debug.h"
 #include "squirrel.hpp"
@@ -47,21 +46,13 @@
 	real_filename = ReallocT(real_filename, scstrlen(real_filename) + scstrlen(filename) + 1);
 	scstrcat(real_filename, filename);
 
-	/* Make sure we are in the root-table, so everything is included in the root-level */
-	sq_pushroottable(vm);
-
-	SQInteger ret = 0;
+	bool ret = Squirrel::LoadScript(vm, FS2OTTD(real_filename));
 
-	/* Compile and load the file */
-	if (!SQ_SUCCEEDED(sqstd_dofile(vm, real_filename, SQFalse, SQTrue))) {
-		DEBUG(misc, 0, "[squirrel] Failed to compile '%s'", filename);
-		ret = SQ_ERROR;
-	}
 	/* Reset the top, so the stack stays correct */
 	sq_settop(vm, top);
 	free(real_filename);
 
-	return ret;
+	return ret ? 0: SQ_ERROR;
 }
 
 void squirrel_register_global_std(Squirrel *engine)