changeset 18518:928151e04253 draft

(svn r23362) -Codechange: refactor AIScanner, splitting it in AIScannerInfo and AIScannerLibrary
author truebrain <truebrain@openttd.org>
date Tue, 29 Nov 2011 23:21:52 +0000
parents 3dc74f255217
children dfcc8f8b1ce0
files src/ai/ai.hpp src/ai/ai_core.cpp src/ai/ai_gui.cpp src/ai/ai_info.cpp src/ai/ai_info.hpp src/ai/ai_scanner.cpp src/ai/ai_scanner.hpp src/network/network_content.cpp src/script/script_info.cpp src/script/script_info.hpp src/script/script_scanner.cpp src/script/script_scanner.hpp src/script/squirrel.cpp src/script/squirrel.hpp
diffstat 14 files changed, 537 insertions(+), 475 deletions(-) [+]
line wrap: on
line diff
--- a/src/ai/ai.hpp
+++ b/src/ai/ai.hpp
@@ -19,7 +19,7 @@
 #include <map>
 
 /** A list that maps AI names to their AIInfo object. */
-typedef std::map<const char *, class AIInfo *, StringCompare> AIInfoList;
+typedef std::map<const char *, class ScriptInfo *, StringCompare> ScriptInfoList;
 
 /**
  * Main AI class. Contains all functions needed to start, stop, save and load AIs.
@@ -129,11 +129,11 @@
 	/** Wrapper function for AIScanner::GetAIConsoleLibraryList */
 	static char *GetConsoleLibraryList(char *p, const char *last);
 	/** Wrapper function for AIScanner::GetAIInfoList */
-	static const AIInfoList *GetInfoList();
+	static const ScriptInfoList *GetInfoList();
 	/** Wrapper function for AIScanner::GetUniqueAIInfoList */
-	static const AIInfoList *GetUniqueInfoList();
+	static const ScriptInfoList *GetUniqueInfoList();
 	/** Wrapper function for AIScanner::FindInfo */
-	static AIInfo *FindInfo(const char *name, int version, bool force_exact_match);
+	static class AIInfo *FindInfo(const char *name, int version, bool force_exact_match);
 	/** Wrapper function for AIScanner::FindLibrary */
 	static class AILibrary *FindLibrary(const char *library, int version);
 
@@ -145,10 +145,12 @@
 #if defined(ENABLE_NETWORK)
 	/** Wrapper function for AIScanner::HasAI */
 	static bool HasAI(const struct ContentInfo *ci, bool md5sum);
+	static bool HasAILibrary(const ContentInfo *ci, bool md5sum);
 #endif
 private:
-	static uint frame_counter;          ///< Tick counter for the AI code
-	static class AIScanner *ai_scanner; ///< AIScanner instance that is used to find AIs
+	static uint frame_counter;                      ///< Tick counter for the AI code
+	static class AIScannerInfo *scanner_info;       ///< ScriptScanner instance that is used to find AIs
+	static class AIScannerLibrary *scanner_library; ///< ScriptScanner instance that is used to find AI Libraries
 };
 
 #else /* ENABLE_AI */
@@ -167,6 +169,7 @@
 	static void KillAll() {}
 	static void GameLoop() {}
 	static bool HasAI(const struct ContentInfo *ci, bool md5sum) { return false; }
+	static bool HasAILibrary(const struct ContentInfo *ci, bool md5sum) { return false; }
 	static void Rescan() {}
 	static char *GetConsoleList(char *p, const char *last, bool newest_only = false) { return p; }
 	static void nop() { }
--- a/src/ai/ai_core.cpp
+++ b/src/ai/ai_core.cpp
@@ -21,10 +21,12 @@
 #include "ai_scanner.hpp"
 #include "ai_instance.hpp"
 #include "ai_config.hpp"
+#include "ai.hpp"
 #include "../script/api/script_error.hpp"
 
 /* static */ uint AI::frame_counter = 0;
