00001
00002
00003
00004
00005
00006
00007
00008
00009
00012 #include "../stdafx.h"
00013 #include "../debug.h"
00014 #include "../fileio_func.h"
00015 #include "../network/network.h"
00016 #include "../core/random_func.hpp"
00017
00018 #include <squirrel.h>
00019 #include "../script/squirrel.hpp"
00020 #include "../script/squirrel_helper.hpp"
00021 #include "../script/squirrel_class.hpp"
00022 #include "ai.hpp"
00023 #include "ai_info.hpp"
00024 #include "ai_scanner.hpp"
00025 #include "api/ai_controller.hpp"
00026
00027 void AIScanner::RescanAIDir()
00028 {
00029 this->ScanScriptDir("info.nut", AI_DIR);
00030 this->ScanScriptDir("library.nut", AI_LIBRARY_DIR);
00031 }
00032
00033 AIScanner::AIScanner() :
00034 ScriptScanner(),
00035 info_dummy(NULL)
00036 {
00037
00038 DefSQClass <AIInfo> SQAIInfo("AIInfo");
00039 SQAIInfo.PreRegister(engine);
00040 SQAIInfo.AddConstructor<void (AIInfo::*)(), 1>(engine, "x");
00041 SQAIInfo.DefSQAdvancedMethod(this->engine, &AIInfo::AddSetting, "AddSetting");
00042 SQAIInfo.DefSQAdvancedMethod(this->engine, &AIInfo::AddLabels, "AddLabels");
00043 SQAIInfo.DefSQConst(engine, AICONFIG_RANDOM, "AICONFIG_RANDOM");
00044 SQAIInfo.DefSQConst(engine, AICONFIG_BOOLEAN, "AICONFIG_BOOLEAN");
00045 SQAIInfo.PostRegister(engine);
00046 this->engine->AddMethod("RegisterAI", &AIInfo::Constructor, 2, "tx");
00047 this->engine->AddMethod("RegisterDummyAI", &AIInfo::DummyConstructor, 2, "tx");
00048
00049
00050 this->engine->AddClassBegin("AILibrary");
00051 this->engine->AddClassEnd();
00052 this->engine->AddMethod("RegisterLibrary", &AILibrary::Constructor, 2, "tx");
00053
00054
00055 this->RescanAIDir();
00056
00057
00058 this->engine->ResetCrashed();
00059 strecpy(this->main_script, "%_dummy", lastof(this->main_script));
00060 extern void AI_CreateAIInfoDummy(HSQUIRRELVM vm);
00061 AI_CreateAIInfoDummy(this->engine->GetVM());
00062 }
00063
00064 AIScanner::~AIScanner()
00065 {
00066 AIInfoList::iterator it = this->info_list.begin();
00067 for (; it != this->info_list.end(); it++) {
00068 free((void *)(*it).first);
00069 delete (*it).second;
00070 }
00071 it = this->info_single_list.begin();
00072 for (; it != this->info_single_list.end(); it++) {
00073 free((void *)(*it).first);
00074 }
00075 AILibraryList::iterator lit = this->library_list.begin();
00076 for (; lit != this->library_list.end(); lit++) {
00077 free((void *)(*lit).first);
00078 delete (*lit).second;
00079 }
00080
00081 delete this->info_dummy;
00082 }
00083
00084 bool AIScanner::ImportLibrary(const char *library, const char *class_name, int version, HSQUIRRELVM vm, AIController *controller)
00085 {
00086
00087 char library_name[1024];
00088 snprintf(library_name, sizeof(library_name), "%s.%d", library, version);
00089 strtolower(library_name);
00090
00091
00092 AILibraryList::iterator iter = this->library_list.find(library_name);
00093 if (iter == this->library_list.end()) {
00094 char error[1024];
00095
00096
00097 iter = this->library_list.find(library);
00098 if (iter == this->library_list.end()) {
00099 snprintf(error, sizeof(error), "couldn't find library '%s'", library);
00100 } else {
00101 snprintf(error, sizeof(error), "couldn't find library '%s' version %d. The latest version available is %d", library, version, (*iter).second->GetVersion());
00102 }
00103 sq_throwerror(vm, OTTD2FS(error));
00104 return false;
00105 }
00106
00107
00108 HSQOBJECT parent;
00109 sq_getstackobj(vm, 1, &parent);
00110
00111 char fake_class[1024];
00112 int next_number;
00113
00114 if (!controller->LoadedLibrary(library_name, &next_number, &fake_class[0], sizeof(fake_class))) {
00115
00116 snprintf(fake_class, sizeof(fake_class), "_internalNA%d", next_number);
00117
00118
00119 sq_pushroottable(vm);
00120 sq_pushstring(vm, OTTD2FS(fake_class), -1);
00121 sq_newclass(vm, SQFalse);
00122
00123 if (!Squirrel::LoadScript(vm, (*iter).second->GetMainScript(), false)) {
00124 char error[1024];
00125 snprintf(error, sizeof(error), "there was a compile error when importing '%s' version %d", library, version);
00126 sq_throwerror(vm, OTTD2FS(error));
00127 return false;
00128 }
00129
00130 sq_newslot(vm, -3, SQFalse);
00131 sq_pop(vm, 1);
00132
00133 controller->AddLoadedLibrary(library_name, fake_class);
00134 }
00135
00136
00137 sq_pushroottable(vm);
00138 sq_pushstring(vm, OTTD2FS(fake_class), -1);
00139 if (SQ_FAILED(sq_get(vm, -2))) {
00140 sq_throwerror(vm, _SC("internal error assigning library class"));
00141 return false;
00142 }
00143 sq_pushstring(vm, OTTD2FS((*iter).second->GetInstanceName()), -1);
00144 if (SQ_FAILED(sq_get(vm, -2))) {
00145 char error[1024];
00146 snprintf(error, sizeof(error), "unable to find class '%s' in the library '%s' version %d", (*iter).second->GetInstanceName(), library, version);
00147 sq_throwerror(vm, OTTD2FS(error));
00148 return false;
00149 }
00150 HSQOBJECT obj;
00151 sq_getstackobj(vm, -1, &obj);
00152 sq_pop(vm, 3);
00153
00154 if (StrEmpty(class_name)) {
00155 sq_pushobject(vm, obj);
00156 return true;
00157 }
00158
00159
00160 sq_pushobject(vm, parent);
00161 sq_pushstring(vm, OTTD2FS(class_name), -1);
00162 sq_pushobject(vm, obj);
00163 sq_newclass(vm, SQTrue);
00164 sq_newslot(vm, -3, SQFalse);
00165 sq_pop(vm, 1);
00166
00167 sq_pushobject(vm, obj);
00168 return true;
00169 }
00170
00171 void AIScanner::RegisterLibrary(AILibrary *library)
00172 {
00173 char library_name[1024];
00174 snprintf(library_name, sizeof(library_name), "%s.%s.%d", library->GetCategory(), library->GetInstanceName(), library->GetVersion());
00175 strtolower(library_name);
00176
00177 if (this->library_list.find(library_name) != this->library_list.end()) {
00178
00179 #ifdef WIN32
00180
00181 if (strcasecmp(this->library_list[library_name]->GetMainScript(), library->GetMainScript()) == 0) {
00182 #else
00183 if (strcmp(this->library_list[library_name]->GetMainScript(), library->GetMainScript()) == 0) {
00184 #endif
00185 delete library;
00186 return;
00187 }
00188
00189 DEBUG(ai, 0, "Registering two libraries with the same name and version");
00190 DEBUG(ai, 0, " 1: %s", this->library_list[library_name]->GetMainScript());
00191 DEBUG(ai, 0, " 2: %s", library->GetMainScript());
00192 DEBUG(ai, 0, "The first is taking precedence.");
00193
00194 delete library;
00195 return;
00196 }
00197
00198 this->library_list[strdup(library_name)] = library;
00199 }
00200
00201 void AIScanner::RegisterAI(AIInfo *info)
00202 {
00203 char ai_name[1024];
00204 snprintf(ai_name, sizeof(ai_name), "%s.%d", info->GetName(), info->GetVersion());
00205 strtolower(ai_name);
00206
00207
00208 if (strlen(info->GetShortName()) != 4) {
00209 DEBUG(ai, 0, "The AI '%s' returned a string from GetShortName() which is not four characaters. Unable to load the AI.", info->GetName());
00210 delete info;
00211 return;
00212 }
00213
00214 if (this->info_list.find(ai_name) != this->info_list.end()) {
00215
00216 #ifdef WIN32
00217
00218 if (strcasecmp(this->info_list[ai_name]->GetMainScript(), info->GetMainScript()) == 0) {
00219 #else
00220 if (strcmp(this->info_list[ai_name]->GetMainScript(), info->GetMainScript()) == 0) {
00221 #endif
00222 delete info;
00223 return;
00224 }
00225
00226 DEBUG(ai, 0, "Registering two AIs with the same name and version");
00227 DEBUG(ai, 0, " 1: %s", this->info_list[ai_name]->GetMainScript());
00228 DEBUG(ai, 0, " 2: %s", info->GetMainScript());
00229 DEBUG(ai, 0, "The first is taking precedence.");
00230
00231 delete info;
00232 return;
00233 }
00234
00235 this->info_list[strdup(ai_name)] = info;
00236
00237
00238
00239 snprintf(ai_name, sizeof(ai_name), "%s", info->GetName());
00240 strtolower(ai_name);
00241 if (this->info_single_list.find(ai_name) == this->info_single_list.end()) {
00242 this->info_single_list[strdup(ai_name)] = info;
00243 } else if (this->info_single_list[ai_name]->GetVersion() < info->GetVersion()) {
00244 this->info_single_list[ai_name] = info;
00245 }
00246 }
00247
00248 AIInfo *AIScanner::SelectRandomAI()
00249 {
00250 uint num_random_ais = 0;
00251 for (AIInfoList::iterator it = this->info_single_list.begin(); it != this->info_single_list.end(); it++) {
00252 if (it->second->UseAsRandomAI()) num_random_ais++;
00253 }
00254
00255 if (num_random_ais == 0) {
00256 DEBUG(ai, 0, "No suitable AI found, loading 'dummy' AI.");
00257 return this->info_dummy;
00258 }
00259
00260
00261 uint pos;
00262 if (_networking) {
00263 pos = InteractiveRandomRange(num_random_ais);
00264 } else {
00265 pos = RandomRange(num_random_ais);
00266 }
00267
00268
00269 AIInfoList::iterator it = this->info_single_list.begin();
00270 while (!it->second->UseAsRandomAI()) it++;
00271 for (; pos > 0; pos--) {
00272 it++;
00273 while (!it->second->UseAsRandomAI()) it++;
00274 }
00275 return (*it).second;
00276 }
00277
00278 AIInfo *AIScanner::FindInfo(const char *nameParam, int versionParam)
00279 {
00280 if (this->info_list.size() == 0) return NULL;
00281 if (nameParam == NULL) return NULL;
00282
00283 char ai_name[1024];
00284 ttd_strlcpy(ai_name, nameParam, sizeof(ai_name));
00285 strtolower(ai_name);
00286
00287 AIInfo *info = NULL;
00288 int version = -1;
00289
00290 if (versionParam == -1) {
00291
00292 if (this->info_single_list.find(ai_name) != this->info_single_list.end()) return this->info_single_list[ai_name];
00293
00294
00295 char *e = strrchr(ai_name, '.');
00296 if (e == NULL) return NULL;
00297 *e = '\0';
00298 e++;
00299 versionParam = atoi(e);
00300
00301 }
00302
00303
00304 char ai_name_tmp[1024];
00305 snprintf(ai_name_tmp, sizeof(ai_name_tmp), "%s.%d", ai_name, versionParam);
00306 strtolower(ai_name_tmp);
00307 if (this->info_list.find(ai_name_tmp) != this->info_list.end()) return this->info_list[ai_name_tmp];
00308
00309
00310
00311 AIInfoList::iterator it = this->info_list.begin();
00312 for (; it != this->info_list.end(); it++) {
00313 char ai_name_compare[1024];
00314 snprintf(ai_name_compare, sizeof(ai_name_compare), "%s", (*it).second->GetName());
00315 strtolower(ai_name_compare);
00316
00317 if (strcasecmp(ai_name, ai_name_compare) == 0 && (*it).second->CanLoadFromVersion(versionParam) && (version == -1 || (*it).second->GetVersion() > version)) {
00318 version = (*it).second->GetVersion();
00319 info = (*it).second;
00320 }
00321 }
00322
00323 return info;
00324 }
00325
00326 char *AIScanner::GetAIConsoleList(char *p, const char *last)
00327 {
00328 p += seprintf(p, last, "List of AIs:\n");
00329 AIInfoList::iterator it = this->info_list.begin();
00330 for (; it != this->info_list.end(); it++) {
00331 AIInfo *i = (*it).second;
00332 p += seprintf(p, last, "%10s (v%d): %s\n", i->GetName(), i->GetVersion(), i->GetDescription());
00333 }
00334 p += seprintf(p, last, "\n");
00335
00336 return p;
00337 }
00338
00339 #if defined(ENABLE_NETWORK)
00340 #include "../network/network_content.h"
00341 #include "../3rdparty/md5/md5.h"
00342 #include "../tar_type.h"
00343
00345 struct AIFileChecksumCreator : FileScanner {
00346 byte md5sum[16];
00347
00352 AIFileChecksumCreator()
00353 {
00354 memset(this->md5sum, 0, sizeof(this->md5sum));
00355 }
00356
00357
00358 virtual bool AddFile(const char *filename, size_t basepath_length)
00359 {
00360 Md5 checksum;
00361 uint8 buffer[1024];
00362 size_t len, size;
00363 byte tmp_md5sum[16];
00364
00365
00366 FILE *f = FioFOpenFile(filename, "rb", DATA_DIR, &size);
00367 if (f == NULL) return false;
00368
00369
00370 while ((len = fread(buffer, 1, (size > sizeof(buffer)) ? sizeof(buffer) : size, f)) != 0 && size != 0) {
00371 size -= len;
00372 checksum.Append(buffer, len);
00373 }
00374 checksum.Finish(tmp_md5sum);
00375
00376 FioFCloseFile(f);
00377
00378
00379 for (uint i = 0; i < sizeof(md5sum); i++) this->md5sum[i] ^= tmp_md5sum[i];
00380
00381 return true;
00382 }
00383 };
00384
00393 static bool IsSameAI(const ContentInfo *ci, bool md5sum, AIFileInfo *info)
00394 {
00395 uint32 id = 0;
00396 const char *str = info->GetShortName();
00397 for (int j = 0; j < 4 && *str != '\0'; j++, str++) id |= *str << (8 * j);
00398
00399 if (id != ci->unique_id) return false;
00400 if (!md5sum) return true;
00401
00402 AIFileChecksumCreator checksum;
00403 char path[MAX_PATH];
00404 strecpy(path, info->GetMainScript(), lastof(path));
00405
00406
00407
00408 *strrchr(path, PATHSEPCHAR) = '\0';
00409 *strrchr(path, PATHSEPCHAR) = '\0';
00410 TarList::iterator iter = _tar_list.find(path);
00411
00412 if (iter != _tar_list.end()) {
00413
00414
00415 TarFileList::iterator tar;
00416 FOR_ALL_TARS(tar) {
00417
00418 if (tar->second.tar_filename != iter->first) continue;
00419
00420
00421 const char *ext = strrchr(tar->first.c_str(), '.');
00422 if (ext == NULL || strcasecmp(ext, ".nut") != 0) continue;
00423
00424
00425 seprintf(path, lastof(path), "%s%c%s", tar->second.tar_filename, PATHSEPCHAR, tar->first.c_str());
00426 checksum.AddFile(path, 0);
00427 }
00428 } else {
00429
00430
00431 path[strlen(path)] = PATHSEPCHAR;
00432 checksum.Scan(".nut", path);
00433 }
00434
00435 return memcmp(ci->md5sum, checksum.md5sum, sizeof(ci->md5sum)) == 0;
00436 }
00437
00444 bool AIScanner::HasAI(const ContentInfo *ci, bool md5sum)
00445 {
00446 switch (ci->type) {
00447 case CONTENT_TYPE_AI:
00448 for (AIInfoList::iterator it = this->info_list.begin(); it != this->info_list.end(); it++) {
00449 if (IsSameAI(ci, md5sum, (*it).second)) return true;
00450 }
00451 return false;
00452
00453 case CONTENT_TYPE_AI_LIBRARY:
00454 for (AILibraryList::iterator it = this->library_list.begin(); it != this->library_list.end(); it++) {
00455 if (IsSameAI(ci, md5sum, (*it).second)) return true;
00456 }
00457 return false;
00458
00459 default:
00460 NOT_REACHED();
00461 }
00462 }
00463
00470 bool AI::HasAI(const ContentInfo *ci, bool md5sum)
00471 {
00472 return AI::ai_scanner->HasAI(ci, md5sum);
00473 }
00474
00475 #endif