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/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 +- 6 files changed, 334 insertions(+), 33 deletions(-) (limited to 'src/script') 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