From e37149a1def6a0e63dda8e0964abf1b82316761a Mon Sep 17 00:00:00 2001 From: truebrain Date: Tue, 29 Nov 2011 23:21:52 +0000 Subject: (svn r23362) -Codechange: refactor AIScanner, splitting it in AIScannerInfo and AIScannerLibrary --- src/ai/ai.hpp | 15 +- src/ai/ai_core.cpp | 56 +++++-- src/ai/ai_gui.cpp | 20 +-- src/ai/ai_info.cpp | 69 +++++--- src/ai/ai_info.hpp | 28 ++-- src/ai/ai_scanner.cpp | 342 +++++----------------------------------- src/ai/ai_scanner.hpp | 106 +++++-------- src/network/network_content.cpp | 5 +- src/script/script_info.cpp | 19 ++- src/script/script_info.hpp | 43 +++-- src/script/script_scanner.cpp | 214 ++++++++++++++++++++++++- src/script/script_scanner.hpp | 79 +++++++++- src/script/squirrel.cpp | 10 +- src/script/squirrel.hpp | 2 +- 14 files changed, 535 insertions(+), 473 deletions(-) diff --git a/src/ai/ai.hpp b/src/ai/ai.hpp index af1888914..d8647be0c 100644 --- a/src/ai/ai.hpp +++ b/src/ai/ai.hpp @@ -19,7 +19,7 @@ #include /** A list that maps AI names to their AIInfo object. */ -typedef std::map AIInfoList; +typedef std::map ScriptInfoList; /** * Main AI class. Contains all functions needed to start, stop, save and load AIs. @@ -129,11 +129,11 @@ public: /** 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 @@ public: #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 @@ public: 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() { } diff --git a/src/ai/ai_core.cpp b/src/ai/ai_core.cpp index 462dac0e6..d6c0b7946 100644 --- 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); +} diff --git a/src/ai/ai_gui.cpp b/src/ai/ai_gui.cpp index 7041f8bd3..2ef7d5db9 100644 --- a/src/ai/ai_gui.cpp +++ b/src/ai/ai_gui.cpp @@ -47,11 +47,11 @@ enum AIListWindowWidgets { * 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 @@ struct AIListWindow : public Window { 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 @@ struct AIListWindow : public Window { 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 @@ struct AIListWindow : public Window { } 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((*it).second); } /* Some info about the currently selected AI. */ if (selected_info != NULL) { @@ -150,7 +150,7 @@ struct AIListWindow : public Window { 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()); } diff --git a/src/ai/ai_info.cpp b/src/ai/ai_info.cpp index a729cb482..a56064e01 100644 --- 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 . */ -/** @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 @@ AIConfigItem _start_date_config = { 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 @@ static bool CheckAPIVersion(const char *api_version) 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() { return "AIInfo"; } + +/* static */ void AIInfo::RegisterAPI(Squirrel *engine) +{ + /* Create the AIInfo class, and add the RegisterAI function */ + DefSQClass SQAIInfo("AIInfo"); + SQAIInfo.PreRegister(engine); + SQAIInfo.AddConstructor(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 @@ static bool CheckAPIVersion(const char *api_version) 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 @@ static bool CheckAPIVersion(const char *api_version) /* 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 @@ static bool CheckAPIVersion(const char *api_version) 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 @@ static bool CheckAPIVersion(const char *api_version) /* 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(info->GetScanner())->SetDummyAI(info); return 0; } @@ -350,12 +361,26 @@ int AIInfo::GetSettingDefaultValue(const char *name) const 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 @@ int AIInfo::GetSettingDefaultValue(const char *name) const } /* Register the Library to the base system */ - library->base->RegisterLibrary(library); + library->GetScanner()->RegisterScript(library); return 0; } diff --git a/src/ai/ai_info.hpp b/src/ai/ai_info.hpp index f3833fdac..82ca6f925 100644 --- a/src/ai/ai_info.hpp +++ b/src/ai/ai_info.hpp @@ -49,24 +49,17 @@ extern AIConfigItem _start_date_config; typedef std::list 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. */ @@ -130,11 +123,16 @@ private: }; /** 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. */ diff --git a/src/ai/ai_scanner.cpp b/src/ai/ai_scanner.cpp index 832b89887..3a296f00e 100644 --- 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() { return "AIInfo"; } -AIScanner::AIScanner() : +AIScannerInfo::AIScannerInfo() : ScriptScanner(), info_dummy(NULL) { - /* Create the AIInfo class, and add the RegisterAI function */ - DefSQClass SQAIInfo("AIInfo"); - SQAIInfo.PreRegister(engine); - SQAIInfo.AddConstructor(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) +void AIScannerInfo::GetScriptName(ScriptInfo *info, char *name, int len) { - /* 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; + snprintf(name, len, "%s", info->GetName()); } -void AIScanner::RegisterLibrary(AILibrary *library) +void AIScannerInfo::RegisterAPI(class Squirrel *engine) { - 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; + AIInfo::RegisterAPI(engine); } -void AIScanner::RegisterAI(AIInfo *info) -{ - 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 *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((*it).second); + if (i->UseAsRandomAI()) num_random_ais++; } if (num_random_ais == 0) { @@ -207,16 +80,17 @@ AIInfo *AIScanner::SelectRandomAI() const } /* 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((*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 @@ AIInfo *AIScanner::FindInfo(const char *nameParam, int versionParam, bool force_ 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(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 @@ AIInfo *AIScanner::FindInfo(const char *nameParam, int versionParam, bool force_ 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(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((*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 -{ - 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; -} -char *AIScanner::GetAIConsoleLibraryList(char *p, const char *last) const +void AIScannerLibrary::GetScriptName(ScriptInfo *info, char *name, int len) { - 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 *library = static_cast(info); + snprintf(name, len, "%s.%s", library->GetCategory(), library->GetInstanceName()); } -#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) +void AIScannerLibrary::RegisterAPI(class Squirrel *engine) { - 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; - - 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//main.nut. */ - *strrchr(path, PATHSEPCHAR) = '\0'; - checksum.Scan(".nut", path); - } - - return memcmp(ci->md5sum, checksum.md5sum, sizeof(ci->md5sum)) == 0; + AILibrary::RegisterAPI(engine); } -/** - * 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) +AILibrary *AIScannerLibrary::FindLibrary(const char *library, int version) { - 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; + /* Internally we store libraries as 'library.version' */ + char library_name[1024]; + snprintf(library_name, sizeof(library_name), "%s.%d", library, version); + strtolower(library_name); - default: - NOT_REACHED(); - } -} + /* 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. - */ -/* static */ bool AI::HasAI(const ContentInfo *ci, bool md5sum) -{ - return AI::ai_scanner->HasAI(ci, md5sum); + return static_cast((*iter).second); } - -#endif /* ENABLE_NETWORK */ diff --git a/src/ai/ai_scanner.hpp b/src/ai/ai_scanner.hpp index cbc778069..2f55c8594 100644 --- 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(); - - /** - * 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); + AIScannerInfo(); + ~AIScannerInfo(); - /** - * 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; } + /* virtual */ void Initialize(const char *name); /** - * Select a Random AI. + * Select a random AI. + * @return A random AI from the pool. */ class AIInfo *SelectRandomAI() const; /** - * Find an AI by name. + * 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. */ - class AIInfo *FindInfo(const char *name, int version, bool force_exact_match); + class AIInfo *FindInfo(const char *nameParam, int versionParam, bool force_exact_match); /** - * Get the list of available AIs for the console. + * Set the Dummy AI. */ - char *GetAIConsoleList(char *p, const char *last, bool newest_only) const; + void SetDummyAI(class AIInfo *info); - /** - * Get the list of available AI Libraries for the console. - */ - char *GetAIConsoleLibraryList(char *p, const char *last) const; +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); - /** - * 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; } - - /** - * Rescan the AI dir for scripts. - */ - void RescanAIDir(); - -#if defined(ENABLE_NETWORK) - bool HasAI(const struct ContentInfo *ci, bool md5sum); -#endif private: - typedef std::map AILibraryList; ///< Type for the list of libraries. - - /** - * Scan the AI dir for scripts. - */ - void ScanAIDir(); + AIInfo *info_dummy; ///< The dummy AI. +}; +class AIScannerLibrary : public ScriptScanner { +public: /** - * 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(); - - 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. + class AILibrary *FindLibrary(const char *library, int version); + +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 */ diff --git a/src/network/network_content.cpp b/src/network/network_content.cpp index d1a04c04e..de7087c1e 100644 --- a/src/network/network_content.cpp +++ b/src/network/network_content.cpp @@ -97,10 +97,13 @@ bool ClientNetworkContentSocketHandler::Receive_SERVER_INFO(Packet *p) break; case CONTENT_TYPE_AI: - case CONTENT_TYPE_AI_LIBRARY: proc = AI::HasAI; break; break; + case CONTENT_TYPE_AI_LIBRARY: + proc = AI::HasAILibrary; break; + break; + case CONTENT_TYPE_SCENARIO: case CONTENT_TYPE_HEIGHTMAP: proc = HasScenario; diff --git a/src/script/script_info.cpp b/src/script/script_info.cpp index 8255bc04b..862907c8b 100644 --- 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 . */ -/** @file script_info.cpp Implementation of ScriptFileInfo. */ +/** @file script_info.cpp Implementation of ScriptInfo. */ #include "../stdafx.h" @@ -21,7 +21,7 @@ static const int MAX_GET_OPS = 1000; /** 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 @@ ScriptFileInfo::~ScriptFileInfo() 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 @@ bool ScriptFileInfo::CheckMethod(const char *name) const 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(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 @@ bool ScriptFileInfo::CheckMethod(const char *name) const 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. */ diff --git a/src/script/script_info.hpp b/src/script/script_info.hpp index ad30bf756..c19478116 100644 --- a/src/script/script_info.hpp +++ b/src/script/script_info.hpp @@ -15,9 +15,9 @@ #include #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 @@ public: 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 @@ public: /** * 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 */ diff --git a/src/script/script_scanner.cpp b/src/script/script_scanner.cpp index 049790196..57e412150 100644 --- 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 #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 @@ bool ScriptScanner::AddFile(const char *filename, size_t basepath_length, const 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 //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 */ diff --git a/src/script/script_scanner.hpp b/src/script/script_scanner.hpp index f50d05747..ac9b25fdc 100644 --- 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 #include "../fileio_func.h" +#include "../core/string_compare_type.hpp" + +typedef std::map 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 @@ public: */ 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 */ diff --git a/src/script/squirrel.cpp b/src/script/squirrel.cpp index 837e86987..9388c45b4 100644 --- a/src/script/squirrel.cpp +++ b/src/script/squirrel.cpp @@ -440,13 +440,19 @@ static SQInteger _io_file_read(SQUserPointer file, SQUserPointer buf, SQInteger 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); diff --git a/src/script/squirrel.hpp b/src/script/squirrel.hpp index e8d6de070..6b9c3ab70 100644 --- a/src/script/squirrel.hpp +++ b/src/script/squirrel.hpp @@ -67,7 +67,7 @@ protected: 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); -- cgit v1.2.3-70-g09d2