-/* static */ AIScanner *AI::ai_scanner = NULL;
+/* static */ AIScannerInfo *AI::scanner_info = NULL;
+/* static */ AIScannerLibrary *AI::scanner_library = NULL;
 
 /* static */ bool AI::CanStartNew()
 {
@@ -42,7 +44,7 @@
 	AIConfig *config = AIConfig::GetConfig(company);
 	AIInfo *info = config->GetInfo();
 	if (info == NULL || (rerandomise_ai && config->IsRandomAI())) {
-		info = AI::ai_scanner->SelectRandomAI();
+		info = AI::scanner_info->SelectRandomAI();
 		assert(info != NULL);
 		/* Load default data and store the name in the settings */
 		config->ChangeAI(info->GetName(), -1, false, true);
@@ -134,12 +136,15 @@
 
 /* static */ void AI::Initialize()
 {
-	if (AI::ai_scanner != NULL) AI::Uninitialize(true);
+	if (AI::scanner_info != NULL) AI::Uninitialize(true);
 
 	AI::frame_counter = 0;
-	if (AI::ai_scanner == NULL) {
+	if (AI::scanner_info == NULL) {
 		TarScanner::DoScan(TarScanner::AI);
-		AI::ai_scanner = new AIScanner();
+		AI::scanner_info = new AIScannerInfo();
+		AI::scanner_info->Initialize("AIScanner");
+		AI::scanner_library = new AIScannerLibrary();
+		AI::scanner_library->Initialize("AISCanner");
 	}
 }
 
@@ -152,8 +157,10 @@
 		 *  still load all the AIS, while keeping the configs in place */
 		Rescan();
 	} else {
-		delete AI::ai_scanner;
-		AI::ai_scanner = NULL;
+		delete AI::scanner_info;
+		delete AI::scanner_library;
+		AI::scanner_info = NULL;
+		AI::scanner_library = NULL;
 
 		for (CompanyID c = COMPANY_FIRST; c < MAX_COMPANIES; c++) {
 			if (_settings_game.ai_config[c] != NULL) {
@@ -286,42 +293,59 @@
 
 /* static */ char *AI::GetConsoleList(char *p, const char *last, bool newest_only)
 {
-	return AI::ai_scanner->GetAIConsoleList(p, last, newest_only);
+	return AI::scanner_info->GetConsoleList(p, last, newest_only);
 }
 
 /* static */ char *AI::GetConsoleLibraryList(char *p, const char *last)
 {
-	 return AI::ai_scanner->GetAIConsoleLibraryList(p, last);
+	 return AI::scanner_library->GetConsoleList(p, last, true);
 }
 
-/* static */ const AIInfoList *AI::GetInfoList()
+/* static */ const ScriptInfoList *AI::GetInfoList()
 {
-	return AI::ai_scanner->GetAIInfoList();
+	return AI::scanner_info->GetInfoList();
 }
 
-/* static */ const AIInfoList *AI::GetUniqueInfoList()
+/* static */ const ScriptInfoList *AI::GetUniqueInfoList()
 {
-	return AI::ai_scanner->GetUniqueAIInfoList();
+	return AI::scanner_info->GetUniqueInfoList();
 }
 
 /* static */ AIInfo *AI::FindInfo(const char *name, int version, bool force_exact_match)
 {
-	return AI::ai_scanner->FindInfo(name, version, force_exact_match);
+	return AI::scanner_info->FindInfo(name, version, force_exact_match);
 }
 
 /* static */ AILibrary *AI::FindLibrary(const char *library, int version)
 {
-	return AI::ai_scanner->FindLibrary(library, version);
+	return AI::scanner_library->FindLibrary(library, version);
 }
 
 /* static */ void AI::Rescan()
 {
 	TarScanner::DoScan(TarScanner::AI);
 
-	AI::ai_scanner->RescanAIDir();
+	AI::scanner_info->RescanDir();
+	AI::scanner_library->RescanDir();
 	ResetConfig();
 
 	InvalidateWindowData(WC_AI_LIST, 0, 1);
 	SetWindowClassesDirty(WC_AI_DEBUG);
 	SetWindowDirty(WC_AI_SETTINGS, 0);
 }
+
+/**
+ * Check whether we have an AI (library) with the exact characteristics as ci.
+ * @param ci the characteristics to search on (shortname and md5sum)
+ * @param md5sum whether to check the MD5 checksum
+ * @return true iff we have an AI (library) matching.
+ */
+/* static */ bool AI::HasAI(const ContentInfo *ci, bool md5sum)
+{
+	return AI::scanner_info->HasScript(ci, md5sum);
+}
+
+/* static */ bool AI::HasAILibrary(const ContentInfo *ci, bool md5sum)
+{
+	return AI::scanner_library->HasScript(ci, md5sum);
+}
--- a/src/ai/ai_gui.cpp
+++ b/src/ai/ai_gui.cpp
@@ -47,11 +47,11 @@
  * Window that let you choose an available AI.
  */
 struct AIListWindow : public Window {
-	const AIInfoList *ai_info_list; ///< The list of AIs.
-	int selected;                   ///< The currently selected AI.
-	CompanyID slot;                 ///< The company we're selecting a new AI for.
-	int line_height;                ///< Height of a row in the matrix widget.
-	Scrollbar *vscroll;             ///< Cache of the vertical scrollbar.
+	const ScriptInfoList *ai_info_list; ///< The list of AIs.
+	int selected;                       ///< The currently selected AI.
+	CompanyID slot;                     ///< The company we're selecting a new AI for.
+	int line_height;                    ///< Height of a row in the matrix widget.
+	Scrollbar *vscroll;                 ///< Cache of the vertical scrollbar.
 
 	/**
 	 * Constructor for the window.
@@ -74,7 +74,7 @@
 		if (AIConfig::GetConfig(slot)->HasAI()) {
 			AIInfo *info = AIConfig::GetConfig(slot)->GetInfo();
 			int i = 0;
-			for (AIInfoList::const_iterator it = this->ai_info_list->begin(); it != this->ai_info_list->end(); it++, i++) {
+			for (ScriptInfoList::const_iterator it = this->ai_info_list->begin(); it != this->ai_info_list->end(); it++, i++) {
 				if ((*it).second == info) {
 					this->selected = i;
 					break;
@@ -105,7 +105,7 @@
 					DrawString(r.left + WD_MATRIX_LEFT, r.right - WD_MATRIX_LEFT, y + WD_MATRIX_TOP, STR_AI_CONFIG_RANDOM_AI, this->selected == -1 ? TC_WHITE : TC_BLACK);
 					y += this->line_height;
 				}
-				AIInfoList::const_iterator it = this->ai_info_list->begin();
+				ScriptInfoList::const_iterator it = this->ai_info_list->begin();
 				for (int i = 1; it != this->ai_info_list->end(); i++, it++) {
 					if (this->vscroll->IsVisible(i)) {
 						DrawString(r.left + WD_MATRIX_LEFT, r.right - WD_MATRIX_RIGHT, y + WD_MATRIX_TOP, (*it).second->GetName(), (this->selected == i - 1) ? TC_WHITE : TC_BLACK);
@@ -116,9 +116,9 @@
 			}
 			case AIL_WIDGET_INFO_BG: {
 				AIInfo *selected_info = NULL;
-				AIInfoList::const_iterator it = this->ai_info_list->begin();
+				ScriptInfoList::const_iterator it = this->ai_info_list->begin();
 				for (int i = 1; selected_info == NULL && it != this->ai_info_list->end(); i++, it++) {
-					if (this->selected == i - 1) selected_info = (*it).second;
+					if (this->selected == i - 1) selected_info = static_cast<AIInfo *>((*it).second);
 				}
 				/* Some info about the currently selected AI. */
 				if (selected_info != NULL) {
@@ -150,7 +150,7 @@
 		if (this->selected == -1) {
 			AIConfig::GetConfig(slot)->ChangeAI(NULL);
 		} else {
-			AIInfoList::const_iterator it = this->ai_info_list->begin();
+			ScriptInfoList::const_iterator it = this->ai_info_list->begin();
 			for (int i = 0; i < this->selected; i++) it++;
 			AIConfig::GetConfig(slot)->ChangeAI((*it).second->GetName(), (*it).second->GetVersion());
 		}
--- a/src/ai/ai_info.cpp
+++ b/src/ai/ai_info.cpp
@@ -7,16 +7,18 @@
  * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
  */
 
-/** @file ai_info.cpp Implementation of AIFileInfo */
+/** @file ai_info.cpp Implementation of AIInfo and AILibrary */
 
 #include "../stdafx.h"
 
 #include "../script/squirrel_helper.hpp"
+#include "../script/squirrel_class.hpp"
 #include "ai_info.hpp"
 #include "ai_scanner.hpp"
 #include "../settings_type.h"
 #include "../debug.h"
 #include "../rev.h"
+#include "ai.hpp"
 
 /** Maximum number of operations allowed for getting a particular setting. */
 static const int MAX_GET_SETTING_OPS = 100000;
@@ -37,20 +39,6 @@
 	NULL
 };
 
-AILibrary::~AILibrary()
-{
-	free(this->category);
-}
-
-/* static */ SQInteger AIFileInfo::Constructor(HSQUIRRELVM vm, AIFileInfo *info)
-{
-	SQInteger res = ScriptFileInfo::Constructor(vm, info);
-	if (res != 0) return res;
-	info->base = ((AIScanner *)Squirrel::GetGlobalPointer(vm));
-
-	return 0;
-}
-
 /**
  * Check if the API version provided by the AI is supported.
  * @param api_version The API version as provided by the AI.
@@ -60,6 +48,29 @@
 	return strcmp(api_version, "0.7") == 0 || strcmp(api_version, "1.0") == 0 || strcmp(api_version, "1.1") == 0 || strcmp(api_version, "1.2") == 0;
 }
 
+#if defined(WIN32)
+#undef GetClassName
+#endif /* WIN32 */
+template <> const char *GetClassName<AIInfo, ST_AI>() { return "AIInfo"; }
+
+/* static */ void AIInfo::RegisterAPI(Squirrel *engine)
+{
+	/* Create the AIInfo class, and add the RegisterAI function */
+	DefSQClass<AIInfo, ST_AI> SQAIInfo("AIInfo");
+	SQAIInfo.PreRegister(engine);
+	SQAIInfo.AddConstructor<void (AIInfo::*)(), 1>(engine, "x");
+	SQAIInfo.DefSQAdvancedMethod(engine, &AIInfo::AddSetting, "AddSetting");
+	SQAIInfo.DefSQAdvancedMethod(engine, &AIInfo::AddLabels, "AddLabels");
+	SQAIInfo.DefSQConst(engine, AICONFIG_NONE, "AICONFIG_NONE");
+	SQAIInfo.DefSQConst(engine, AICONFIG_RANDOM, "AICONFIG_RANDOM");
+	SQAIInfo.DefSQConst(engine, AICONFIG_BOOLEAN, "AICONFIG_BOOLEAN");
+	SQAIInfo.DefSQConst(engine, AICONFIG_INGAME, "AICONFIG_INGAME");
+	SQAIInfo.DefSQConst(engine, AICONFIG_AI_DEVELOPER, "AICONFIG_AI_DEVELOPER");
+	SQAIInfo.PostRegister(engine);
+	engine->AddMethod("RegisterAI", &AIInfo::Constructor, 2, "tx");
+	engine->AddMethod("RegisterDummyAI", &AIInfo::DummyConstructor, 2, "tx");
+}
+
 /* static */ SQInteger AIInfo::Constructor(HSQUIRRELVM vm)
 {
 	/* Get the AIInfo */
@@ -67,7 +78,7 @@
 	if (SQ_FAILED(sq_getinstanceup(vm, 2, &instance, 0)) || instance == NULL) return sq_throwerror(vm, _SC("Pass an instance of a child class of AIInfo to RegisterAI"));
 	AIInfo *info = (AIInfo *)instance;
 
-	SQInteger res = AIFileInfo::Constructor(vm, info);
+	SQInteger res = ScriptInfo::Constructor(vm, info);
 	if (res != 0) return res;
 
 	AIConfigItem config = _start_date_config;
@@ -104,7 +115,7 @@
 	/* Remove the link to the real instance, else it might get deleted by RegisterAI() */
 	sq_setinstanceup(vm, 2, NULL);
 	/* Register the AI to the base system */
-	info->base->RegisterAI(info);
+	info->GetScanner()->RegisterScript(info);
 	return 0;
 }
 
@@ -116,7 +127,7 @@
 	AIInfo *info = (AIInfo *)instance;
 	info->api_version = NULL;
 
-	SQInteger res = AIFileInfo::Constructor(vm, info);
+	SQInteger res = ScriptInfo::Constructor(vm, info);
 	if (res != 0) return res;
 
 	char buf[8];
@@ -126,7 +137,7 @@
 	/* Remove the link to the real instance, else it might get deleted by RegisterAI() */
 	sq_setinstanceup(vm, 2, NULL);
 	/* Register the AI to the base system */
-	info->base->SetDummyAI(info);
+	static_cast<AIScannerInfo *>(info->GetScanner())->SetDummyAI(info);
 	return 0;
 }
 
@@ -350,12 +361,26 @@
 	return -1;
 }
 
+
+AILibrary::~AILibrary()
+{
+	free(this->category);
+}
+
+/* static */ void AILibrary::RegisterAPI(Squirrel *engine)
+{
+	/* Create the AILibrary class, and add the RegisterLibrary function */
+	engine->AddClassBegin("AILibrary");
+	engine->AddClassEnd();
+	engine->AddMethod("RegisterLibrary", &AILibrary::Constructor, 2, "tx");
+}
+
 /* static */ SQInteger AILibrary::Constructor(HSQUIRRELVM vm)
 {
-	/* Create a new AIFileInfo */
+	/* Create a new library */
 	AILibrary *library = new AILibrary();
 
-	SQInteger res = AIFileInfo::Constructor(vm, library);
+	SQInteger res = ScriptInfo::Constructor(vm, library);
 	if (res != 0) {
 		delete library;
 		return res;
@@ -368,7 +393,7 @@
 	}
 
 	/* Register the Library to the base system */
-	library->base->RegisterLibrary(library);
+	library->GetScanner()->RegisterScript(library);
 
 	return 0;
 }
--- a/src/ai/ai_info.hpp
+++ b/src/ai/ai_info.hpp
@@ -49,25 +49,18 @@
 
 typedef std::list<AIConfigItem> AIConfigItemList; ///< List of AIConfig items.
 
-/** Base class that holds some basic information about AIs and AI libraries. */
-class AIFileInfo : public ScriptFileInfo {
-public:
-	/**
-	 * Process the creation of a FileInfo object.
-	 */
-	static SQInteger Constructor(HSQUIRRELVM vm, AIFileInfo *info);
-
-protected:
-	class AIScanner *base; ///< AIScanner object that was used to scan this AI (library) info.
-};
-
 /** All static information from an AI like name, version, etc. */
-class AIInfo : public AIFileInfo {
+class AIInfo : public ScriptInfo {
 public:
 	AIInfo();
 	~AIInfo();
 
 	/**
+	 * Register the functions of this class.
+	 */
+	static void RegisterAPI(Squirrel *engine);
+
+	/**
 	 * Create an AI, using this AIInfo as start-template.
 	 */
 	static SQInteger Constructor(HSQUIRRELVM vm);
@@ -130,12 +123,17 @@
 };
 
 /** All static information from an AI library like name, version, etc. */
-class AILibrary : public AIFileInfo {
+class AILibrary : public ScriptInfo {
 public:
-	AILibrary() : AIFileInfo(), category(NULL) {};
+	AILibrary() : ScriptInfo(), category(NULL) {};
 	~AILibrary();
 
 	/**
+	 * Register the functions of this class.
+	 */
+	static void RegisterAPI(Squirrel *engine);
+
+	/**
 	 * Create an AI, using this AIInfo as start-template.
 	 */
 	static SQInteger Constructor(HSQUIRRELVM vm);
--- a/src/ai/ai_scanner.cpp
+++ b/src/ai/ai_scanner.cpp
@@ -20,177 +20,50 @@
 #include "ai_scanner.hpp"
 #include "../script/api/script_controller.hpp"
 
-void AIScanner::RescanAIDir()
-{
-	/* Get rid of information of old AIs. */
-	this->Reset();
-	this->Scan(PATHSEP "info.nut", AI_DIR);
-	this->Scan(PATHSEP "library.nut", AI_LIBRARY_DIR);
-}
 
-template <> const char *GetClassName<AIInfo, ST_AI>() { return "AIInfo"; }
-
-AIScanner::AIScanner() :
+AIScannerInfo::AIScannerInfo() :
 	ScriptScanner(),
 	info_dummy(NULL)
 {
-	/* Create the AIInfo class, and add the RegisterAI function */
-	DefSQClass<AIInfo, ST_AI> SQAIInfo("AIInfo");
-	SQAIInfo.PreRegister(engine);
-	SQAIInfo.AddConstructor<void (AIInfo::*)(), 1>(engine, "x");
-	SQAIInfo.DefSQAdvancedMethod(this->engine, &AIInfo::AddSetting, "AddSetting");
-	SQAIInfo.DefSQAdvancedMethod(this->engine, &AIInfo::AddLabels, "AddLabels");
-	SQAIInfo.DefSQConst(engine, AICONFIG_NONE, "AICONFIG_NONE");
-	SQAIInfo.DefSQConst(engine, AICONFIG_RANDOM, "AICONFIG_RANDOM");
-	SQAIInfo.DefSQConst(engine, AICONFIG_BOOLEAN, "AICONFIG_BOOLEAN");
-	SQAIInfo.DefSQConst(engine, AICONFIG_INGAME, "AICONFIG_INGAME");
-	SQAIInfo.DefSQConst(engine, AICONFIG_AI_DEVELOPER, "AICONFIG_AI_DEVELOPER");
-	SQAIInfo.PostRegister(engine);
-	this->engine->AddMethod("RegisterAI", &AIInfo::Constructor, 2, "tx");
-	this->engine->AddMethod("RegisterDummyAI", &AIInfo::DummyConstructor, 2, "tx");
+}
 
-	/* Create the AILibrary class, and add the RegisterLibrary function */
-	this->engine->AddClassBegin("AILibrary");
-	this->engine->AddClassEnd();
-	this->engine->AddMethod("RegisterLibrary", &AILibrary::Constructor, 2, "tx");
-
-	/* Scan the AI dir for scripts */
-	this->RescanAIDir();
+void AIScannerInfo::Initialize(const char *name)
+{
+	ScriptScanner::Initialize(name);
 
 	/* Create the dummy AI */
-	this->engine->ResetCrashed();
-
 	free(this->main_script);
 	this->main_script = strdup("%_dummy");
 	extern void AI_CreateAIInfoDummy(HSQUIRRELVM vm);
 	AI_CreateAIInfoDummy(this->engine->GetVM());
 }
 
-void AIScanner::Reset()
+void AIScannerInfo::SetDummyAI(class AIInfo *info)
 {
-	AIInfoList::iterator it = this->info_list.begin();
-	for (; it != this->info_list.end(); it++) {
-		free((*it).first);
-		delete (*it).second;
-	}
-	it = this->info_single_list.begin();
-	for (; it != this->info_single_list.end(); it++) {
-		free((*it).first);
-	}
-	AILibraryList::iterator lit = this->library_list.begin();
-	for (; lit != this->library_list.end(); lit++) {
-		free((*lit).first);
-		delete (*lit).second;
-	}
-
-	this->info_list.clear();
-	this->info_single_list.clear();
-	this->library_list.clear();
+	this->info_dummy = info;
 }
 
-AIScanner::~AIScanner()
+AIScannerInfo::~AIScannerInfo()
 {
-	this->Reset();
-
 	delete this->info_dummy;
 }
 
-AILibrary *AIScanner::FindLibrary(const char *library, int version)
-{
-	/* Internally we store libraries as 'library.version' */
-	char library_name[1024];
-	snprintf(library_name, sizeof(library_name), "%s.%d", library, version);
-	strtolower(library_name);
-
-	/* Check if the library + version exists */
-	AILibraryList::iterator iter = this->library_list.find(library_name);
-	if (iter == this->library_list.end()) return NULL;
-
-	return (*iter).second;
-}
-
-void AIScanner::RegisterLibrary(AILibrary *library)
+void AIScannerInfo::GetScriptName(ScriptInfo *info, char *name, int len)
 {
-	char library_name[1024];
-	snprintf(library_name, sizeof(library_name), "%s.%s.%d", library->GetCategory(), library->GetInstanceName(), library->GetVersion());
-	strtolower(library_name);
-
-	if (this->library_list.find(library_name) != this->library_list.end()) {
-		/* This AI was already registered */
-#ifdef WIN32
-		/* Windows doesn't care about the case */
-		if (strcasecmp(this->library_list[library_name]->GetMainScript(), library->GetMainScript()) == 0) {
-#else
-		if (strcmp(this->library_list[library_name]->GetMainScript(), library->GetMainScript()) == 0) {
-#endif
-			delete library;
-			return;
-		}
-
-		DEBUG(ai, 1, "Registering two libraries with the same name and version");
-		DEBUG(ai, 1, "  1: %s", this->library_list[library_name]->GetMainScript());
-		DEBUG(ai, 1, "  2: %s", library->GetMainScript());
-		DEBUG(ai, 1, "The first is taking precedence.");
-
-		delete library;
-		return;
-	}
-
-	this->library_list[strdup(library_name)] = library;
+	snprintf(name, len, "%s", info->GetName());
 }
 
-void AIScanner::RegisterAI(AIInfo *info)
+void AIScannerInfo::RegisterAPI(class Squirrel *engine)
 {
-	char ai_name[1024];
-	snprintf(ai_name, sizeof(ai_name), "%s.%d", info->GetName(), info->GetVersion());
-	strtolower(ai_name);
-
-	/* Check if GetShortName follows the rules */
-	if (strlen(info->GetShortName()) != 4) {
-		DEBUG(ai, 0, "The AI '%s' returned a string from GetShortName() which is not four characaters. Unable to load the AI.", info->GetName());
-		delete info;
-		return;
-	}
-
-	if (this->info_list.find(ai_name) != this->info_list.end()) {
-		/* This AI was already registered */
-#ifdef WIN32
-		/* Windows doesn't care about the case */
-		if (strcasecmp(this->info_list[ai_name]->GetMainScript(), info->GetMainScript()) == 0) {
-#else
-		if (strcmp(this->info_list[ai_name]->GetMainScript(), info->GetMainScript()) == 0) {
-#endif
-			delete info;
-			return;
-		}
-
-		DEBUG(ai, 1, "Registering two AIs with the same name and version");
-		DEBUG(ai, 1, "  1: %s", this->info_list[ai_name]->GetMainScript());
-		DEBUG(ai, 1, "  2: %s", info->GetMainScript());
-		DEBUG(ai, 1, "The first is taking precedence.");
-
-		delete info;
-		return;
-	}
-
-	this->info_list[strdup(ai_name)] = info;
-
-	/* Add the AI to the 'unique' AI list, where only the highest version of the
-	 *  AI is registered. */
-	snprintf(ai_name, sizeof(ai_name), "%s", info->GetName());
-	strtolower(ai_name);
-	if (this->info_single_list.find(ai_name) == this->info_single_list.end()) {
-		this->info_single_list[strdup(ai_name)] = info;
-	} else if (this->info_single_list[ai_name]->GetVersion() < info->GetVersion()) {
-		this->info_single_list[ai_name] = info;
-	}
+	AIInfo::RegisterAPI(engine);
 }
 
-AIInfo *AIScanner::SelectRandomAI() const
+AIInfo *AIScannerInfo::SelectRandomAI() const
 {
 	uint num_random_ais = 0;
-	for (AIInfoList::const_iterator it = this->info_single_list.begin(); it != this->info_single_list.end(); it++) {
-		if (it->second->UseAsRandomAI()) num_random_ais++;
+	for (ScriptInfoList::const_iterator it = this->info_single_list.begin(); it != this->info_single_list.end(); it++) {
+		AIInfo *i = static_cast<AIInfo *>((*it).second);
+		if (i->UseAsRandomAI()) num_random_ais++;
 	}
 
 	if (num_random_ais == 0) {
@@ -207,16 +80,17 @@
 	}
 
 	/* Find the Nth item from the array */
-	AIInfoList::const_iterator it = this->info_single_list.begin();
-	while (!it->second->UseAsRandomAI()) it++;
+	ScriptInfoList::const_iterator it = this->info_single_list.begin();
+	AIInfo *i = static_cast<AIInfo *>((*it).second);
+	while (!i->UseAsRandomAI()) it++;
 	for (; pos > 0; pos--) {
 		it++;
-		while (!it->second->UseAsRandomAI()) it++;
+		while (!i->UseAsRandomAI()) it++;
 	}
-	return (*it).second;
+	return i;
 }
 
-AIInfo *AIScanner::FindInfo(const char *nameParam, int versionParam, bool force_exact_match)
+AIInfo *AIScannerInfo::FindInfo(const char *nameParam, int versionParam, bool force_exact_match)
 {
 	if (this->info_list.size() == 0) return NULL;
 	if (nameParam == NULL) return NULL;
@@ -230,7 +104,7 @@
 
 	if (versionParam == -1) {
 		/* We want to load the latest version of this AI; so find it */
-		if (this->info_single_list.find(ai_name) != this->info_single_list.end()) return this->info_single_list[ai_name];
+		if (this->info_single_list.find(ai_name) != this->info_single_list.end()) return static_cast<AIInfo *>(this->info_single_list[ai_name]);
 
 		/* If we didn't find a match AI, maybe the user included a version */
 		char *e = strrchr(ai_name, '.');
@@ -246,177 +120,45 @@
 		char ai_name_tmp[1024];
 		snprintf(ai_name_tmp, sizeof(ai_name_tmp), "%s.%d", ai_name, versionParam);
 		strtolower(ai_name_tmp);
-		if (this->info_list.find(ai_name_tmp) != this->info_list.end()) return this->info_list[ai_name_tmp];
+		if (this->info_list.find(ai_name_tmp) != this->info_list.end()) return static_cast<AIInfo *>(this->info_list[ai_name_tmp]);
 	}
 
 	/* See if there is a compatible AI which goes by that name, with the highest
 	 *  version which allows loading the requested version */
-	AIInfoList::iterator it = this->info_list.begin();
+	ScriptInfoList::iterator it = this->info_list.begin();
 	for (; it != this->info_list.end(); it++) {
-		if (strcasecmp(ai_name, (*it).second->GetName()) == 0 && (*it).second->CanLoadFromVersion(versionParam) && (version == -1 || (*it).second->GetVersion() > version)) {
+		AIInfo *i = static_cast<AIInfo *>((*it).second);
+		if (strcasecmp(ai_name, i->GetName()) == 0 && i->CanLoadFromVersion(versionParam) && (version == -1 || i->GetVersion() > version)) {
 			version = (*it).second->GetVersion();
-			info = (*it).second;
+			info = i;
 		}
 	}
 
 	return info;
 }
 
-char *AIScanner::GetAIConsoleList(char *p, const char *last, bool newest_only) const
+
+void AIScannerLibrary::GetScriptName(ScriptInfo *info, char *name, int len)
 {
-	p += seprintf(p, last, "List of AIs:\n");
-	const AIInfoList &list = newest_only ? this->info_single_list : this->info_list;
-	AIInfoList::const_iterator it = list.begin();
-	for (; it != list.end(); it++) {
-		AIInfo *i = (*it).second;
-		p += seprintf(p, last, "%10s (v%d): %s\n", i->GetName(), i->GetVersion(), i->GetDescription());
-	}
-	p += seprintf(p, last, "\n");
-
-	return p;
+	AILibrary *library = static_cast<AILibrary *>(info);
+	snprintf(name, len, "%s.%s", library->GetCategory(), library->GetInstanceName());
 }
 
-char *AIScanner::GetAIConsoleLibraryList(char *p, const char *last) const
+void AIScannerLibrary::RegisterAPI(class Squirrel *engine)
 {
-	p += seprintf(p, last, "List of AI Libraries:\n");
-	AILibraryList::const_iterator it = this->library_list.begin();
-	for (; it != this->library_list.end(); it++) {
-		AILibrary *i = (*it).second;
-		p += seprintf(p, last, "%10s (v%d): %s\n", i->GetName(), i->GetVersion(), i->GetDescription());
-	}
-	p += seprintf(p, last, "\n");
-
-	return p;
+	AILibrary::RegisterAPI(engine);
 }
 
-#if defined(ENABLE_NETWORK)
-#include "../network/network_content.h"
-#include "../3rdparty/md5/md5.h"
-#include "../tar_type.h"
-
-/** Helper for creating a MD5sum of all files within of an AI. */
-struct AIFileChecksumCreator : FileScanner {
-	byte md5sum[16]; ///< The final md5sum
-
-	/**
-	 * Initialise the md5sum to be all zeroes,
-	 * so we can easily xor the data.
-	 */
-	AIFileChecksumCreator()
-	{
-		memset(this->md5sum, 0, sizeof(this->md5sum));
-	}
-
-	/* Add the file and calculate the md5 sum. */
-	virtual bool AddFile(const char *filename, size_t basepath_length, const char *tar_filename)
-	{
-		Md5 checksum;
-		uint8 buffer[1024];
-		size_t len, size;
-		byte tmp_md5sum[16];
-
-		/* Open the file ... */
-		FILE *f = FioFOpenFile(filename, "rb", AI_DIR, &size);
-		if (f == NULL) return false;
-
-		/* ... calculate md5sum... */
-		while ((len = fread(buffer, 1, (size > sizeof(buffer)) ? sizeof(buffer) : size, f)) != 0 && size != 0) {
-			size -= len;
-			checksum.Append(buffer, len);
-		}
-		checksum.Finish(tmp_md5sum);
-
-		FioFCloseFile(f);
-
-		/* ... and xor it to the overall md5sum. */
-		for (uint i = 0; i < sizeof(md5sum); i++) this->md5sum[i] ^= tmp_md5sum[i];
-
-		return true;
-	}
-};
-
-/**
- * Check whether the AI given in info is the same as in ci based
- * on the shortname and md5 sum.
- * @param ci   the information to compare to
- * @param md5sum whether to check the MD5 checksum
- * @param info the AI to get the shortname and md5 sum from
- * @return true iff they're the same
- */
-static bool IsSameAI(const ContentInfo *ci, bool md5sum, AIFileInfo *info)
+AILibrary *AIScannerLibrary::FindLibrary(const char *library, int version)
 {
-	uint32 id = 0;
-	const char *str = info->GetShortName();
-	for (int j = 0; j < 4 && *str != '\0'; j++, str++) id |= *str << (8 * j);
-
-	if (id != ci->unique_id) return false;
-	if (!md5sum) return true;
+	/* Internally we store libraries as 'library.version' */
+	char library_name[1024];
+	snprintf(library_name, sizeof(library_name), "%s.%d", library, version);
+	strtolower(library_name);
 
-	AIFileChecksumCreator checksum;
-	const char *tar_filename = info->GetTarFile();
-	TarList::iterator iter;
-	if (tar_filename != NULL && (iter = _tar_list[AI_DIR].find(tar_filename)) != _tar_list[AI_DIR].end()) {
-		/* The main script is in a tar file, so find all files that
-		 * are in the same tar and add them to the MD5 checksumming. */
-		TarFileList::iterator tar;
-		FOR_ALL_TARS(tar, AI_DIR) {
-			/* Not in the same tar. */
-			if (tar->second.tar_filename != iter->first) continue;
-
-			/* Check the extension. */
-			const char *ext = strrchr(tar->first.c_str(), '.');
-			if (ext == NULL || strcasecmp(ext, ".nut") != 0) continue;
-
-			checksum.AddFile(tar->first.c_str(), 0, tar_filename);
-		}
-	} else {
-		char path[MAX_PATH];
-		strecpy(path, info->GetMainScript(), lastof(path));
-		/* There'll always be at least 1 path separator character in an AI's
-		 * main script name as the search algorithm requires the main script to
-		 * be in a subdirectory of the AI directory; so ai/<path>/main.nut. */
-		*strrchr(path, PATHSEPCHAR) = '\0';
-		checksum.Scan(".nut", path);
-	}
-
-	return memcmp(ci->md5sum, checksum.md5sum, sizeof(ci->md5sum)) == 0;
-}
+	/* Check if the library + version exists */
+	ScriptInfoList::iterator iter = this->info_list.find(library_name);
+	if (iter == this->info_list.end()) return NULL;
 
-/**
- * Check whether we have an AI (library) with the exact characteristics as ci.
- * @param ci the characteristics to search on (shortname and md5sum)
- * @param md5sum whether to check the MD5 checksum
- * @return true iff we have an AI (library) matching.
- */
-bool AIScanner::HasAI(const ContentInfo *ci, bool md5sum)
-{
-	switch (ci->type) {
-		case CONTENT_TYPE_AI:
-			for (AIInfoList::iterator it = this->info_list.begin(); it != this->info_list.end(); it++) {
-				if (IsSameAI(ci, md5sum, (*it).second)) return true;
-			}
-			return false;
-
-		case CONTENT_TYPE_AI_LIBRARY:
-			for (AILibraryList::iterator it = this->library_list.begin(); it != this->library_list.end(); it++) {
-				if (IsSameAI(ci, md5sum, (*it).second)) return true;
-			}
-			return false;
-
-		default:
-			NOT_REACHED();
-	}
+	return static_cast<AILibrary *>((*iter).second);
 }
-
-/**
- * Check whether we have an AI (library) with the exact characteristics as ci.
- * @param ci the characteristics to search on (shortname and md5sum)
- * @param md5sum whether to check the MD5 checksum
- * @return true iff we have an AI (library) matching.
- */
-/* static */ bool AI::HasAI(const ContentInfo *ci, bool md5sum)
-{
-	return AI::ai_scanner->HasAI(ci, md5sum);
-}
-
-#endif /* ENABLE_NETWORK */
--- a/src/ai/ai_scanner.hpp
+++ b/src/ai/ai_scanner.hpp
@@ -13,95 +13,61 @@
 #define AI_SCANNER_HPP
 
 #include "../script/script_scanner.hpp"
-#include "ai.hpp"
 
-/**
- * Class that scans for available AIs.
- */
-class AIScanner : public ScriptScanner {
+class AIScannerInfo : public ScriptScanner {
 public:
-	AIScanner();
-	~AIScanner();
+	AIScannerInfo();
+	~AIScannerInfo();
+
+	/* virtual */ void Initialize(const char *name);
 
 	/**
-	 * Find a library by name + version.
-	 * @param library The name of the library to find.
-	 * @param version The prefered version of the library.
-	 * @return The library if found, NULL otherwise.
-	 */
-	AILibrary *FindLibrary(const char *library, int version);
-
-	/**
-	 * Register a library to be put in the available list.
-	 */
-	void RegisterLibrary(class AILibrary *library);
-
-	/**
-	 * Register an AI to be put in the available list.
-	 */
-	void RegisterAI(class AIInfo *info);
-
-	/**
-	 * Register the dummy AI.
-	 * @param info The dummy AI that.
-	 */
-	void SetDummyAI(class AIInfo *info) { this->info_dummy = info; }
-
-	/**
-	 * Select a Random AI.
+	 * Select a random AI.
+	 * @return A random AI from the pool.
 	 */
 	class AIInfo *SelectRandomAI() const;
 
 	/**
-	 * Find an AI by name.
-	 */
-	class AIInfo *FindInfo(const char *name, int version, bool force_exact_match);
-
-	/**
-	 * Get the list of available AIs for the console.
-	 */
-	char *GetAIConsoleList(char *p, const char *last, bool newest_only) const;
-
-	/**
-	 * Get the list of available AI Libraries for the console.
+	 * Check if we have an AI by name and version available in our list.
+	 * @param nameParam The name of the AI.
+	 * @param versionParam The versionof the AI, or -1 if you want the latest.
+	 * @param force_exact_match Only match name+version, never latest.
+	 * @return NULL if no match found, otherwise the AI that matched.
 	 */
-	char *GetAIConsoleLibraryList(char *p, const char *last) const;
-
-	/**
-	 * Get the list of all registered AIs.
-	 */
-	const AIInfoList *GetAIInfoList() { return &this->info_list; }
-
-	/**
-	 * Get the list of the newest version of all registered AIs.
-	 */
-	const AIInfoList *GetUniqueAIInfoList() { return &this->info_single_list; }
+	class AIInfo *FindInfo(const char *nameParam, int versionParam, bool force_exact_match);
 
 	/**
-	 * Rescan the AI dir for scripts.
+	 * Set the Dummy AI.
 	 */
-	void RescanAIDir();
+	void SetDummyAI(class AIInfo *info);
 
-#if defined(ENABLE_NETWORK)
-	bool HasAI(const struct ContentInfo *ci, bool md5sum);
-#endif
+protected:
+	/* virtual */ void GetScriptName(ScriptInfo *info, char *name, int len);
+	/* virtual */ const char *GetFileName() const { return PATHSEP "info.nut"; }
+	/* virtual */ Subdirectory GetDirectory() const { return AI_DIR; }
+	/* virtual */ const char *GetScannerName() const { return "AIs"; }
+	/* virtual */ void RegisterAPI(class Squirrel *engine);
+
 private:
-	typedef std::map<const char *, class AILibrary *, StringCompare> AILibraryList; ///< Type for the list of libraries.
+	AIInfo *info_dummy; ///< The dummy AI.
+};
 
+class AIScannerLibrary : public ScriptScanner {
+public:
 	/**
-	 * Scan the AI dir for scripts.
-	 */
-	void ScanAIDir();
-
-	/**
-	 * Reset all allocated lists.
+	 * Find a library in the pool.
+	 * @param library The library name to find.
+	 * @param version The version the library should have.
+	 * @return The library if found, NULL otherwise.
 	 */
-	void Reset();
+	class AILibrary *FindLibrary(const char *library, int version);
 
-	AIInfo *info_dummy;          ///< The dummy AI.
-	AIInfoList info_list;        ///< The list of all AIs.
-	AIInfoList info_single_list; ///< The list of all unique AIs, based on shortname. The best AI (highest version) is shown.
-	AILibraryList library_list;  ///< The list of libraries.
+protected:
+	/* virtual */ void GetScriptName(ScriptInfo *info, char *name, int len);
+	/* virtual */ const char *GetFileName() const { return PATHSEP "library.nut"; }
+	/* virtual */ Subdirectory GetDirectory() const { return AI_LIBRARY_DIR; }
+	/* virtual */ const char *GetScannerName() const { return "AI Libraries"; }
+	/* virtual */ void RegisterAPI(class Squirrel *engine);
 };
 
 #endif /* AI_SCANNER_HPP */
--- a/src/network/network_content.cpp
+++ b/src/network/network_content.cpp
@@ -97,8 +97,11 @@
 			break;
 
 		case CONTENT_TYPE_AI:
+			proc = AI::HasAI; break;
+			break;
+
 		case CONTENT_TYPE_AI_LIBRARY:
-			proc = AI::HasAI; break;
+			proc = AI::HasAILibrary; break;
 			break;
 
 		case CONTENT_TYPE_SCENARIO:
--- a/src/script/script_info.cpp
+++ b/src/script/script_info.cpp
@@ -7,7 +7,7 @@
  * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
  */
 
-/** @file script_info.cpp Implementation of ScriptFileInfo. */
+/** @file script_info.cpp Implementation of ScriptInfo. */
 
 #include "../stdafx.h"
 
@@ -21,7 +21,7 @@
 /** Number of operations to create an instance of a script. */
 static const int MAX_CREATEINSTANCE_OPS = 100000;
 
-ScriptFileInfo::~ScriptFileInfo()
+ScriptInfo::~ScriptInfo()
 {
 	free(this->author);
 	free(this->name);
@@ -35,7 +35,7 @@
 	free(this->SQ_instance);
 }
 
-bool ScriptFileInfo::CheckMethod(const char *name) const
+bool ScriptInfo::CheckMethod(const char *name) const
 {
 	if (!this->engine->MethodExists(*this->SQ_instance, name)) {
 		char error[1024];
@@ -46,16 +46,18 @@
 	return true;
 }
 
-/* static */ SQInteger ScriptFileInfo::Constructor(HSQUIRRELVM vm, ScriptFileInfo *info)
+/* static */ SQInteger ScriptInfo::Constructor(HSQUIRRELVM vm, ScriptInfo *info)
 {
 	/* Set some basic info from the parent */
 	info->SQ_instance = MallocT<SQObject>(1);
 	Squirrel::GetInstance(vm, info->SQ_instance, 2);
 	/* Make sure the instance stays alive over time */
 	sq_addref(vm, info->SQ_instance);
-	ScriptScanner *scanner = (ScriptScanner *)Squirrel::GetGlobalPointer(vm);
-	info->engine = scanner->GetEngine();
 
+	info->scanner = (ScriptScanner *)Squirrel::GetGlobalPointer(vm);
+	info->engine = info->scanner->GetEngine();
+
+	/* Ensure the mandatory functions exist */
 	static const char * const required_functions[] = {
 		"GetAuthor",
 		"GetName",
@@ -69,8 +71,9 @@
 		if (!info->CheckMethod(required_functions[i])) return SQ_ERROR;
 	}
 
-	info->main_script = strdup(scanner->GetMainScript());
-	const char *tar_name = scanner->GetTarFile();
+	/* Get location information of the scanner */
+	info->main_script = strdup(info->scanner->GetMainScript());
+	const char *tar_name = info->scanner->GetTarFile();
 	if (tar_name != NULL) info->tar_file = strdup(tar_name);
 
 	/* Cache the data the info file gives us. */
--- a/src/script/script_info.hpp
+++ b/src/script/script_info.hpp
@@ -15,9 +15,9 @@
 #include <squirrel.h>
 #include "../misc/countedptr.hpp"
 
-class ScriptFileInfo : public SimpleCountedObject {
+class ScriptInfo : public SimpleCountedObject {
 public:
-	ScriptFileInfo() :
+	ScriptInfo() :
 		SQ_instance(NULL),
 		main_script(NULL),
 		tar_file(NULL),
@@ -28,9 +28,10 @@
 		date(NULL),
 		instance_name(NULL),
 		version(0),
-		url(NULL)
+		url(NULL),
+		scanner(NULL)
 	{}
-	~ScriptFileInfo();
+	~ScriptInfo();
 
 	/**
 	 * Get the Author of the script.
@@ -90,22 +91,30 @@
 	/**
 	 * Process the creation of a FileInfo object.
 	 */
-	static SQInteger Constructor(HSQUIRRELVM vm, ScriptFileInfo *info);
+	static SQInteger Constructor(HSQUIRRELVM vm, ScriptInfo *info);
+
+	/**
+	 * Get the scanner which has found this ScriptInfo.
+	 */
+	virtual class ScriptScanner *GetScanner() { return this->scanner; }
 
 protected:
-	class Squirrel *engine;
-	HSQOBJECT *SQ_instance;
+	class Squirrel *engine;       ///< Engine used to register for Squirrel.
+	HSQOBJECT *SQ_instance;       ///< The Squirrel instance created for this info.
+
 private:
-	char *main_script;
-	char *tar_file;
-	const char *author;
-	const char *name;
-	const char *short_name;
-	const char *description;
-	const char *date;
-	const char *instance_name;
-	int version;
-	const char *url;
+	char *main_script;            ///< Name of the main script.
+	char *tar_file;               ///< If, which tar file the script was in.
+	const char *author;           ///< Author of the script.
+	const char *name;             ///< Full name of the script.
+	const char *short_name;       ///< Short name (4 chars) which uniquely identifies the script.
+	const char *description;      ///< Small description of the script.
+	const char *date;             ///< The date the script was written at.
+	const char *instance_name;    ///< Which instance name the script has.
+	int version;                  ///< Version of the script.
+	const char *url;              ///< URL of the script.
+
+	class ScriptScanner *scanner; ///< ScriptScanner object that was used to scan this script info.
 };
 
 #endif /* SCRIPT_INFO_HPP */
--- a/src/script/script_scanner.cpp
+++ b/src/script/script_scanner.cpp
@@ -10,12 +10,14 @@
 /** @file script_scanner.cpp Allows scanning for scripts. */
 
 #include "../stdafx.h"
+#include "../debug.h"
 #include "../string_func.h"
 #include "../fileio_func.h"
 #include <sys/stat.h>
 
 #include "../script/squirrel.hpp"
 #include "script_scanner.hpp"
+#include "script_info.hpp"
 
 bool ScriptScanner::AddFile(const char *filename, size_t basepath_length, const char *tar_filename)
 {
@@ -50,18 +52,222 @@
 	return true;
 }
 
-ScriptScanner::ScriptScanner()
+ScriptScanner::ScriptScanner() :
+	engine(NULL),
+	main_script(NULL),
+	tar_file(NULL)
 {
-	this->engine = new Squirrel("Scanner");
+}
+
+void ScriptScanner::Initialize(const char *name)
+{
+	this->engine = new Squirrel(name);
 
 	/* Mark this class as global pointer */
 	this->engine->SetGlobalPointer(this);
-	this->main_script = NULL;
-	this->tar_file = NULL;
+
+	this->RegisterAPI(this->engine);
+	this->RescanDir();
+
+	this->engine->ResetCrashed();
 }
 
 ScriptScanner::~ScriptScanner()
 {
+	this->Reset();
+
 	free(this->main_script);
 	delete this->engine;
 }
+
+void ScriptScanner::RescanDir()
+{
+	/* Forget about older scans */
+	this->Reset();
+
+	/* Scan for scripts */
+	this->Scan(this->GetFileName(), this->GetDirectory());
+}
+
+void ScriptScanner::Reset()
+{
+	ScriptInfoList::iterator it = this->info_list.begin();
+	for (; it != this->info_list.end(); it++) {
+		free((*it).first);
+		delete (*it).second;
+	}
+	it = this->info_single_list.begin();
+	for (; it != this->info_single_list.end(); it++) {
+		free((*it).first);
+	}
+
+	this->info_list.clear();
+	this->info_single_list.clear();
+}
+
+void ScriptScanner::RegisterScript(ScriptInfo *info)
+{
+	char script_original_name[1024];
+	this->GetScriptName(info, script_original_name, sizeof(script_original_name));
+	strtolower(script_original_name);
+
+	char script_name[1024];
+	snprintf(script_name, sizeof(script_name), "%s.%d", script_original_name, info->GetVersion());
+
+	/* Check if GetShortName follows the rules */
+	if (strlen(info->GetShortName()) != 4) {
+		DEBUG(ai, 0, "The script '%s' returned a string from GetShortName() which is not four characaters. Unable to load the script.", info->GetName());
+		delete info;
+		return;
+	}
+
+	if (this->info_list.find(script_name) != this->info_list.end()) {
+		/* This script was already registered */
+#ifdef WIN32
+		/* Windows doesn't care about the case */
+		if (strcasecmp(this->info_list[script_name]->GetMainScript(), info->GetMainScript()) == 0) {
+#else
+		if (strcmp(this->info_list[script_name]->GetMainScript(), info->GetMainScript()) == 0) {
+#endif
+			delete info;
+			return;
+		}
+
+		DEBUG(ai, 1, "Registering two scripts with the same name and version");
+		DEBUG(ai, 1, "  1: %s", this->info_list[script_name]->GetMainScript());
+		DEBUG(ai, 1, "  2: %s", info->GetMainScript());
+		DEBUG(ai, 1, "The first is taking precedence.");
+
+		delete info;
+		return;
+	}
+
+	this->info_list[strdup(script_name)] = info;
+
+	/* Add the script to the 'unique' script list, where only the highest version
+	 *  of the script is registered. */
+	if (this->info_single_list.find(script_original_name) == this->info_single_list.end()) {
+		this->info_single_list[strdup(script_original_name)] = info;
+	} else if (this->info_single_list[script_original_name]->GetVersion() < info->GetVersion()) {
+		this->info_single_list[script_original_name] = info;
+	}
+}
+
+char *ScriptScanner::GetConsoleList(char *p, const char *last, bool newest_only) const
+{
+	p += seprintf(p, last, "List of %s:\n", this->GetScannerName());
+	const ScriptInfoList &list = newest_only ? this->info_single_list : this->info_list;
+	ScriptInfoList::const_iterator it = list.begin();
+	for (; it != list.end(); it++) {
+		ScriptInfo *i = (*it).second;
+		p += seprintf(p, last, "%10s (v%d): %s\n", i->GetName(), i->GetVersion(), i->GetDescription());
+	}
+	p += seprintf(p, last, "\n");
+
+	return p;
+}
+
+#if defined(ENABLE_NETWORK)
+#include "../network/network_content.h"
+#include "../3rdparty/md5/md5.h"
+#include "../tar_type.h"
+
+/** Helper for creating a MD5sum of all files within of a script. */
+struct ScriptFileChecksumCreator : FileScanner {
+	byte md5sum[16];  ///< The final md5sum.
+	Subdirectory dir; ///< The directory to look in.
+
+	/**
+	 * Initialise the md5sum to be all zeroes,
+	 * so we can easily xor the data.
+	 */
+	ScriptFileChecksumCreator(Subdirectory dir)
+	{
+		this->dir = dir;
+		memset(this->md5sum, 0, sizeof(this->md5sum));
+	}
+
+	/* Add the file and calculate the md5 sum. */
+	virtual bool AddFile(const char *filename, size_t basepath_length, const char *tar_filename)
+	{
+		Md5 checksum;
+		uint8 buffer[1024];
+		size_t len, size;
+		byte tmp_md5sum[16];
+
+		/* Open the file ... */
+		FILE *f = FioFOpenFile(filename, "rb", this->dir, &size);
+		if (f == NULL) return false;
+
+		/* ... calculate md5sum... */
+		while ((len = fread(buffer, 1, (size > sizeof(buffer)) ? sizeof(buffer) : size, f)) != 0 && size != 0) {
+			size -= len;
+			checksum.Append(buffer, len);
+		}
+		checksum.Finish(tmp_md5sum);
+
+		FioFCloseFile(f);
+
+		/* ... and xor it to the overall md5sum. */
+		for (uint i = 0; i < sizeof(md5sum); i++) this->md5sum[i] ^= tmp_md5sum[i];
+
+		return true;
+	}
+};
+
+/**
+ * Check whether the script given in info is the same as in ci based
+ * on the shortname and md5 sum.
+ * @param ci The information to compare to.
+ * @param md5sum Whether to check the MD5 checksum.
+ * @param info The script to get the shortname and md5 sum from.
+ * @return True iff they're the same.
+ */
+static bool IsSameScript(const ContentInfo *ci, bool md5sum, ScriptInfo *info, Subdirectory dir)
+{
+	uint32 id = 0;
+	const char *str = info->GetShortName();
+	for (int j = 0; j < 4 && *str != '\0'; j++, str++) id |= *str << (8 * j);
+
+	if (id != ci->unique_id) return false;
+	if (!md5sum) return true;
+
+	ScriptFileChecksumCreator checksum(dir);
+	const char *tar_filename = info->GetTarFile();
+	TarList::iterator iter;
+	if (tar_filename != NULL && (iter = _tar_list[dir].find(tar_filename)) != _tar_list[dir].end()) {
+		/* The main script is in a tar file, so find all files that
+		 * are in the same tar and add them to the MD5 checksumming. */
+		TarFileList::iterator tar;
+		FOR_ALL_TARS(tar, dir) {
+			/* Not in the same tar. */
+			if (tar->second.tar_filename != iter->first) continue;
+
+			/* Check the extension. */
+			const char *ext = strrchr(tar->first.c_str(), '.');
+			if (ext == NULL || strcasecmp(ext, ".nut") != 0) continue;
+
+			checksum.AddFile(tar->first.c_str(), 0, tar_filename);
+		}
+	} else {
+		char path[MAX_PATH];
+		strecpy(path, info->GetMainScript(), lastof(path));
+		/* There'll always be at least 1 path separator character in a script
+		 * main script name as the search algorithm requires the main script to
+		 * be in a subdirectory of the script directory; so <dir>/<path>/main.nut. */
+		*strrchr(path, PATHSEPCHAR) = '\0';
+		checksum.Scan(".nut", path);
+	}
+
+	return memcmp(ci->md5sum, checksum.md5sum, sizeof(ci->md5sum)) == 0;
+}
+
+bool ScriptScanner::HasScript(const ContentInfo *ci, bool md5sum)
+{
+	for (ScriptInfoList::iterator it = this->info_list.begin(); it != this->info_list.end(); it++) {
+		if (IsSameScript(ci, md5sum, (*it).second, this->GetDirectory())) return true;
+	}
+	return false;
+}
+
+#endif /* ENABLE_NETWORK */
--- a/src/script/script_scanner.hpp
+++ b/src/script/script_scanner.hpp
@@ -12,13 +12,23 @@
 #ifndef SCRIPT_SCANNER_HPP
 #define SCRIPT_SCANNER_HPP
 
+#include <map>
 #include "../fileio_func.h"
+#include "../core/string_compare_type.hpp"
+
+typedef std::map<const char *, class ScriptInfo *, StringCompare> ScriptInfoList; ///< Type for the list of scripts.
 
 /** Scanner to help finding scripts. */
 class ScriptScanner : public FileScanner {
 public:
 	ScriptScanner();
-	~ScriptScanner();
+	virtual ~ScriptScanner();
+
+	/**
+	 * Initialize the scanner.
+	 * @param name The name of the scanner ("AIScanner", "GSScanner", ..).
+	 */
+	virtual void Initialize(const char *name);
 
 	/**
 	 * Get the engine of the main squirrel handler (it indexes all available scripts).
@@ -35,12 +45,79 @@
 	 */
 	const char *GetTarFile() { return this->tar_file; }
 
+	/**
+	 * Get the list of all registered scripts.
+	 */
+	const ScriptInfoList *GetInfoList() { return &this->info_list; }
+
+	/**
+	 * Get the list of the latest version of all registered scripts.
+	 */
+	const ScriptInfoList *GetUniqueInfoList() { return &this->info_single_list; }
+
+	/**
+	 * Register a ScriptInfo to the scanner.
+	 */
+	void RegisterScript(class ScriptInfo *info);
+
+	/**
+	 * Get the list of registered scripts to print on the console.
+	 */
+	char *GetConsoleList(char *p, const char *last, bool newest_only) const;
+
+	/**
+	 * Check whether we have a script with the exact characteristics as ci.
+	 * @param ci The characteristics to search on (shortname and md5sum).
+	 * @param md5sum Whether to check the MD5 checksum.
+	 * @return True iff we have a script matching.
+	 */
+	bool HasScript(const struct ContentInfo *ci, bool md5sum);
+
 	/* virtual */ bool AddFile(const char *filename, size_t basepath_length, const char *tar_filename);
 
+	/**
+	 * Rescan the script dir.
+	 */
+	void RescanDir();
+
 protected:
 	class Squirrel *engine; ///< The engine we're scanning with.
 	char *main_script;      ///< The name of the current main script.
 	char *tar_file;         ///< The filename of the tar for the main script.
+
+	ScriptInfoList info_list;        ///< The list of all script.
+	ScriptInfoList info_single_list; ///< The list of all unique script. The best script (highest version) is shown.
+
+	/**
+	 * Get the script name how to store the script in memory.
+	 */
+	virtual void GetScriptName(ScriptInfo *info, char *name, int len) = 0;
+
+	/**
+	 * Get the filename to scan for this type of script.
+	 */
+	virtual const char *GetFileName() const = 0;
+
+	/**
+	 * Get the directory to scan in.
+	 */
+	virtual Subdirectory GetDirectory() const = 0;
+
+	/**
+	 * Register the API for this ScriptInfo.
+	 */
+	virtual void RegisterAPI(class Squirrel *engine) = 0;
+
+	/**
+	 * Get the type of the script, in plural.
+	 */
+	virtual const char *GetScannerName() const = 0;
+
+	/**
+	 * Reset all allocated lists.
+	 */
+	void Reset();
+
 };
 
 #endif /* SCRIPT_SCANNER_HPP */
--- a/src/script/squirrel.cpp
+++ b/src/script/squirrel.cpp
@@ -440,13 +440,19 @@
 SQRESULT Squirrel::LoadFile(HSQUIRRELVM vm, const char *filename, SQBool printerror)
 {
 	size_t size;
-	FILE *file = FioFOpenFile(filename, "rb", AI_DIR, &size);
-	if (file == NULL) file = FioFOpenFile(filename, "rb", AI_LIBRARY_DIR, &size);
+	FILE *file;
 	SQInteger ret;
 	unsigned short us;
 	unsigned char uc;
 	SQLEXREADFUNC func;
 
+	if (strncmp(this->GetAPIName(), "AI", 2) == 0) {
+		file = FioFOpenFile(filename, "rb", AI_DIR, &size);
+		if (file == NULL) file = FioFOpenFile(filename, "rb", AI_LIBRARY_DIR, &size);
+	} else {
+		NOT_REACHED();
+	}
+
 	if (file != NULL) {
 		SQFile f(file, size);
 		ret = fread(&us, 1, sizeof(us), file);
--- a/src/script/squirrel.hpp
+++ b/src/script/squirrel.hpp
@@ -67,7 +67,7 @@
 	static void ErrorPrintFunc(HSQUIRRELVM vm, const SQChar *s, ...);
 
 public:
-	friend class AIScanner;
+	friend class AIScannerInfo;
 	friend class ScriptInstance;
 	friend class ScriptController;
 	friend void squirrel_register_std(Squirrel *engine);