1 /* This file is part of the Spring engine (GPL v2 or later), see LICENSE.html */
2 
3 #include "Interface.h"
4 
5 #include "CUtils/Util.h"
6 #include "CUtils/SimpleLog.h"
7 #include "CUtils/SharedLibrary.h"
8 
9 #include "ExternalAI/Interface/aidefines.h"
10 #include "ExternalAI/Interface/SAIInterfaceLibrary.h"
11 #include "ExternalAI/Interface/SSkirmishAILibrary.h"
12 #include "ExternalAI/Interface/SAIInterfaceCallback.h"
13 struct SSkirmishAICallback;
14 
15 #include "System/Util.h"
16 
CInterface(int interfaceId,const struct SAIInterfaceCallback * callback)17 CInterface::CInterface(int interfaceId,
18 		const struct SAIInterfaceCallback* callback)
19 		: interfaceId(interfaceId), callback(callback) {
20 
21 	char* logFileName = util_allocStrCatFSPath(2, "log", "interface-log.txt");
22 	bool timeStamps = true;
23 #ifdef DEBUG
24 	int logLevel = SIMPLELOG_LEVEL_FINE;
25 #else
26 	int logLevel = SIMPLELOG_LEVEL_ERROR;
27 #endif
28 	static const unsigned int logFilePath_sizeMax = 1024;
29 	char logFilePath[logFilePath_sizeMax];
30 	// eg: "~/.spring/AI/Interfaces/C/log/interface-log.txt"
31 	bool ok = callback->DataDirs_locatePath(interfaceId,
32 			logFilePath, logFilePath_sizeMax,
33 			logFileName, true, true, false, false);
34 	if (!ok) {
35 		simpleLog_logL(SIMPLELOG_LEVEL_ERROR,
36 			"Failed locating the log file %s.", logFileName);
37 	}
38 
39 	simpleLog_init(logFilePath, timeStamps, logLevel, false);
40 
41 	const char* const myShortName = callback->AIInterface_Info_getValueByKey(interfaceId,
42 			AI_INTERFACE_PROPERTY_SHORT_NAME);
43 	const char* const myVersion = callback->AIInterface_Info_getValueByKey(interfaceId,
44 			AI_INTERFACE_PROPERTY_VERSION);
45 
46 	simpleLog_log("This is the log-file of the %s v%s AI Interface",
47 			myShortName, myVersion);
48 	simpleLog_log("Using read/write data-directory: %s",
49 			callback->DataDirs_getWriteableDir(interfaceId));
50 	simpleLog_log("Using log file: %s", logFileName);
51 
52 	FREE(logFileName);
53 }
54 
55 //LevelOfSupport CInterface::GetLevelOfSupportFor(
56 //		const char* engineVersion, int engineAIInterfaceGeneratedVersion) {
57 //	return LOS_Working;
58 //}
59 
60 // SSkirmishAISpecifier CInterface::ExtractSpecifier(
61 // 		const std::map<std::string, std::string>& infoMap) {
62 //
63 // 	const char* const skirmDD =
64 // 			callback->SkirmishAIs_Info_getValueByKey(interfaceId,
65 // 			shortName, version,
66 // 			SKIRMISH_AI_PROPERTY_DATA_DIR);
67 // 	const char* sn = callback->AIInterface_Info_getValueByKey)(int interfaceId, const char* const key);
68 // 	.find(SKIRMISH_AI_PROPERTY_SHORT_NAME)->second.c_str();
69 // 	const char* v = infoMap.find(SKIRMISH_AI_PROPERTY_VERSION)->second.c_str();
70 //
71 // 	SSkirmishAISpecifier spec = {sn, v};
72 //
73 // 	return spec;
74 // }
75 
LoadSkirmishAILibrary(const char * const shortName,const char * const version)76 const SSkirmishAILibrary* CInterface::LoadSkirmishAILibrary(
77 		const char* const shortName,
78 		const char* const version) {
79 
80 	SSkirmishAILibrary* ai = NULL;
81 
82 	SSkirmishAISpecifier spec;
83 	spec.shortName = shortName;
84 	spec.version = version;
85 
86 	mySkirmishAISpecifiers.insert(spec);
87 
88 	T_skirmishAIs::iterator skirmishAI;
89 	skirmishAI = myLoadedSkirmishAIs.find(spec);
90 	if (skirmishAI == myLoadedSkirmishAIs.end()) {
91 		ai = new SSkirmishAILibrary;
92 		sharedLib_t lib = Load(spec, ai);
93 		if (sharedLib_isLoaded(lib)) {
94 			// success
95 			myLoadedSkirmishAIs[spec] = ai;
96 			myLoadedSkirmishAILibs[spec] = lib;
97 		} else {
98 			// failure
99 			delete ai;
100 			ai = NULL;
101 		}
102 	} else {
103 		ai = skirmishAI->second;
104 	}
105 
106 	return ai;
107 }
UnloadSkirmishAILibrary(const char * const shortName,const char * const version)108 int CInterface::UnloadSkirmishAILibrary(
109 	const char* const shortName,
110 	const char* const version
111 ) {
112 	SSkirmishAISpecifier spec;
113 	spec.shortName = shortName;
114 	spec.version = version;
115 
116 	T_skirmishAIs::iterator skirmishAI = myLoadedSkirmishAIs.find(spec);
117 	T_skirmishAILibs::iterator skirmishAILib = myLoadedSkirmishAILibs.find(spec);
118 
119 	if (skirmishAI == myLoadedSkirmishAIs.end()) {
120 		// to-unload-AI is not loaded -> no problem, do nothing
121 	} else {
122 		delete skirmishAI->second;
123 		myLoadedSkirmishAIs.erase(skirmishAI);
124 		sharedLib_unload(skirmishAILib->second);
125 		myLoadedSkirmishAILibs.erase(skirmishAILib);
126 	}
127 
128 	return 0;
129 }
UnloadAllSkirmishAILibraries()130 int CInterface::UnloadAllSkirmishAILibraries() {
131 
132 	while (!myLoadedSkirmishAIs.empty()) {
133 		T_skirmishAISpecifiers::const_iterator ai =
134 				mySkirmishAISpecifiers.begin();
135 		UnloadSkirmishAILibrary((*ai).shortName, (*ai).version);
136 	}
137 
138 	return 0; // signal: ok
139 }
140 
141 
142 // private functions following
143 
Load(const SSkirmishAISpecifier & spec,SSkirmishAILibrary * skirmishAILibrary)144 sharedLib_t CInterface::Load(const SSkirmishAISpecifier& spec, SSkirmishAILibrary* skirmishAILibrary) {
145 	return LoadSkirmishAILib(FindLibFile(spec), skirmishAILibrary);
146 }
LoadSkirmishAILib(const std::string & libFilePath,SSkirmishAILibrary * skirmishAILibrary)147 sharedLib_t CInterface::LoadSkirmishAILib(const std::string& libFilePath,
148 		SSkirmishAILibrary* skirmishAILibrary) {
149 
150 	sharedLib_t sharedLib = sharedLib_load(libFilePath.c_str());
151 
152 	if (!sharedLib_isLoaded(sharedLib)) {
153 		reportError(std::string("Failed loading shared library: ") + libFilePath);
154 		return sharedLib;
155 	}
156 
157 	// initialize the AI library
158 	std::string funcName;
159 
160 	funcName = "getLevelOfSupportFor";
161 	skirmishAILibrary->getLevelOfSupportFor
162 			= (LevelOfSupport (CALLING_CONV_FUNC_POINTER *)(
163 			const char* aiShortName, const char* aiVersion,
164 			const char* engineVersionString, int engineVersionNumber,
165 			const char* aiInterfaceShortName, const char* aiInterfaceVersion))
166 			sharedLib_findAddress(sharedLib, funcName.c_str());
167 	if (skirmishAILibrary->getLevelOfSupportFor == NULL) {
168 		// do nothing: it is permitted that an AI does not export this function
169 		//reportInterfaceFunctionError(libFilePath, funcName);
170 	}
171 
172 	funcName = "init";
173 	skirmishAILibrary->init
174 			= (int (CALLING_CONV_FUNC_POINTER *)(int skirmishAIId,
175 			const struct SSkirmishAICallback*))
176 			sharedLib_findAddress(sharedLib, funcName.c_str());
177 	if (skirmishAILibrary->init == NULL) {
178 		// do nothing: it is permitted that an AI does not export this function,
179 		// as it can still use EVENT_INIT instead
180 		//reportInterfaceFunctionError(libFilePath, funcName);
181 	}
182 
183 	funcName = "release";
184 	skirmishAILibrary->release
185 			= (int (CALLING_CONV_FUNC_POINTER *)(int skirmishAIId))
186 			sharedLib_findAddress(sharedLib, funcName.c_str());
187 	if (skirmishAILibrary->release == NULL) {
188 		// do nothing: it is permitted that an AI does not export this function,
189 		// as it can still use EVENT_RELEASE instead
190 		//reportInterfaceFunctionError(libFilePath, funcName);
191 	}
192 
193 	funcName = "handleEvent";
194 	skirmishAILibrary->handleEvent
195 			= (int (CALLING_CONV_FUNC_POINTER *)(int skirmishAIId,
196 			int topicId, const void* data))
197 			sharedLib_findAddress(sharedLib, funcName.c_str());
198 	if (skirmishAILibrary->handleEvent == NULL) {
199 		reportInterfaceFunctionError(libFilePath, funcName);
200 	}
201 
202 	return sharedLib;
203 }
204 
205 
reportInterfaceFunctionError(const std::string & libFilePath,const std::string & functionName)206 void CInterface::reportInterfaceFunctionError(const std::string& libFilePath,
207 		const std::string& functionName) {
208 
209 	std::string msg("Failed loading AI Library from file \"");
210 	msg += libFilePath + "\": no \"" + functionName + "\" function exported";
211 	reportError(msg);
212 }
213 
reportError(const std::string & msg)214 void CInterface::reportError(const std::string& msg) {
215 	simpleLog_logL(SIMPLELOG_LEVEL_ERROR, msg.c_str());
216 }
217 
218 
FindLibFile(const SSkirmishAISpecifier & spec)219 std::string CInterface::FindLibFile(const SSkirmishAISpecifier& spec) {
220 
221 	const char* const skirmDD =
222 			callback->SkirmishAIs_Info_getValueByKey(interfaceId,
223 			spec.shortName, spec.version,
224 			SKIRMISH_AI_PROPERTY_DATA_DIR);
225 	if (skirmDD == NULL) {
226 		reportError(std::string("Missing Skirmish AI data-dir for ")
227 				+ spec.shortName + " " + spec.version);
228 	}
229 
230 	static const size_t libFileName_sizeMax = 512;
231 	// eg. "libSkirmishAI.so" or "SkirmishAI.dll"
232 	char libFileName[libFileName_sizeMax];
233 	sharedLib_createFullLibName("SkirmishAI", libFileName, libFileName_sizeMax);
234 
235 	return util_allocStrCatFSPath(2, skirmDD, libFileName);
236 }
237 
FitsThisInterface(const std::string & requestedShortName,const std::string & requestedVersion)238 bool CInterface::FitsThisInterface(const std::string& requestedShortName,
239 		const std::string& requestedVersion) {
240 
241 	const char* const myShortName = callback->AIInterface_Info_getValueByKey(interfaceId,
242 			AI_INTERFACE_PROPERTY_SHORT_NAME);
243 	const char* const myVersion = callback->AIInterface_Info_getValueByKey(interfaceId,
244 			AI_INTERFACE_PROPERTY_VERSION);
245 
246 	bool shortNameFits = (requestedShortName == myShortName);
247 	bool versionFits = (requestedVersion == myVersion);
248 
249 	return shortNameFits && versionFits;
250 }
251