mirror of https://github.com/OpenTTD/OpenTTD
(svn r23362) -Codechange: refactor AIScanner, splitting it in AIScannerInfo and AIScannerLibrary
parent
ae8540f5e0
commit
e37149a1de
|
@ -19,7 +19,7 @@
|
||||||
#include <map>
|
#include <map>
|
||||||
|
|
||||||
/** A list that maps AI names to their AIInfo object. */
|
/** 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.
|
* Main AI class. Contains all functions needed to start, stop, save and load AIs.
|
||||||
|
@ -129,11 +129,11 @@ public:
|
||||||
/** Wrapper function for AIScanner::GetAIConsoleLibraryList */
|
/** Wrapper function for AIScanner::GetAIConsoleLibraryList */
|
||||||
static char *GetConsoleLibraryList(char *p, const char *last);
|
static char *GetConsoleLibraryList(char *p, const char *last);
|
||||||
/** Wrapper function for AIScanner::GetAIInfoList */
|
/** Wrapper function for AIScanner::GetAIInfoList */
|
||||||
static const AIInfoList *GetInfoList();
|
static const ScriptInfoList *GetInfoList();
|
||||||
/** Wrapper function for AIScanner::GetUniqueAIInfoList */
|
/** Wrapper function for AIScanner::GetUniqueAIInfoList */
|
||||||
static const AIInfoList *GetUniqueInfoList();
|
static const ScriptInfoList *GetUniqueInfoList();
|
||||||
/** Wrapper function for AIScanner::FindInfo */
|
/** 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 */
|
/** Wrapper function for AIScanner::FindLibrary */
|
||||||
static class AILibrary *FindLibrary(const char *library, int version);
|
static class AILibrary *FindLibrary(const char *library, int version);
|
||||||
|
|
||||||
|
@ -145,10 +145,12 @@ public:
|
||||||
#if defined(ENABLE_NETWORK)
|
#if defined(ENABLE_NETWORK)
|
||||||
/** Wrapper function for AIScanner::HasAI */
|
/** Wrapper function for AIScanner::HasAI */
|
||||||
static bool HasAI(const struct ContentInfo *ci, bool md5sum);
|
static bool HasAI(const struct ContentInfo *ci, bool md5sum);
|
||||||
|
static bool HasAILibrary(const ContentInfo *ci, bool md5sum);
|
||||||
#endif
|
#endif
|
||||||
private:
|
private:
|
||||||
static uint frame_counter; ///< Tick counter for the AI code
|
static uint frame_counter; ///< Tick counter for the AI code
|
||||||
static class AIScanner *ai_scanner; ///< AIScanner instance that is used to find AIs
|
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 */
|
#else /* ENABLE_AI */
|
||||||
|
@ -167,6 +169,7 @@ public:
|
||||||
static void KillAll() {}
|
static void KillAll() {}
|
||||||
static void GameLoop() {}
|
static void GameLoop() {}
|
||||||
static bool HasAI(const struct ContentInfo *ci, bool md5sum) { return false; }
|
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 void Rescan() {}
|
||||||
static char *GetConsoleList(char *p, const char *last, bool newest_only = false) { return p; }
|
static char *GetConsoleList(char *p, const char *last, bool newest_only = false) { return p; }
|
||||||
static void nop() { }
|
static void nop() { }
|
||||||
|
|
|
@ -21,10 +21,12 @@
|
||||||
#include "ai_scanner.hpp"
|
#include "ai_scanner.hpp"
|
||||||
#include "ai_instance.hpp"
|
#include "ai_instance.hpp"
|
||||||
#include "ai_config.hpp"
|
#include "ai_config.hpp"
|
||||||
|
#include "ai.hpp"
|
||||||
#include "../script/api/script_error.hpp"
|
#include "../script/api/script_error.hpp"
|
||||||
|
|
||||||
/* static */ uint AI::frame_counter = 0;
|
/* 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()
|
/* static */ bool AI::CanStartNew()
|
||||||
{
|
{
|
||||||
|
@ -42,7 +44,7 @@
|
||||||
AIConfig *config = AIConfig::GetConfig(company);
|
AIConfig *config = AIConfig::GetConfig(company);
|
||||||
AIInfo *info = config->GetInfo();
|
AIInfo *info = config->GetInfo();
|
||||||
if (info == NULL || (rerandomise_ai && config->IsRandomAI())) {
|
if (info == NULL || (rerandomise_ai && config->IsRandomAI())) {
|
||||||
info = AI::ai_scanner->SelectRandomAI();
|
info = AI::scanner_info->SelectRandomAI();
|
||||||
assert(info != NULL);
|
assert(info != NULL);
|
||||||
/* Load default data and store the name in the settings */
|
/* Load default data and store the name in the settings */
|
||||||
config->ChangeAI(info->GetName(), -1, false, true);
|
config->ChangeAI(info->GetName(), -1, false, true);
|
||||||
|
@ -134,12 +136,15 @@
|
||||||
|
|
||||||
/* static */ void AI::Initialize()
|
/* static */ void AI::Initialize()
|
||||||
{
|
{
|
||||||
if (AI::ai_scanner != NULL) AI::Uninitialize(true);
|
if (AI::scanner_info != NULL) AI::Uninitialize(true);
|
||||||
|
|
||||||
AI::frame_counter = 0;
|
AI::frame_counter = 0;
|
||||||
if (AI::ai_scanner == NULL) {
|
if (AI::scanner_info == NULL) {
|
||||||
TarScanner::DoScan(TarScanner::AI);
|
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 */
|
* still load all the AIS, while keeping the configs in place */
|
||||||
Rescan();
|
Rescan();
|
||||||
} else {
|
} else {
|
||||||
delete AI::ai_scanner;
|
delete AI::scanner_info;
|
||||||
AI::ai_scanner = NULL;
|
delete AI::scanner_library;
|
||||||
|
AI::scanner_info = NULL;
|
||||||
|
AI::scanner_library = NULL;
|
||||||
|
|
||||||
for (CompanyID c = COMPANY_FIRST; c < MAX_COMPANIES; c++) {
|
for (CompanyID c = COMPANY_FIRST; c < MAX_COMPANIES; c++) {
|
||||||
if (_settings_game.ai_config[c] != NULL) {
|
if (_settings_game.ai_config[c] != NULL) {
|
||||||
|
@ -286,42 +293,59 @@
|
||||||
|
|
||||||
/* static */ char *AI::GetConsoleList(char *p, const char *last, bool newest_only)
|
/* 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)
|
/* 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)
|
/* 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)
|
/* 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()
|
/* static */ void AI::Rescan()
|
||||||
{
|
{
|
||||||
TarScanner::DoScan(TarScanner::AI);
|
TarScanner::DoScan(TarScanner::AI);
|
||||||
|
|
||||||
AI::ai_scanner->RescanAIDir();
|
AI::scanner_info->RescanDir();
|
||||||
|
AI::scanner_library->RescanDir();
|
||||||
ResetConfig();
|
ResetConfig();
|
||||||
|
|
||||||
InvalidateWindowData(WC_AI_LIST, 0, 1);
|
InvalidateWindowData(WC_AI_LIST, 0, 1);
|
||||||
SetWindowClassesDirty(WC_AI_DEBUG);
|
SetWindowClassesDirty(WC_AI_DEBUG);
|
||||||
SetWindowDirty(WC_AI_SETTINGS, 0);
|
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);
|
||||||
|
}
|
||||||
|
|
|
@ -47,11 +47,11 @@ enum AIListWindowWidgets {
|
||||||
* Window that let you choose an available AI.
|
* Window that let you choose an available AI.
|
||||||
*/
|
*/
|
||||||
struct AIListWindow : public Window {
|
struct AIListWindow : public Window {
|
||||||
const AIInfoList *ai_info_list; ///< The list of AIs.
|
const ScriptInfoList *ai_info_list; ///< The list of AIs.
|
||||||
int selected; ///< The currently selected AI.
|
int selected; ///< The currently selected AI.
|
||||||
CompanyID slot; ///< The company we're selecting a new AI for.
|
CompanyID slot; ///< The company we're selecting a new AI for.
|
||||||
int line_height; ///< Height of a row in the matrix widget.
|
int line_height; ///< Height of a row in the matrix widget.
|
||||||
Scrollbar *vscroll; ///< Cache of the vertical scrollbar.
|
Scrollbar *vscroll; ///< Cache of the vertical scrollbar.
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructor for the window.
|
* Constructor for the window.
|
||||||
|
@ -74,7 +74,7 @@ struct AIListWindow : public Window {
|
||||||
if (AIConfig::GetConfig(slot)->HasAI()) {
|
if (AIConfig::GetConfig(slot)->HasAI()) {
|
||||||
AIInfo *info = AIConfig::GetConfig(slot)->GetInfo();
|
AIInfo *info = AIConfig::GetConfig(slot)->GetInfo();
|
||||||
int i = 0;
|
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) {
|
if ((*it).second == info) {
|
||||||
this->selected = i;
|
this->selected = i;
|
||||||
break;
|
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);
|
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;
|
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++) {
|
for (int i = 1; it != this->ai_info_list->end(); i++, it++) {
|
||||||
if (this->vscroll->IsVisible(i)) {
|
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);
|
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: {
|
case AIL_WIDGET_INFO_BG: {
|
||||||
AIInfo *selected_info = NULL;
|
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++) {
|
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. */
|
/* Some info about the currently selected AI. */
|
||||||
if (selected_info != NULL) {
|
if (selected_info != NULL) {
|
||||||
|
@ -150,7 +150,7 @@ struct AIListWindow : public Window {
|
||||||
if (this->selected == -1) {
|
if (this->selected == -1) {
|
||||||
AIConfig::GetConfig(slot)->ChangeAI(NULL);
|
AIConfig::GetConfig(slot)->ChangeAI(NULL);
|
||||||
} else {
|
} 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++;
|
for (int i = 0; i < this->selected; i++) it++;
|
||||||
AIConfig::GetConfig(slot)->ChangeAI((*it).second->GetName(), (*it).second->GetVersion());
|
AIConfig::GetConfig(slot)->ChangeAI((*it).second->GetName(), (*it).second->GetVersion());
|
||||||
}
|
}
|
||||||
|
|
|
@ -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/>.
|
* 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 "../stdafx.h"
|
||||||
|
|
||||||
#include "../script/squirrel_helper.hpp"
|
#include "../script/squirrel_helper.hpp"
|
||||||
|
#include "../script/squirrel_class.hpp"
|
||||||
#include "ai_info.hpp"
|
#include "ai_info.hpp"
|
||||||
#include "ai_scanner.hpp"
|
#include "ai_scanner.hpp"
|
||||||
#include "../settings_type.h"
|
#include "../settings_type.h"
|
||||||
#include "../debug.h"
|
#include "../debug.h"
|
||||||
#include "../rev.h"
|
#include "../rev.h"
|
||||||
|
#include "ai.hpp"
|
||||||
|
|
||||||
/** Maximum number of operations allowed for getting a particular setting. */
|
/** Maximum number of operations allowed for getting a particular setting. */
|
||||||
static const int MAX_GET_SETTING_OPS = 100000;
|
static const int MAX_GET_SETTING_OPS = 100000;
|
||||||
|
@ -37,20 +39,6 @@ AIConfigItem _start_date_config = {
|
||||||
NULL
|
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.
|
* Check if the API version provided by the AI is supported.
|
||||||
* @param api_version The API version as provided by the AI.
|
* @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;
|
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)
|
/* static */ SQInteger AIInfo::Constructor(HSQUIRRELVM vm)
|
||||||
{
|
{
|
||||||
/* Get the AIInfo */
|
/* 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"));
|
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;
|
AIInfo *info = (AIInfo *)instance;
|
||||||
|
|
||||||
SQInteger res = AIFileInfo::Constructor(vm, info);
|
SQInteger res = ScriptInfo::Constructor(vm, info);
|
||||||
if (res != 0) return res;
|
if (res != 0) return res;
|
||||||
|
|
||||||
AIConfigItem config = _start_date_config;
|
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() */
|
/* Remove the link to the real instance, else it might get deleted by RegisterAI() */
|
||||||
sq_setinstanceup(vm, 2, NULL);
|
sq_setinstanceup(vm, 2, NULL);
|
||||||
/* Register the AI to the base system */
|
/* Register the AI to the base system */
|
||||||
info->base->RegisterAI(info);
|
info->GetScanner()->RegisterScript(info);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -116,7 +127,7 @@ static bool CheckAPIVersion(const char *api_version)
|
||||||
AIInfo *info = (AIInfo *)instance;
|
AIInfo *info = (AIInfo *)instance;
|
||||||
info->api_version = NULL;
|
info->api_version = NULL;
|
||||||
|
|
||||||
SQInteger res = AIFileInfo::Constructor(vm, info);
|
SQInteger res = ScriptInfo::Constructor(vm, info);
|
||||||
if (res != 0) return res;
|
if (res != 0) return res;
|
||||||
|
|
||||||
char buf[8];
|
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() */
|
/* Remove the link to the real instance, else it might get deleted by RegisterAI() */
|
||||||
sq_setinstanceup(vm, 2, NULL);
|
sq_setinstanceup(vm, 2, NULL);
|
||||||
/* Register the AI to the base system */
|
/* Register the AI to the base system */
|
||||||
info->base->SetDummyAI(info);
|
static_cast<AIScannerInfo *>(info->GetScanner())->SetDummyAI(info);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -350,12 +361,26 @@ int AIInfo::GetSettingDefaultValue(const char *name) const
|
||||||
return -1;
|
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)
|
/* static */ SQInteger AILibrary::Constructor(HSQUIRRELVM vm)
|
||||||
{
|
{
|
||||||
/* Create a new AIFileInfo */
|
/* Create a new library */
|
||||||
AILibrary *library = new AILibrary();
|
AILibrary *library = new AILibrary();
|
||||||
|
|
||||||
SQInteger res = AIFileInfo::Constructor(vm, library);
|
SQInteger res = ScriptInfo::Constructor(vm, library);
|
||||||
if (res != 0) {
|
if (res != 0) {
|
||||||
delete library;
|
delete library;
|
||||||
return res;
|
return res;
|
||||||
|
@ -368,7 +393,7 @@ int AIInfo::GetSettingDefaultValue(const char *name) const
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Register the Library to the base system */
|
/* Register the Library to the base system */
|
||||||
library->base->RegisterLibrary(library);
|
library->GetScanner()->RegisterScript(library);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -49,24 +49,17 @@ extern AIConfigItem _start_date_config;
|
||||||
|
|
||||||
typedef std::list<AIConfigItem> AIConfigItemList; ///< List of AIConfig items.
|
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. */
|
/** All static information from an AI like name, version, etc. */
|
||||||
class AIInfo : public AIFileInfo {
|
class AIInfo : public ScriptInfo {
|
||||||
public:
|
public:
|
||||||
AIInfo();
|
AIInfo();
|
||||||
~AIInfo();
|
~AIInfo();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Register the functions of this class.
|
||||||
|
*/
|
||||||
|
static void RegisterAPI(Squirrel *engine);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create an AI, using this AIInfo as start-template.
|
* 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. */
|
/** All static information from an AI library like name, version, etc. */
|
||||||
class AILibrary : public AIFileInfo {
|
class AILibrary : public ScriptInfo {
|
||||||
public:
|
public:
|
||||||
AILibrary() : AIFileInfo(), category(NULL) {};
|
AILibrary() : ScriptInfo(), category(NULL) {};
|
||||||
~AILibrary();
|
~AILibrary();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Register the functions of this class.
|
||||||
|
*/
|
||||||
|
static void RegisterAPI(Squirrel *engine);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create an AI, using this AIInfo as start-template.
|
* Create an AI, using this AIInfo as start-template.
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -20,177 +20,50 @@
|
||||||
#include "ai_scanner.hpp"
|
#include "ai_scanner.hpp"
|
||||||
#include "../script/api/script_controller.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"; }
|
AIScannerInfo::AIScannerInfo() :
|
||||||
|
|
||||||
AIScanner::AIScanner() :
|
|
||||||
ScriptScanner(),
|
ScriptScanner(),
|
||||||
info_dummy(NULL)
|
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 */
|
void AIScannerInfo::Initialize(const char *name)
|
||||||
this->engine->AddClassBegin("AILibrary");
|
{
|
||||||
this->engine->AddClassEnd();
|
ScriptScanner::Initialize(name);
|
||||||
this->engine->AddMethod("RegisterLibrary", &AILibrary::Constructor, 2, "tx");
|
|
||||||
|
|
||||||
/* Scan the AI dir for scripts */
|
|
||||||
this->RescanAIDir();
|
|
||||||
|
|
||||||
/* Create the dummy AI */
|
/* Create the dummy AI */
|
||||||
this->engine->ResetCrashed();
|
|
||||||
|
|
||||||
free(this->main_script);
|
free(this->main_script);
|
||||||
this->main_script = strdup("%_dummy");
|
this->main_script = strdup("%_dummy");
|
||||||
extern void AI_CreateAIInfoDummy(HSQUIRRELVM vm);
|
extern void AI_CreateAIInfoDummy(HSQUIRRELVM vm);
|
||||||
AI_CreateAIInfoDummy(this->engine->GetVM());
|
AI_CreateAIInfoDummy(this->engine->GetVM());
|
||||||
}
|
}
|
||||||
|
|
||||||
void AIScanner::Reset()
|
void AIScannerInfo::SetDummyAI(class AIInfo *info)
|
||||||
{
|
{
|
||||||
AIInfoList::iterator it = this->info_list.begin();
|
this->info_dummy = info;
|
||||||
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();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
AIScanner::~AIScanner()
|
AIScannerInfo::~AIScannerInfo()
|
||||||
{
|
{
|
||||||
this->Reset();
|
|
||||||
|
|
||||||
delete this->info_dummy;
|
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' */
|
snprintf(name, len, "%s", info->GetName());
|
||||||
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::RegisterAPI(class Squirrel *engine)
|
||||||
{
|
{
|
||||||
char library_name[1024];
|
AIInfo::RegisterAPI(engine);
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void AIScanner::RegisterAI(AIInfo *info)
|
AIInfo *AIScannerInfo::SelectRandomAI() const
|
||||||
{
|
|
||||||
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
|
|
||||||
{
|
{
|
||||||
uint num_random_ais = 0;
|
uint num_random_ais = 0;
|
||||||
for (AIInfoList::const_iterator it = this->info_single_list.begin(); it != this->info_single_list.end(); it++) {
|
for (ScriptInfoList::const_iterator it = this->info_single_list.begin(); it != this->info_single_list.end(); it++) {
|
||||||
if (it->second->UseAsRandomAI()) num_random_ais++;
|
AIInfo *i = static_cast<AIInfo *>((*it).second);
|
||||||
|
if (i->UseAsRandomAI()) num_random_ais++;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (num_random_ais == 0) {
|
if (num_random_ais == 0) {
|
||||||
|
@ -207,16 +80,17 @@ AIInfo *AIScanner::SelectRandomAI() const
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Find the Nth item from the array */
|
/* Find the Nth item from the array */
|
||||||
AIInfoList::const_iterator it = this->info_single_list.begin();
|
ScriptInfoList::const_iterator it = this->info_single_list.begin();
|
||||||
while (!it->second->UseAsRandomAI()) it++;
|
AIInfo *i = static_cast<AIInfo *>((*it).second);
|
||||||
|
while (!i->UseAsRandomAI()) it++;
|
||||||
for (; pos > 0; pos--) {
|
for (; pos > 0; pos--) {
|
||||||
it++;
|
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 (this->info_list.size() == 0) return NULL;
|
||||||
if (nameParam == NULL) return NULL;
|
if (nameParam == NULL) return NULL;
|
||||||
|
@ -230,7 +104,7 @@ AIInfo *AIScanner::FindInfo(const char *nameParam, int versionParam, bool force_
|
||||||
|
|
||||||
if (versionParam == -1) {
|
if (versionParam == -1) {
|
||||||
/* We want to load the latest version of this AI; so find it */
|
/* 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 */
|
/* If we didn't find a match AI, maybe the user included a version */
|
||||||
char *e = strrchr(ai_name, '.');
|
char *e = strrchr(ai_name, '.');
|
||||||
|
@ -246,177 +120,45 @@ AIInfo *AIScanner::FindInfo(const char *nameParam, int versionParam, bool force_
|
||||||
char ai_name_tmp[1024];
|
char ai_name_tmp[1024];
|
||||||
snprintf(ai_name_tmp, sizeof(ai_name_tmp), "%s.%d", ai_name, versionParam);
|
snprintf(ai_name_tmp, sizeof(ai_name_tmp), "%s.%d", ai_name, versionParam);
|
||||||
strtolower(ai_name_tmp);
|
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
|
/* See if there is a compatible AI which goes by that name, with the highest
|
||||||
* version which allows loading the requested version */
|
* 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++) {
|
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();
|
version = (*it).second->GetVersion();
|
||||||
info = (*it).second;
|
info = i;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return info;
|
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;
|
void AIScannerLibrary::GetScriptName(ScriptInfo *info, char *name, int len)
|
||||||
|
{
|
||||||
|
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");
|
AILibrary::RegisterAPI(engine);
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#if defined(ENABLE_NETWORK)
|
AILibrary *AIScannerLibrary::FindLibrary(const char *library, int version)
|
||||||
#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)
|
|
||||||
{
|
{
|
||||||
uint32 id = 0;
|
/* Internally we store libraries as 'library.version' */
|
||||||
const char *str = info->GetShortName();
|
char library_name[1024];
|
||||||
for (int j = 0; j < 4 && *str != '\0'; j++, str++) id |= *str << (8 * j);
|
snprintf(library_name, sizeof(library_name), "%s.%d", library, version);
|
||||||
|
strtolower(library_name);
|
||||||
|
|
||||||
if (id != ci->unique_id) return false;
|
/* Check if the library + version exists */
|
||||||
if (!md5sum) return true;
|
ScriptInfoList::iterator iter = this->info_list.find(library_name);
|
||||||
|
if (iter == this->info_list.end()) return NULL;
|
||||||
|
|
||||||
AIFileChecksumCreator checksum;
|
return static_cast<AILibrary *>((*iter).second);
|
||||||
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 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();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 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 */
|
|
||||||
|
|
|
@ -13,95 +13,61 @@
|
||||||
#define AI_SCANNER_HPP
|
#define AI_SCANNER_HPP
|
||||||
|
|
||||||
#include "../script/script_scanner.hpp"
|
#include "../script/script_scanner.hpp"
|
||||||
#include "ai.hpp"
|
|
||||||
|
|
||||||
/**
|
class AIScannerInfo : public ScriptScanner {
|
||||||
* Class that scans for available AIs.
|
|
||||||
*/
|
|
||||||
class AIScanner : public ScriptScanner {
|
|
||||||
public:
|
public:
|
||||||
AIScanner();
|
AIScannerInfo();
|
||||||
~AIScanner();
|
~AIScannerInfo();
|
||||||
|
|
||||||
|
/* virtual */ void Initialize(const char *name);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Find a library by name + version.
|
* Select a random AI.
|
||||||
* @param library The name of the library to find.
|
* @return A random AI from the pool.
|
||||||
* @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.
|
|
||||||
*/
|
*/
|
||||||
class AIInfo *SelectRandomAI() const;
|
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);
|
||||||
|
|
||||||
/**
|
protected:
|
||||||
* Get the list of available AI Libraries for the console.
|
/* virtual */ void GetScriptName(ScriptInfo *info, char *name, int len);
|
||||||
*/
|
/* virtual */ const char *GetFileName() const { return PATHSEP "info.nut"; }
|
||||||
char *GetAIConsoleLibraryList(char *p, const char *last) const;
|
/* 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:
|
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.
|
* 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 ScanAIDir();
|
class AILibrary *FindLibrary(const char *library, int version);
|
||||||
|
|
||||||
/**
|
protected:
|
||||||
* Reset all allocated lists.
|
/* virtual */ void GetScriptName(ScriptInfo *info, char *name, int len);
|
||||||
*/
|
/* virtual */ const char *GetFileName() const { return PATHSEP "library.nut"; }
|
||||||
void Reset();
|
/* virtual */ Subdirectory GetDirectory() const { return AI_LIBRARY_DIR; }
|
||||||
|
/* virtual */ const char *GetScannerName() const { return "AI Libraries"; }
|
||||||
AIInfo *info_dummy; ///< The dummy AI.
|
/* virtual */ void RegisterAPI(class Squirrel *engine);
|
||||||
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.
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif /* AI_SCANNER_HPP */
|
#endif /* AI_SCANNER_HPP */
|
||||||
|
|
|
@ -97,10 +97,13 @@ bool ClientNetworkContentSocketHandler::Receive_SERVER_INFO(Packet *p)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case CONTENT_TYPE_AI:
|
case CONTENT_TYPE_AI:
|
||||||
case CONTENT_TYPE_AI_LIBRARY:
|
|
||||||
proc = AI::HasAI; break;
|
proc = AI::HasAI; break;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case CONTENT_TYPE_AI_LIBRARY:
|
||||||
|
proc = AI::HasAILibrary; break;
|
||||||
|
break;
|
||||||
|
|
||||||
case CONTENT_TYPE_SCENARIO:
|
case CONTENT_TYPE_SCENARIO:
|
||||||
case CONTENT_TYPE_HEIGHTMAP:
|
case CONTENT_TYPE_HEIGHTMAP:
|
||||||
proc = HasScenario;
|
proc = HasScenario;
|
||||||
|
|
|
@ -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/>.
|
* 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"
|
#include "../stdafx.h"
|
||||||
|
|
||||||
|
@ -21,7 +21,7 @@ static const int MAX_GET_OPS = 1000;
|
||||||
/** Number of operations to create an instance of a script. */
|
/** Number of operations to create an instance of a script. */
|
||||||
static const int MAX_CREATEINSTANCE_OPS = 100000;
|
static const int MAX_CREATEINSTANCE_OPS = 100000;
|
||||||
|
|
||||||
ScriptFileInfo::~ScriptFileInfo()
|
ScriptInfo::~ScriptInfo()
|
||||||
{
|
{
|
||||||
free(this->author);
|
free(this->author);
|
||||||
free(this->name);
|
free(this->name);
|
||||||
|
@ -35,7 +35,7 @@ ScriptFileInfo::~ScriptFileInfo()
|
||||||
free(this->SQ_instance);
|
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)) {
|
if (!this->engine->MethodExists(*this->SQ_instance, name)) {
|
||||||
char error[1024];
|
char error[1024];
|
||||||
|
@ -46,16 +46,18 @@ bool ScriptFileInfo::CheckMethod(const char *name) const
|
||||||
return true;
|
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 */
|
/* Set some basic info from the parent */
|
||||||
info->SQ_instance = MallocT<SQObject>(1);
|
info->SQ_instance = MallocT<SQObject>(1);
|
||||||
Squirrel::GetInstance(vm, info->SQ_instance, 2);
|
Squirrel::GetInstance(vm, info->SQ_instance, 2);
|
||||||
/* Make sure the instance stays alive over time */
|
/* Make sure the instance stays alive over time */
|
||||||
sq_addref(vm, info->SQ_instance);
|
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[] = {
|
static const char * const required_functions[] = {
|
||||||
"GetAuthor",
|
"GetAuthor",
|
||||||
"GetName",
|
"GetName",
|
||||||
|
@ -69,8 +71,9 @@ bool ScriptFileInfo::CheckMethod(const char *name) const
|
||||||
if (!info->CheckMethod(required_functions[i])) return SQ_ERROR;
|
if (!info->CheckMethod(required_functions[i])) return SQ_ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
info->main_script = strdup(scanner->GetMainScript());
|
/* Get location information of the scanner */
|
||||||
const char *tar_name = scanner->GetTarFile();
|
info->main_script = strdup(info->scanner->GetMainScript());
|
||||||
|
const char *tar_name = info->scanner->GetTarFile();
|
||||||
if (tar_name != NULL) info->tar_file = strdup(tar_name);
|
if (tar_name != NULL) info->tar_file = strdup(tar_name);
|
||||||
|
|
||||||
/* Cache the data the info file gives us. */
|
/* Cache the data the info file gives us. */
|
||||||
|
|
|
@ -15,9 +15,9 @@
|
||||||
#include <squirrel.h>
|
#include <squirrel.h>
|
||||||
#include "../misc/countedptr.hpp"
|
#include "../misc/countedptr.hpp"
|
||||||
|
|
||||||
class ScriptFileInfo : public SimpleCountedObject {
|
class ScriptInfo : public SimpleCountedObject {
|
||||||
public:
|
public:
|
||||||
ScriptFileInfo() :
|
ScriptInfo() :
|
||||||
SQ_instance(NULL),
|
SQ_instance(NULL),
|
||||||
main_script(NULL),
|
main_script(NULL),
|
||||||
tar_file(NULL),
|
tar_file(NULL),
|
||||||
|
@ -28,9 +28,10 @@ public:
|
||||||
date(NULL),
|
date(NULL),
|
||||||
instance_name(NULL),
|
instance_name(NULL),
|
||||||
version(0),
|
version(0),
|
||||||
url(NULL)
|
url(NULL),
|
||||||
|
scanner(NULL)
|
||||||
{}
|
{}
|
||||||
~ScriptFileInfo();
|
~ScriptInfo();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the Author of the script.
|
* Get the Author of the script.
|
||||||
|
@ -90,22 +91,30 @@ public:
|
||||||
/**
|
/**
|
||||||
* Process the creation of a FileInfo object.
|
* 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:
|
protected:
|
||||||
class Squirrel *engine;
|
class Squirrel *engine; ///< Engine used to register for Squirrel.
|
||||||
HSQOBJECT *SQ_instance;
|
HSQOBJECT *SQ_instance; ///< The Squirrel instance created for this info.
|
||||||
|
|
||||||
private:
|
private:
|
||||||
char *main_script;
|
char *main_script; ///< Name of the main script.
|
||||||
char *tar_file;
|
char *tar_file; ///< If, which tar file the script was in.
|
||||||
const char *author;
|
const char *author; ///< Author of the script.
|
||||||
const char *name;
|
const char *name; ///< Full name of the script.
|
||||||
const char *short_name;
|
const char *short_name; ///< Short name (4 chars) which uniquely identifies the script.
|
||||||
const char *description;
|
const char *description; ///< Small description of the script.
|
||||||
const char *date;
|
const char *date; ///< The date the script was written at.
|
||||||
const char *instance_name;
|
const char *instance_name; ///< Which instance name the script has.
|
||||||
int version;
|
int version; ///< Version of the script.
|
||||||
const char *url;
|
const char *url; ///< URL of the script.
|
||||||
|
|
||||||
|
class ScriptScanner *scanner; ///< ScriptScanner object that was used to scan this script info.
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif /* SCRIPT_INFO_HPP */
|
#endif /* SCRIPT_INFO_HPP */
|
||||||
|
|
|
@ -10,12 +10,14 @@
|
||||||
/** @file script_scanner.cpp Allows scanning for scripts. */
|
/** @file script_scanner.cpp Allows scanning for scripts. */
|
||||||
|
|
||||||
#include "../stdafx.h"
|
#include "../stdafx.h"
|
||||||
|
#include "../debug.h"
|
||||||
#include "../string_func.h"
|
#include "../string_func.h"
|
||||||
#include "../fileio_func.h"
|
#include "../fileio_func.h"
|
||||||
#include <sys/stat.h>
|
#include <sys/stat.h>
|
||||||
|
|
||||||
#include "../script/squirrel.hpp"
|
#include "../script/squirrel.hpp"
|
||||||
#include "script_scanner.hpp"
|
#include "script_scanner.hpp"
|
||||||
|
#include "script_info.hpp"
|
||||||
|
|
||||||
bool ScriptScanner::AddFile(const char *filename, size_t basepath_length, const char *tar_filename)
|
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;
|
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 */
|
/* Mark this class as global pointer */
|
||||||
this->engine->SetGlobalPointer(this);
|
this->engine->SetGlobalPointer(this);
|
||||||
this->main_script = NULL;
|
|
||||||
this->tar_file = NULL;
|
this->RegisterAPI(this->engine);
|
||||||
|
this->RescanDir();
|
||||||
|
|
||||||
|
this->engine->ResetCrashed();
|
||||||
}
|
}
|
||||||
|
|
||||||
ScriptScanner::~ScriptScanner()
|
ScriptScanner::~ScriptScanner()
|
||||||
{
|
{
|
||||||
|
this->Reset();
|
||||||
|
|
||||||
free(this->main_script);
|
free(this->main_script);
|
||||||
delete this->engine;
|
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 */
|
||||||
|
|
|
@ -12,13 +12,23 @@
|
||||||
#ifndef SCRIPT_SCANNER_HPP
|
#ifndef SCRIPT_SCANNER_HPP
|
||||||
#define SCRIPT_SCANNER_HPP
|
#define SCRIPT_SCANNER_HPP
|
||||||
|
|
||||||
|
#include <map>
|
||||||
#include "../fileio_func.h"
|
#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. */
|
/** Scanner to help finding scripts. */
|
||||||
class ScriptScanner : public FileScanner {
|
class ScriptScanner : public FileScanner {
|
||||||
public:
|
public:
|
||||||
ScriptScanner();
|
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).
|
* 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; }
|
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);
|
/* virtual */ bool AddFile(const char *filename, size_t basepath_length, const char *tar_filename);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Rescan the script dir.
|
||||||
|
*/
|
||||||
|
void RescanDir();
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
class Squirrel *engine; ///< The engine we're scanning with.
|
class Squirrel *engine; ///< The engine we're scanning with.
|
||||||
char *main_script; ///< The name of the current main script.
|
char *main_script; ///< The name of the current main script.
|
||||||
char *tar_file; ///< The filename of the tar for the 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 */
|
#endif /* SCRIPT_SCANNER_HPP */
|
||||||
|
|
|
@ -440,13 +440,19 @@ static SQInteger _io_file_read(SQUserPointer file, SQUserPointer buf, SQInteger
|
||||||
SQRESULT Squirrel::LoadFile(HSQUIRRELVM vm, const char *filename, SQBool printerror)
|
SQRESULT Squirrel::LoadFile(HSQUIRRELVM vm, const char *filename, SQBool printerror)
|
||||||
{
|
{
|
||||||
size_t size;
|
size_t size;
|
||||||
FILE *file = FioFOpenFile(filename, "rb", AI_DIR, &size);
|
FILE *file;
|
||||||
if (file == NULL) file = FioFOpenFile(filename, "rb", AI_LIBRARY_DIR, &size);
|
|
||||||
SQInteger ret;
|
SQInteger ret;
|
||||||
unsigned short us;
|
unsigned short us;
|
||||||
unsigned char uc;
|
unsigned char uc;
|
||||||
SQLEXREADFUNC func;
|
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) {
|
if (file != NULL) {
|
||||||
SQFile f(file, size);
|
SQFile f(file, size);
|
||||||
ret = fread(&us, 1, sizeof(us), file);
|
ret = fread(&us, 1, sizeof(us), file);
|
||||||
|
|
|
@ -67,7 +67,7 @@ protected:
|
||||||
static void ErrorPrintFunc(HSQUIRRELVM vm, const SQChar *s, ...);
|
static void ErrorPrintFunc(HSQUIRRELVM vm, const SQChar *s, ...);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
friend class AIScanner;
|
friend class AIScannerInfo;
|
||||||
friend class ScriptInstance;
|
friend class ScriptInstance;
|
||||||
friend class ScriptController;
|
friend class ScriptController;
|
||||||
friend void squirrel_register_std(Squirrel *engine);
|
friend void squirrel_register_std(Squirrel *engine);
|
||||||
|
|
Loading…
Reference in New Issue