Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Fetching contributors…

Cannot retrieve contributors at this time

2727 lines (2152 sloc) 56.844 kB
/* This file is part of the Spring engine (GPL v2 or later), see LICENSE.html */
#include "unitsync.h"
#include "unitsync_api.h"
#include <algorithm>
#include <string>
#include <vector>
#include <set>
// shared with spring:
#include "lib/lua/include/LuaInclude.h"
#include "Game/GameVersion.h"
#include "Lua/LuaParser.h"
#include "Map/MapParser.h"
#include "Map/ReadMap.h"
#include "Map/SMF/SMFMapFile.h"
#include "Rendering/Textures/Bitmap.h"
#include "Sim/Misc/SideParser.h"
#include "ExternalAI/Interface/aidefines.h"
#include "ExternalAI/Interface/SSkirmishAILibrary.h"
#include "ExternalAI/LuaAIImplHandler.h"
#include "System/FileSystem/IArchive.h"
#include "System/FileSystem/ArchiveLoader.h"
#include "System/FileSystem/ArchiveScanner.h"
#include "System/FileSystem/DataDirsAccess.h"
#include "System/FileSystem/DataDirLocater.h"
#include "System/FileSystem/FileHandler.h"
#include "System/FileSystem/VFSHandler.h"
#include "System/FileSystem/FileSystem.h"
#include "System/FileSystem/FileSystemInitializer.h"
#include "System/Config/ConfigHandler.h"
#include "System/Exceptions.h"
#include "System/Log/ILog.h"
#include "System/Log/Level.h"
#include "System/Log/DefaultFilter.h"
#include "System/LogOutput.h"
#include "System/Util.h"
#include "System/exportdefines.h"
#include "System/Info.h"
#include "System/Option.h"
#include "System/SafeCStrings.h"
#ifdef WIN32
#include <windows.h>
#endif
// unitsync only:
#include "Syncer.h"
//////////////////////////
//////////////////////////
#define LOG_SECTION_UNITSYNC "unitsync"
LOG_REGISTER_SECTION_GLOBAL(LOG_SECTION_UNITSYNC)
// use the specific section for all LOG*() calls in this source file
#ifdef LOG_SECTION_CURRENT
#undef LOG_SECTION_CURRENT
#endif
#define LOG_SECTION_CURRENT LOG_SECTION_UNITSYNC
// NOTE This means that the DLL can only support one instance.
// This is no problem in the current architecture.
static CSyncer* syncer;
static bool logOutputInitialised = false;
// for we do not have to include global-stuff (Sim/Misc/GlobalConstants.h)
#define SQUARE_SIZE 8
#ifdef WIN32
BOOL CALLING_CONV DllMain(HINSTANCE hInst, DWORD dwReason, LPVOID lpReserved)
{
return TRUE;
}
#endif
//////////////////////////
//////////////////////////
// Helper class for popping up a MessageBox only once
class CMessageOnce
{
public:
CMessageOnce(const std::string& message)
: alreadyDone(false)
, message(message)
{
assert(!message.empty());
}
void print() {
if (!alreadyDone) {
alreadyDone = true;
LOG_L(L_WARNING, "%s", message.c_str());
}
}
private:
bool alreadyDone;
const std::string message;
};
#define DEPRECATED \
static CMessageOnce msg( \
"The deprecated unitsync function " \
+ std::string(__FUNCTION__) + " was called." \
" Please update your lobby client"); \
msg.print(); \
SetLastError("deprecated unitsync function called: " \
+ std::string(__FUNCTION__))
//////////////////////////
//////////////////////////
// function argument checking
static void CheckInit()
{
if (!archiveScanner || !vfsHandler)
throw std::logic_error("Unitsync not initialized. Call Init first.");
}
static void _CheckNull(void* condition, const char* name)
{
if (!condition)
throw std::invalid_argument("Argument " + std::string(name) + " may not be null.");
}
static void _CheckNullOrEmpty(const char* condition, const char* name)
{
if (!condition || *condition == 0)
throw std::invalid_argument("Argument " + std::string(name) + " may not be null or empty.");
}
static void _CheckBounds(int index, int size, const char* name)
{
if (index < 0 || index >= size)
throw std::out_of_range("Argument " + std::string(name) + " out of bounds. Index: " +
IntToString(index) + " Array size: " + IntToString(size));
}
static void _CheckPositive(int value, const char* name)
{
if (value <= 0)
throw std::out_of_range("Argument " + std::string(name) + " must be positive.");
}
#define CheckNull(arg) _CheckNull((arg), #arg)
#define CheckNullOrEmpty(arg) _CheckNullOrEmpty((arg), #arg)
#define CheckBounds(arg, size) _CheckBounds((arg), (size), #arg)
#define CheckPositive(arg) _CheckPositive((arg), #arg);
static std::vector<InfoItem> info;
static std::set<std::string> infoSet;
//////////////////////////
//////////////////////////
// error handling
static std::string lastError;
static void _SetLastError(const std::string& err)
{
LOG_L(L_ERROR, "%s", err.c_str());
lastError = err;
}
#define SetLastError(str) \
_SetLastError(std::string(__FUNCTION__) + ": " + (str))
#define UNITSYNC_CATCH_BLOCKS \
catch (const user_error& ex) { \
SetLastError(ex.what()); \
} \
catch (const std::exception& ex) { \
SetLastError(ex.what()); \
} \
catch (...) { \
SetLastError("an unknown exception was thrown"); \
}
//////////////////////////
//////////////////////////
static std::string GetMapFile(const std::string& mapName)
{
std::string mapFile = archiveScanner->MapNameToMapFile(mapName);
if (mapFile != mapName) {
//! translation finished fine
return mapFile;
}
/*CFileHandler f(mapFile);
if (f.FileExists()) {
return mapFile;
}
CFileHandler f = CFileHandler(map);
if (f.FileExists()) {
return map;
}
f = CFileHandler("maps/" + map);
if (f.FileExists()) {
return "maps/" + map;
}*/
throw std::invalid_argument("Could not find a map named \"" + mapName + "\"");
return "";
}
class ScopedMapLoader {
public:
/**
* @brief Helper class for loading a map archive temporarily
* @param mapName the name of the to be loaded map
* @param mapFile checks if this file already exists in the current VFS,
* if so skip reloading
*/
ScopedMapLoader(const std::string& mapName, const std::string& mapFile)
: oldHandler(vfsHandler)
{
CFileHandler f(mapFile);
if (f.FileExists()) {
return;
}
vfsHandler = new CVFSHandler();
vfsHandler->AddArchiveWithDeps(mapName, false);
}
~ScopedMapLoader()
{
if (vfsHandler != oldHandler) {
delete vfsHandler;
vfsHandler = oldHandler;
}
}
private:
CVFSHandler* oldHandler;
};
//////////////////////////
//////////////////////////
EXPORT(const char*) GetNextError()
{
try {
// queue is only 1 element long now for simplicity :-)
if (lastError.empty()) return NULL;
std::string err = lastError;
lastError.clear();
return GetStr(err);
}
UNITSYNC_CATCH_BLOCKS;
// Oops, can't even return errors anymore...
// Returning anything but NULL might cause infinite loop in lobby client...
//return __FUNCTION__ ": fatal error: an exception was thrown in GetNextError";
return NULL;
}
EXPORT(const char*) GetSpringVersion()
{
return GetStr(SpringVersion::GetSync());
}
EXPORT(const char*) GetSpringVersionPatchset()
{
return GetStr(SpringVersion::GetPatchSet());
}
EXPORT(bool) IsSpringReleaseVersion()
{
return SpringVersion::IsRelease();
}
static void internal_deleteMapInfos();
static void _Cleanup()
{
internal_deleteMapInfos();
lpClose();
if (syncer) {
SafeDelete(syncer);
LOG("deinitialized");
}
}
static void CheckForImportantFilesInVFS()
{
std::vector<std::string> filesToCheck;
filesToCheck.push_back("base/springcontent.sdz");
filesToCheck.push_back("base/maphelper.sdz");
filesToCheck.push_back("base/spring/bitmaps.sdz");
filesToCheck.push_back("base/cursors.sdz");
for (std::vector<std::string>::const_iterator it = filesToCheck.begin(); it != filesToCheck.end(); ++it) {
if (!CFileHandler::FileExists(*it, SPRING_VFS_RAW)) {
throw content_error("Required base file '" + *it + "' does not exist.");
}
}
}
EXPORT(int) Init(bool isServer, int id)
{
try {
// Cleanup data from previous Init() calls
_Cleanup();
// LogSystem 1
if (!logOutputInitialised) {
logOutput.SetFileName("unitsync.log");
}
#ifndef DEBUG
log_filter_section_setMinLevel(LOG_SECTION_UNITSYNC, LOG_LEVEL_INFO);
#endif
// VFS
if (archiveScanner || vfsHandler){
FileSystemInitializer::Cleanup(); //reinitialize filesystem to detect new files
}
if (!configHandler) {
ConfigHandler::Instantiate(); // use the default config file
}
dataDirLocater.UpdateIsolationModeByEnvVar();
FileSystemInitializer::Initialize();
// LogSystem 2
if (!logOutputInitialised) {
logOutput.Initialize();
logOutputInitialised = true;
}
LOG("loaded, %s", SpringVersion::GetFull().c_str());
// check if VFS is okay
CheckForImportantFilesInVFS();
// Finish
syncer = new CSyncer();
LOG("initialized, %s", SpringVersion::GetFull().c_str());
LOG("%s", (isServer ? "hosting" : "joining"));
return 1;
}
UNITSYNC_CATCH_BLOCKS;
return 0;
}
EXPORT(void) UnInit()
{
try {
_Cleanup();
FileSystemInitializer::Cleanup();
ConfigHandler::Deallocate();
}
UNITSYNC_CATCH_BLOCKS;
}
EXPORT(const char*) GetWritableDataDirectory()
{
try {
CheckInit();
return GetStr(dataDirLocater.GetWriteDirPath());
}
UNITSYNC_CATCH_BLOCKS;
return NULL;
}
EXPORT(int) GetDataDirectoryCount()
{
int count = -1;
try {
CheckInit();
count = (int) dataDirLocater.GetDataDirs().size();
}
UNITSYNC_CATCH_BLOCKS;
return count;
}
EXPORT(const char*) GetDataDirectory(int index)
{
try {
CheckInit();
const std::vector<std::string> datadirs = dataDirLocater.GetDataDirPaths();
if (index > datadirs.size()) {
return NULL;
}
return GetStr(datadirs[index]);
}
UNITSYNC_CATCH_BLOCKS;
return NULL;
}
EXPORT(int) ProcessUnits()
{
int leftToProcess = -1;
try {
LOG_L(L_DEBUG, "syncer: process units");
leftToProcess = syncer->ProcessUnits();
}
UNITSYNC_CATCH_BLOCKS;
return leftToProcess;
}
EXPORT(int) ProcessUnitsNoChecksum()
{
return ProcessUnits();
}
EXPORT(int) GetUnitCount()
{
int count = -1;
try {
CheckInit();
LOG_L(L_DEBUG, "syncer: get unit count");
count = syncer->GetUnitCount();
}
UNITSYNC_CATCH_BLOCKS;
return count;
}
EXPORT(const char*) GetUnitName(int unit)
{
try {
CheckInit();
LOG_L(L_DEBUG, "syncer: get unit %d name", unit);
std::string tmp = syncer->GetUnitName(unit);
return GetStr(tmp);
}
UNITSYNC_CATCH_BLOCKS;
return NULL;
}
EXPORT(const char*) GetFullUnitName(int unit)
{
try {
CheckInit();
LOG_L(L_DEBUG, "syncer: get full unit %d name", unit);
std::string tmp = syncer->GetFullUnitName(unit);
return GetStr(tmp);
}
UNITSYNC_CATCH_BLOCKS;
return NULL;
}
//////////////////////////
//////////////////////////
EXPORT(void) AddArchive(const char* archiveName)
{
try {
CheckInit();
CheckNullOrEmpty(archiveName);
LOG_L(L_DEBUG, "adding archive: %s", archiveName);
vfsHandler->AddArchive(archiveName, false);
}
UNITSYNC_CATCH_BLOCKS;
}
EXPORT(void) AddAllArchives(const char* rootArchiveName)
{
try {
CheckInit();
CheckNullOrEmpty(rootArchiveName);
vfsHandler->AddArchiveWithDeps(rootArchiveName, false);
}
UNITSYNC_CATCH_BLOCKS;
}
EXPORT(void) RemoveAllArchives()
{
try {
CheckInit();
LOG_L(L_DEBUG, "removing all archives");
SafeDelete(vfsHandler);
SafeDelete(syncer);
vfsHandler = new CVFSHandler();
syncer = new CSyncer();
}
UNITSYNC_CATCH_BLOCKS;
}
EXPORT(unsigned int) GetArchiveChecksum(const char* archiveName)
{
try {
CheckInit();
CheckNullOrEmpty(archiveName);
LOG_L(L_DEBUG, "archive checksum: %s", archiveName);
return archiveScanner->GetSingleArchiveChecksum(archiveName);
}
UNITSYNC_CATCH_BLOCKS;
return 0;
}
EXPORT(const char*) GetArchivePath(const char* archiveName)
{
try {
CheckInit();
CheckNullOrEmpty(archiveName);
LOG_L(L_DEBUG, "archive path: %s", archiveName);
return GetStr(archiveScanner->GetArchivePath(archiveName));
}
UNITSYNC_CATCH_BLOCKS;
return NULL;
}
static void safe_strzcpy(char* dst, std::string src, size_t max)
{
STRCPY_T(dst, max, src.c_str());
}
/**
* @brief map related meta-data
*/
struct InternalMapInfo
{
std::string description; ///< Description (max 255 chars)
std::string author; ///< Creator of the map (max 200 chars)
int tidalStrength; ///< Tidal strength
int gravity; ///< Gravity
float maxMetal; ///< Metal scale factor
int extractorRadius; ///< Extractor radius (of metal extractors)
int minWind; ///< Minimum wind speed
int maxWind; ///< Maximum wind speed
int width; ///< Width of the map
int height; ///< Height of the map
std::vector<float> xPos; ///< Start positions X coordinates defined by the map
std::vector<float> zPos; ///< Start positions Z coordinates defined by the map
};
static bool internal_GetMapInfo(const char* mapName, InternalMapInfo* outInfo)
{
CheckInit();
CheckNullOrEmpty(mapName);
CheckNull(outInfo);
LOG_L(L_DEBUG, "get map info: %s", mapName);
const std::string mapFile = GetMapFile(mapName);
ScopedMapLoader mapLoader(mapName, mapFile);
std::string err("");
MapParser mapParser(mapFile);
if (!mapParser.IsValid()) {
err = mapParser.GetErrorLog();
}
const LuaTable mapTable = mapParser.GetRoot();
// Retrieve the map header as well
if (err.empty()) {
const std::string extension = FileSystem::GetExtension(mapFile);
if (extension == "smf") {
try {
const CSMFMapFile file(mapFile);
const SMFHeader& mh = file.GetHeader();
outInfo->width = mh.mapx * SQUARE_SIZE;
outInfo->height = mh.mapy * SQUARE_SIZE;
}
catch (content_error&) {
outInfo->width = -1;
}
} else {
const int w = mapTable.GetInt("gameAreaW", 0);
const int h = mapTable.GetInt("gameAreaW", 1);
outInfo->width = w * SQUARE_SIZE;
outInfo->height = h * SQUARE_SIZE;
}
// Make sure we found stuff in both the smd and the header
if (outInfo->width <= 0) {
err = "Bad map width";
} else if (outInfo->height <= 0) {
err = "Bad map height";
}
}
// If the map did not parse, say so now
if (!err.empty()) {
SetLastError(err);
outInfo->description = err;
return false;
}
outInfo->description = mapTable.GetString("description", "");
outInfo->tidalStrength = mapTable.GetInt("tidalstrength", 0);
outInfo->gravity = mapTable.GetInt("gravity", 0);
outInfo->extractorRadius = mapTable.GetInt("extractorradius", 0);
outInfo->maxMetal = mapTable.GetFloat("maxmetal", 0.0f);
outInfo->author = mapTable.GetString("author", "");
const LuaTable atmoTable = mapTable.SubTable("atmosphere");
outInfo->minWind = atmoTable.GetInt("minWind", 0);
outInfo->maxWind = atmoTable.GetInt("maxWind", 0);
// Find as many start positions as there are defined by the map
for (size_t curTeam = 0; true; ++curTeam) {
float3 pos(-1.0f, -1.0f, -1.0f); // defaults
if (!mapParser.GetStartPos(curTeam, pos)) {
break; // position could not be parsed
}
outInfo->xPos.push_back(pos.x);
outInfo->zPos.push_back(pos.z);
LOG_L(L_DEBUG, "startpos: %.0f, %.0f", pos.x, pos.z);
}
return true;
}
/** @deprecated */
static bool _GetMapInfoEx(const char* mapName, MapInfo* outInfo, int version)
{
CheckInit();
CheckNullOrEmpty(mapName);
CheckNull(outInfo);
bool fetchOk;
InternalMapInfo internalMapInfo;
fetchOk = internal_GetMapInfo(mapName, &internalMapInfo);
if (fetchOk) {
safe_strzcpy(outInfo->description, internalMapInfo.description, 255);
outInfo->tidalStrength = internalMapInfo.tidalStrength;
outInfo->gravity = internalMapInfo.gravity;
outInfo->maxMetal = internalMapInfo.maxMetal;
outInfo->extractorRadius = internalMapInfo.extractorRadius;
outInfo->minWind = internalMapInfo.minWind;
outInfo->maxWind = internalMapInfo.maxWind;
outInfo->width = internalMapInfo.width;
outInfo->height = internalMapInfo.height;
outInfo->posCount = internalMapInfo.xPos.size();
if (outInfo->posCount > 16) {
// legacy interface does not support more then 16
outInfo->posCount = 16;
}
for (size_t curTeam = 0; curTeam < outInfo->posCount; ++curTeam) {
outInfo->positions[curTeam].x = internalMapInfo.xPos[curTeam];
outInfo->positions[curTeam].z = internalMapInfo.zPos[curTeam];
}
if (version >= 1) {
safe_strzcpy(outInfo->author, internalMapInfo.author, 200);
}
} else {
// contains the error message
safe_strzcpy(outInfo->description, internalMapInfo.description, 255);
// Fill in stuff so TASClient does not crash
outInfo->posCount = 0;
if (version >= 1) {
outInfo->author[0] = '\0';
}
return false;
}
return fetchOk;
}
EXPORT(int) GetMapInfoEx(const char* mapName, MapInfo* outInfo, int version)
{
DEPRECATED;
int ret = 0;
try {
const bool fetchOk = _GetMapInfoEx(mapName, outInfo, version);
ret = fetchOk ? 1 : 0;
}
UNITSYNC_CATCH_BLOCKS;
return ret;
}
EXPORT(int) GetMapInfo(const char* mapName, MapInfo* outInfo)
{
DEPRECATED;
int ret = 0;
try {
const bool fetchOk = _GetMapInfoEx(mapName, outInfo, 0);
ret = fetchOk ? 1 : 0;
}
UNITSYNC_CATCH_BLOCKS;
return ret;
}
// Updated on every call to GetMapCount
static std::vector<std::string> mapNames;
EXPORT(int) GetMapCount()
{
int count = -1;
try {
CheckInit();
mapNames.clear();
const std::vector<std::string> scannedNames = archiveScanner->GetMaps();
mapNames.insert(mapNames.begin(), scannedNames.begin(), scannedNames.end());
sort(mapNames.begin(), mapNames.end());
count = mapNames.size();
}
UNITSYNC_CATCH_BLOCKS;
return count;
}
EXPORT(const char*) GetMapName(int index)
{
try {
CheckInit();
CheckBounds(index, mapNames.size());
return GetStr(mapNames[index]);
}
UNITSYNC_CATCH_BLOCKS;
return NULL;
}
EXPORT(const char*) GetMapFileName(int index)
{
try {
CheckInit();
CheckBounds(index, mapNames.size());
return GetStr(archiveScanner->MapNameToMapFile(mapNames[index]));
}
UNITSYNC_CATCH_BLOCKS;
return NULL;
}
static std::map<int, InternalMapInfo> mapInfos;
static InternalMapInfo* internal_getMapInfo(int index) {
if (index >= mapNames.size()) {
SetLastError("invalid map index");
} else {
if (mapInfos.find(index) == mapInfos.end()) {
try {
InternalMapInfo imi;
if (internal_GetMapInfo(mapNames[index].c_str(), &imi)) {
mapInfos[index] = imi;
return &(mapInfos[index]);
}
}
UNITSYNC_CATCH_BLOCKS;
} else {
return &(mapInfos[index]);
}
}
return NULL;
}
static void internal_deleteMapInfos() {
while (!mapInfos.empty()) {
std::map<int, InternalMapInfo>::iterator mi = mapInfos.begin();
mapInfos.erase(mi);
}
}
EXPORT(const char*) GetMapDescription(int index) {
const InternalMapInfo* mapInfo = internal_getMapInfo(index);
if (mapInfo) {
return mapInfo->description.c_str();
}
return NULL;
}
EXPORT(const char*) GetMapAuthor(int index) {
const InternalMapInfo* mapInfo = internal_getMapInfo(index);
if (mapInfo) {
return mapInfo->author.c_str();
}
return NULL;
}
EXPORT(int) GetMapWidth(int index) {
const InternalMapInfo* mapInfo = internal_getMapInfo(index);
if (mapInfo) {
return mapInfo->width;
}
return -1;
}
EXPORT(int) GetMapHeight(int index) {
const InternalMapInfo* mapInfo = internal_getMapInfo(index);
if (mapInfo) {
return mapInfo->height;
}
return -1;
}
EXPORT(int) GetMapTidalStrength(int index) {
const InternalMapInfo* mapInfo = internal_getMapInfo(index);
if (mapInfo) {
return mapInfo->tidalStrength;
}
return -1;
}
EXPORT(int) GetMapWindMin(int index) {
const InternalMapInfo* mapInfo = internal_getMapInfo(index);
if (mapInfo) {
return mapInfo->minWind;
}
return -1;
}
EXPORT(int) GetMapWindMax(int index) {
const InternalMapInfo* mapInfo = internal_getMapInfo(index);
if (mapInfo) {
return mapInfo->maxWind;
}
return -1;
}
EXPORT(int) GetMapGravity(int index) {
const InternalMapInfo* mapInfo = internal_getMapInfo(index);
if (mapInfo) {
return mapInfo->gravity;
}
return -1;
}
EXPORT(int) GetMapResourceCount(int index) {
return 1;
}
EXPORT(const char*) GetMapResourceName(int index, int resourceIndex) {
if (resourceIndex == 0) {
return "Metal";
} else {
SetLastError("No valid map resource index");
}
return NULL;
}
EXPORT(float) GetMapResourceMax(int index, int resourceIndex) {
if (resourceIndex == 0) {
const InternalMapInfo* mapInfo = internal_getMapInfo(index);
if (mapInfo) {
return mapInfo->maxMetal;
}
} else {
SetLastError("No valid map resource index");
}
return 0.0f;
}
EXPORT(int) GetMapResourceExtractorRadius(int index, int resourceIndex) {
if (resourceIndex == 0) {
const InternalMapInfo* mapInfo = internal_getMapInfo(index);
if (mapInfo) {
return mapInfo->extractorRadius;
}
} else {
SetLastError("No valid map resource index");
}
return -1;
}
EXPORT(int) GetMapPosCount(int index) {
int count = -1;
const InternalMapInfo* mapInfo = internal_getMapInfo(index);
if (mapInfo) {
count = mapInfo->xPos.size();
}
return count;
}
//FIXME: rename to GetMapStartPosX ?
EXPORT(float) GetMapPosX(int index, int posIndex) {
const InternalMapInfo* mapInfo = internal_getMapInfo(index);
if (mapInfo) {
return mapInfo->xPos[posIndex];
}
return -1.0f;
}
//FIXME: rename to GetMapStartPosZ ?
EXPORT(float) GetMapPosZ(int index, int posIndex) {
const InternalMapInfo* mapInfo = internal_getMapInfo(index);
if (mapInfo) {
return mapInfo->zPos[posIndex];
}
return -1.0f;
}
EXPORT(float) GetMapMinHeight(const char* mapName) {
try {
CheckInit();
const std::string mapFile = GetMapFile(mapName);
ScopedMapLoader loader(mapName, mapFile);
CSMFMapFile file(mapFile);
MapParser parser(mapFile);
const SMFHeader& header = file.GetHeader();
const LuaTable rootTable = parser.GetRoot();
const LuaTable smfTable = rootTable.SubTable("smf");
if (smfTable.KeyExists("minHeight")) {
// override the header's minHeight value
return (smfTable.GetFloat("minHeight", 0.0f));
} else {
return (header.minHeight);
}
}
UNITSYNC_CATCH_BLOCKS;
return 0.0f;
}
EXPORT(float) GetMapMaxHeight(const char* mapName) {
try {
CheckInit();
const std::string mapFile = GetMapFile(mapName);
ScopedMapLoader loader(mapName, mapFile);
CSMFMapFile file(mapFile);
MapParser parser(mapFile);
const SMFHeader& header = file.GetHeader();
const LuaTable rootTable = parser.GetRoot();
const LuaTable smfTable = rootTable.SubTable("smf");
if (smfTable.KeyExists("maxHeight")) {
// override the header's maxHeight value
return (smfTable.GetFloat("maxHeight", 0.0f));
} else {
return (header.maxHeight);
}
}
UNITSYNC_CATCH_BLOCKS;
return 0.0f;
}
static std::vector<std::string> mapArchives;
EXPORT(int) GetMapArchiveCount(const char* mapName)
{
int count = -1;
try {
CheckInit();
CheckNullOrEmpty(mapName);
mapArchives = archiveScanner->GetArchives(mapName);
count = mapArchives.size();
}
UNITSYNC_CATCH_BLOCKS;
return count;
}
EXPORT(const char*) GetMapArchiveName(int index)
{
try {
CheckInit();
CheckBounds(index, mapArchives.size());
return GetStr(mapArchives[index]);
}
UNITSYNC_CATCH_BLOCKS;
return NULL;
}
EXPORT(unsigned int) GetMapChecksum(int index)
{
try {
CheckInit();
CheckBounds(index, mapNames.size());
return archiveScanner->GetArchiveCompleteChecksum(mapNames[index]);
}
UNITSYNC_CATCH_BLOCKS;
return 0;
}
EXPORT(unsigned int) GetMapChecksumFromName(const char* mapName)
{
try {
CheckInit();
return archiveScanner->GetArchiveCompleteChecksum(mapName);
}
UNITSYNC_CATCH_BLOCKS;
return 0;
}
#define RM 0x0000F800
#define GM 0x000007E0
#define BM 0x0000001F
#define RED_RGB565(x) ((x&RM)>>11)
#define GREEN_RGB565(x) ((x&GM)>>5)
#define BLUE_RGB565(x) (x&BM)
#define PACKRGB(r, g, b) (((r<<11)&RM) | ((g << 5)&GM) | (b&BM) )
// Used to return the image
static unsigned short imgbuf[1024*1024];
static unsigned short* GetMinimapSM3(std::string mapFileName, int mipLevel)
{
MapParser mapParser(mapFileName);
const std::string minimapFile = mapParser.GetRoot().GetString("minimap", "");
if (minimapFile.empty()) {
memset(imgbuf,0,sizeof(imgbuf));
return imgbuf;
}
CBitmap bm;
if (!bm.Load(minimapFile)) {
memset(imgbuf,0,sizeof(imgbuf));
return imgbuf;
}
if (1024 >> mipLevel != bm.xsize || 1024 >> mipLevel != bm.ysize)
bm = bm.CreateRescaled(1024 >> mipLevel, 1024 >> mipLevel);
unsigned short* dst = (unsigned short*)imgbuf;
unsigned char* src = bm.mem;
for (int y=0; y < bm.ysize; y++) {
for (int x=0; x < bm.xsize; x++) {
*dst = 0;
*dst |= ((src[0]>>3) << 11) & RM;
*dst |= ((src[1]>>2) << 5) & GM;
*dst |= (src[2]>>3) & BM;
dst ++;
src += 4;
}
}
return imgbuf;
}
static unsigned short* GetMinimapSMF(std::string mapFileName, int mipLevel)
{
CSMFMapFile in(mapFileName);
std::vector<uint8_t> buffer;
const int mipsize = in.ReadMinimap(buffer, mipLevel);
// Do stuff
unsigned short* colors = (unsigned short*)((void*)imgbuf);
unsigned char* temp = &buffer[0];
const int numblocks = buffer.size()/8;
for ( int i = 0; i < numblocks; i++ ) {
unsigned short color0 = (*(unsigned short*)&temp[0]);
unsigned short color1 = (*(unsigned short*)&temp[2]);
unsigned int bits = (*(unsigned int*)&temp[4]);
for ( int a = 0; a < 4; a++ ) {
for ( int b = 0; b < 4; b++ ) {
int x = 4*(i % ((mipsize+3)/4))+b;
int y = 4*(i / ((mipsize+3)/4))+a;
unsigned char code = bits & 0x3;
bits >>= 2;
if ( color0 > color1 ) {
if ( code == 0 ) {
colors[y*mipsize+x] = color0;
}
else if ( code == 1 ) {
colors[y*mipsize+x] = color1;
}
else if ( code == 2 ) {
colors[y*mipsize+x] = PACKRGB((2*RED_RGB565(color0)+RED_RGB565(color1))/3, (2*GREEN_RGB565(color0)+GREEN_RGB565(color1))/3, (2*BLUE_RGB565(color0)+BLUE_RGB565(color1))/3);
}
else {
colors[y*mipsize+x] = PACKRGB((2*RED_RGB565(color1)+RED_RGB565(color0))/3, (2*GREEN_RGB565(color1)+GREEN_RGB565(color0))/3, (2*BLUE_RGB565(color1)+BLUE_RGB565(color0))/3);
}
}
else {
if ( code == 0 ) {
colors[y*mipsize+x] = color0;
}
else if ( code == 1 ) {
colors[y*mipsize+x] = color1;
}
else if ( code == 2 ) {
colors[y*mipsize+x] = PACKRGB((RED_RGB565(color0)+RED_RGB565(color1))/2, (GREEN_RGB565(color0)+GREEN_RGB565(color1))/2, (BLUE_RGB565(color0)+BLUE_RGB565(color1))/2);
}
else {
colors[y*mipsize+x] = 0;
}
}
}
}
temp += 8;
}
return colors;
}
EXPORT(unsigned short*) GetMinimap(const char* mapName, int mipLevel)
{
try {
CheckInit();
CheckNullOrEmpty(mapName);
if (mipLevel < 0 || mipLevel > 8)
throw std::out_of_range("Miplevel must be between 0 and 8 (inclusive) in GetMinimap.");
const std::string mapFile = GetMapFile(mapName);
ScopedMapLoader mapLoader(mapName, mapFile);
unsigned short* ret = NULL;
const std::string extension = FileSystem::GetExtension(mapFile);
if (extension == "smf") {
ret = GetMinimapSMF(mapFile, mipLevel);
} else if (extension == "sm3") {
ret = GetMinimapSM3(mapFile, mipLevel);
}
return ret;
}
UNITSYNC_CATCH_BLOCKS;
return NULL;
}
EXPORT(int) GetInfoMapSize(const char* mapName, const char* name, int* width, int* height)
{
try {
CheckInit();
CheckNullOrEmpty(mapName);
CheckNullOrEmpty(name);
CheckNull(width);
CheckNull(height);
const std::string mapFile = GetMapFile(mapName);
ScopedMapLoader mapLoader(mapName, mapFile);
CSMFMapFile file(mapFile);
MapBitmapInfo bmInfo;
file.GetInfoMapSize(name, &bmInfo);
*width = bmInfo.width;
*height = bmInfo.height;
return bmInfo.width * bmInfo.height;
}
UNITSYNC_CATCH_BLOCKS;
if (width) *width = 0;
if (height) *height = 0;
return -1;
}
EXPORT(int) GetInfoMap(const char* mapName, const char* name, unsigned char* data, int typeHint)
{
int ret = -1;
try {
CheckInit();
CheckNullOrEmpty(mapName);
CheckNullOrEmpty(name);
CheckNull(data);
const std::string mapFile = GetMapFile(mapName);
ScopedMapLoader mapLoader(mapName, mapFile);
CSMFMapFile file(mapFile);
const std::string n = name;
int actualType = (n == "height" ? bm_grayscale_16 : bm_grayscale_8);
if (actualType == typeHint) {
ret = file.ReadInfoMap(n, data);
} else if (actualType == bm_grayscale_16 && typeHint == bm_grayscale_8) {
// convert from 16 bits per pixel to 8 bits per pixel
MapBitmapInfo bmInfo;
file.GetInfoMapSize(name, &bmInfo);
const int size = bmInfo.width * bmInfo.height;
if (size > 0) {
unsigned short* temp = new unsigned short[size];
if (file.ReadInfoMap(n, temp)) {
const unsigned short* inp = temp;
const unsigned short* inp_end = temp + size;
unsigned char* outp = data;
for (; inp < inp_end; ++inp, ++outp) {
*outp = *inp >> 8;
}
ret = 1;
}
delete[] temp;
}
} else if (actualType == bm_grayscale_8 && typeHint == bm_grayscale_16) {
throw content_error("converting from 8 bits per pixel to 16 bits per pixel is unsupported");
}
}
UNITSYNC_CATCH_BLOCKS;
return ret;
}
//////////////////////////
//////////////////////////
std::vector<CArchiveScanner::ArchiveData> modData;
EXPORT(int) GetPrimaryModCount()
{
int count = -1;
try {
CheckInit();
modData = archiveScanner->GetPrimaryMods();
count = modData.size();
}
UNITSYNC_CATCH_BLOCKS;
return count;
}
EXPORT(int) GetPrimaryModInfoCount(int modIndex) {
try {
CheckInit();
CheckBounds(modIndex, modData.size());
info.clear();
std::vector<InfoItem> modInfoItems = modData[modIndex].GetInfoItems();
info.insert(info.end(), modInfoItems.begin(), modInfoItems.end());
return (int)info.size();
}
UNITSYNC_CATCH_BLOCKS;
info.clear();
return -1;
}
EXPORT(const char*) GetPrimaryModName(int index)
{
DEPRECATED;
try {
CheckInit();
CheckBounds(index, modData.size());
const std::string& x = modData[index].GetName();
return GetStr(x);
}
UNITSYNC_CATCH_BLOCKS;
return NULL;
}
EXPORT(const char*) GetPrimaryModShortName(int index)
{
DEPRECATED;
try {
CheckInit();
CheckBounds(index, modData.size());
const std::string& x = modData[index].GetShortName();
return GetStr(x);
}
UNITSYNC_CATCH_BLOCKS;
return NULL;
}
EXPORT(const char*) GetPrimaryModVersion(int index)
{
DEPRECATED;
try {
CheckInit();
CheckBounds(index, modData.size());
const std::string& x = modData[index].GetVersion();
return GetStr(x);
}
UNITSYNC_CATCH_BLOCKS;
return NULL;
}
EXPORT(const char*) GetPrimaryModMutator(int index)
{
DEPRECATED;
try {
CheckInit();
CheckBounds(index, modData.size());
const std::string& x = modData[index].GetMutator();
return GetStr(x);
}
UNITSYNC_CATCH_BLOCKS;
return NULL;
}
EXPORT(const char*) GetPrimaryModGame(int index)
{
DEPRECATED;
try {
CheckInit();
CheckBounds(index, modData.size());
const std::string& x = modData[index].GetGame();
return GetStr(x);
}
UNITSYNC_CATCH_BLOCKS;
return NULL;
}
EXPORT(const char*) GetPrimaryModShortGame(int index)
{
DEPRECATED;
try {
CheckInit();
CheckBounds(index, modData.size());
const std::string& x = modData[index].GetShortGame();
return GetStr(x);
}
UNITSYNC_CATCH_BLOCKS;
return NULL;
}
EXPORT(const char*) GetPrimaryModDescription(int index)
{
DEPRECATED;
try {
CheckInit();
CheckBounds(index, modData.size());
const std::string& x = modData[index].GetDescription();
return GetStr(x);
}
UNITSYNC_CATCH_BLOCKS;
return NULL;
}
EXPORT(const char*) GetPrimaryModArchive(int index)
{
try {
CheckInit();
CheckBounds(index, modData.size());
const std::string& x = modData[index].GetDependencies()[0];
return GetStr(x);
}
UNITSYNC_CATCH_BLOCKS;
return NULL;
}
std::vector<std::string> primaryArchives;
EXPORT(int) GetPrimaryModArchiveCount(int index)
{
int count = -1;
try {
CheckInit();
CheckBounds(index, modData.size());
primaryArchives = archiveScanner->GetArchives(modData[index].GetDependencies()[0]);
count = primaryArchives.size();
}
UNITSYNC_CATCH_BLOCKS;
return count;
}
EXPORT(const char*) GetPrimaryModArchiveList(int archiveNr)
{
try {
CheckInit();
CheckBounds(archiveNr, primaryArchives.size());
LOG_L(L_DEBUG, "primary mod archive list: %s",
primaryArchives[archiveNr].c_str());
return GetStr(primaryArchives[archiveNr]);
}
UNITSYNC_CATCH_BLOCKS;
return NULL;
}
EXPORT(int) GetPrimaryModIndex(const char* name)
{
try {
CheckInit();
const std::string searchedName(name);
for (unsigned i = 0; i < modData.size(); ++i) {
if (modData[i].GetName() == searchedName)
return i;
}
}
UNITSYNC_CATCH_BLOCKS;
return -1;
}
EXPORT(unsigned int) GetPrimaryModChecksum(int index)
{
try {
CheckInit();
CheckBounds(index, modData.size());
return archiveScanner->GetArchiveCompleteChecksum(GetPrimaryModArchive(index));
}
UNITSYNC_CATCH_BLOCKS;
return 0;
}
EXPORT(unsigned int) GetPrimaryModChecksumFromName(const char* name)
{
try {
CheckInit();
return archiveScanner->GetArchiveCompleteChecksum(archiveScanner->ArchiveFromName(name));
}
UNITSYNC_CATCH_BLOCKS;
return 0;
}
//////////////////////////
//////////////////////////
EXPORT(int) GetSideCount()
{
int count = -1;
try {
CheckInit();
if (!sideParser.Load()) {
throw content_error("failed: " + sideParser.GetErrorLog());
}
count = sideParser.GetCount();
}
UNITSYNC_CATCH_BLOCKS;
return count;
}
EXPORT(const char*) GetSideName(int side)
{
try {
CheckInit();
CheckBounds(side, sideParser.GetCount());
// the full case name (not the lowered version)
return GetStr(sideParser.GetCaseName(side));
}
UNITSYNC_CATCH_BLOCKS;
return NULL;
}
EXPORT(const char*) GetSideStartUnit(int side)
{
try {
CheckInit();
CheckBounds(side, sideParser.GetCount());
return GetStr(sideParser.GetStartUnit(side));
}
UNITSYNC_CATCH_BLOCKS;
return NULL;
}
//////////////////////////
//////////////////////////
static std::vector<Option> options;
static std::set<std::string> optionsSet;
static void ParseOptions(const std::string& fileName, const std::string& fileModes, const std::string& accessModes)
{
option_parseOptions(options, fileName, fileModes, accessModes, &optionsSet);
}
static void ParseMapOptions(const std::string& mapName)
{
option_parseMapOptions(options, "MapOptions.lua", mapName, SPRING_VFS_MAP,
SPRING_VFS_MAP, &optionsSet);
}
static void CheckOptionIndex(int optIndex)
{
CheckInit();
CheckBounds(optIndex, options.size());
}
static void CheckOptionType(int optIndex, int type)
{
CheckOptionIndex(optIndex);
if (options[optIndex].typeCode != type)
throw std::invalid_argument("wrong option type");
}
EXPORT(int) GetMapOptionCount(const char* name)
{
try {
CheckInit();
CheckNullOrEmpty(name);
const std::string mapFile = GetMapFile(name);
ScopedMapLoader mapLoader(name, mapFile);
options.clear();
optionsSet.clear();
ParseMapOptions(name);
optionsSet.clear();
return options.size();
}
UNITSYNC_CATCH_BLOCKS;
options.clear();
optionsSet.clear();
return -1;
}
EXPORT(int) GetModOptionCount()
{
try {
CheckInit();
options.clear();
optionsSet.clear();
// EngineOptions must be read first, so accidentally "overloading" engine
// options with mod options with identical names is not possible.
// Both files are now optional
try {
ParseOptions("EngineOptions.lua", SPRING_VFS_MOD_BASE, SPRING_VFS_MOD_BASE);
}
UNITSYNC_CATCH_BLOCKS;
try {
ParseOptions("ModOptions.lua", SPRING_VFS_MOD, SPRING_VFS_MOD);
}
UNITSYNC_CATCH_BLOCKS;
optionsSet.clear();
return options.size();
}
UNITSYNC_CATCH_BLOCKS;
// Failed to load engineoptions
options.clear();
optionsSet.clear();
return -1;
}
EXPORT(int) GetCustomOptionCount(const char* fileName)
{
try {
CheckInit();
options.clear();
optionsSet.clear();
try {
ParseOptions(fileName, SPRING_VFS_ZIP, SPRING_VFS_ZIP);
}
UNITSYNC_CATCH_BLOCKS;
optionsSet.clear();
return options.size();
}
UNITSYNC_CATCH_BLOCKS;
// Failed to load custom options file
options.clear();
optionsSet.clear();
return -1;
}
//////////////////////////
//////////////////////////
static std::vector< std::vector<InfoItem> > luaAIInfos;
static void GetLuaAIInfo()
{
luaAIInfos = luaAIImplHandler.LoadInfos();
}
/**
* @brief Retrieve the number of LUA AIs available
* @return Zero on error; the number of LUA AIs otherwise
*
* Usually LUA AIs are shipped inside a mod, so be sure to map the mod into
* the VFS using AddArchive() or AddAllArchives() prior to using this function.
*/
static int GetNumberOfLuaAIs()
{
try {
CheckInit();
GetLuaAIInfo();
return luaAIInfos.size();
}
UNITSYNC_CATCH_BLOCKS;
return 0;
}
//////////////////////////
//////////////////////////
// Updated on every call to GetSkirmishAICount
static std::vector<std::string> skirmishAIDataDirs;
EXPORT(int) GetSkirmishAICount() {
int count = -1;
try {
CheckInit();
skirmishAIDataDirs.clear();
std::vector<std::string> dataDirs_tmp =
dataDirsAccess.FindDirsInDirectSubDirs(SKIRMISH_AI_DATA_DIR);
// filter out dirs not containing an AIInfo.lua file
std::vector<std::string>::const_iterator i;
for (i = dataDirs_tmp.begin(); i != dataDirs_tmp.end(); ++i) {
const std::string& possibleDataDir = *i;
std::vector<std::string> infoFile = CFileHandler::FindFiles(
possibleDataDir, "AIInfo.lua");
if (!infoFile.empty()) {
skirmishAIDataDirs.push_back(possibleDataDir);
}
}
sort(skirmishAIDataDirs.begin(), skirmishAIDataDirs.end());
int luaAIs = GetNumberOfLuaAIs();
//LOG_L(L_DEBUG, "GetSkirmishAICount: luaAIs: %i / skirmishAIs: %u",
// luaAIs, skirmishAIDataDirs.size());
count = skirmishAIDataDirs.size() + luaAIs;
}
UNITSYNC_CATCH_BLOCKS;
return count;
}
static void ParseInfo(const std::string& fileName,
const std::string& fileModes,
const std::string& accessModes)
{
info_parseInfo(info, fileName, fileModes, accessModes, &infoSet);
}
static void CheckSkirmishAIIndex(int aiIndex)
{
CheckInit();
int numSkirmishAIs = skirmishAIDataDirs.size() + luaAIInfos.size();
CheckBounds(aiIndex, numSkirmishAIs);
}
static bool IsLuaAIIndex(int aiIndex) {
return (((unsigned int) aiIndex) >= skirmishAIDataDirs.size());
}
static int ToPureLuaAIIndex(int aiIndex) {
return (aiIndex - skirmishAIDataDirs.size());
}
EXPORT(int) GetSkirmishAIInfoCount(int aiIndex) {
try {
CheckSkirmishAIIndex(aiIndex);
info.clear();
if (IsLuaAIIndex(aiIndex)) {
const std::vector<InfoItem>& iInfo = luaAIInfos[ToPureLuaAIIndex(aiIndex)];
info.insert(info.end(), iInfo.begin(), iInfo.end());
} else {
infoSet.clear();
ParseInfo(skirmishAIDataDirs[aiIndex] + "/AIInfo.lua",
SPRING_VFS_RAW, SPRING_VFS_RAW);
infoSet.clear();
}
return (int)info.size();
}
UNITSYNC_CATCH_BLOCKS;
info.clear();
return -1;
}
static const InfoItem* GetInfoItem(int infoIndex) {
CheckInit();
CheckBounds(infoIndex, info.size());
return &(info[infoIndex]);
}
static void CheckInfoValueType(const InfoItem* infoItem, InfoValueType requiredValueType) {
if (infoItem->valueType != requiredValueType) {
throw std::invalid_argument(
std::string("Tried to fetch info-value of type ")
+ info_convertTypeToString(infoItem->valueType)
+ " as " + info_convertTypeToString(requiredValueType) + ".");
}
}
EXPORT(const char*) GetInfoKey(int infoIndex) {
const char* key = NULL;
try {
key = GetStr(GetInfoItem(infoIndex)->key);
}
UNITSYNC_CATCH_BLOCKS;
return key;
}
EXPORT(const char*) GetInfoType(int infoIndex) {
const char* type = NULL;
try {
type = info_convertTypeToString(GetInfoItem(infoIndex)->valueType);
}
UNITSYNC_CATCH_BLOCKS;
return type;
}
EXPORT(const char*) GetInfoValue(int infoIndex) {
DEPRECATED;
const char* value = NULL;
try {
const InfoItem* infoItem = GetInfoItem(infoIndex);
value = GetStr(info_getValueAsString(infoItem));
}
UNITSYNC_CATCH_BLOCKS;
return value;
}
EXPORT(const char*) GetInfoValueString(int infoIndex) {
const char* value = NULL;
try {
const InfoItem* infoItem = GetInfoItem(infoIndex);
CheckInfoValueType(infoItem, INFO_VALUE_TYPE_STRING);
value = GetStr(infoItem->valueTypeString);
}
UNITSYNC_CATCH_BLOCKS;
return value;
}
EXPORT(int) GetInfoValueInteger(int infoIndex) {
int value = -1;
try {
const InfoItem* infoItem = GetInfoItem(infoIndex);
CheckInfoValueType(infoItem, INFO_VALUE_TYPE_INTEGER);
value = infoItem->value.typeInteger;
}
UNITSYNC_CATCH_BLOCKS;
return value;
}
EXPORT(float) GetInfoValueFloat(int infoIndex) {
float value = -1.0f;
try {
const InfoItem* infoItem = GetInfoItem(infoIndex);
CheckInfoValueType(infoItem, INFO_VALUE_TYPE_FLOAT);
value = infoItem->value.typeFloat;
}
UNITSYNC_CATCH_BLOCKS;
return value;
}
EXPORT(bool) GetInfoValueBool(int infoIndex) {
bool value = false;
try {
const InfoItem* infoItem = GetInfoItem(infoIndex);
CheckInfoValueType(infoItem, INFO_VALUE_TYPE_BOOL);
value = infoItem->value.typeBool;
}
UNITSYNC_CATCH_BLOCKS;
return value;
}
EXPORT(const char*) GetInfoDescription(int infoIndex) {
const char* desc = NULL;
try {
desc = GetStr(GetInfoItem(infoIndex)->desc);
}
UNITSYNC_CATCH_BLOCKS;
return desc;
}
EXPORT(int) GetSkirmishAIOptionCount(int aiIndex) {
try {
CheckSkirmishAIIndex(aiIndex);
options.clear();
optionsSet.clear();
if (IsLuaAIIndex(aiIndex)) {
// lua AIs do not have options
return 0;
} else {
ParseOptions(skirmishAIDataDirs[aiIndex] + "/AIOptions.lua",
SPRING_VFS_RAW, SPRING_VFS_RAW);
optionsSet.clear();
GetLuaAIInfo();
return options.size();
}
}
UNITSYNC_CATCH_BLOCKS;
options.clear();
optionsSet.clear();
return -1;
}
// Common Options Parameters
EXPORT(const char*) GetOptionKey(int optIndex)
{
try {
CheckOptionIndex(optIndex);
return GetStr(options[optIndex].key);
}
UNITSYNC_CATCH_BLOCKS;
return NULL;
}
EXPORT(const char*) GetOptionScope(int optIndex)
{
try {
CheckOptionIndex(optIndex);
return GetStr(options[optIndex].scope);
}
UNITSYNC_CATCH_BLOCKS;
return NULL;
}
EXPORT(const char*) GetOptionName(int optIndex)
{
try {
CheckOptionIndex(optIndex);
return GetStr(options[optIndex].name);
}
UNITSYNC_CATCH_BLOCKS;
return NULL;
}
EXPORT(const char*) GetOptionSection(int optIndex)
{
try {
CheckOptionIndex(optIndex);
return GetStr(options[optIndex].section);
}
UNITSYNC_CATCH_BLOCKS;
return NULL;
}
EXPORT(const char*) GetOptionStyle(int optIndex)
{
try {
CheckOptionIndex(optIndex);
return GetStr(options[optIndex].style);
}
UNITSYNC_CATCH_BLOCKS;
return NULL;
}
EXPORT(const char*) GetOptionDesc(int optIndex)
{
try {
CheckOptionIndex(optIndex);
return GetStr(options[optIndex].desc);
}
UNITSYNC_CATCH_BLOCKS;
return NULL;
}
EXPORT(int) GetOptionType(int optIndex)
{
int type = -1;
try {
CheckOptionIndex(optIndex);
type = options[optIndex].typeCode;
}
UNITSYNC_CATCH_BLOCKS;
return type;
}
// Bool Options
EXPORT(int) GetOptionBoolDef(int optIndex)
{
try {
CheckOptionType(optIndex, opt_bool);
return options[optIndex].boolDef ? 1 : 0;
}
UNITSYNC_CATCH_BLOCKS;
return 0;
}
// Number Options
EXPORT(float) GetOptionNumberDef(int optIndex)
{
float numDef = -1.0f;
try {
CheckOptionType(optIndex, opt_number);
numDef = options[optIndex].numberDef;
}
UNITSYNC_CATCH_BLOCKS;
return numDef;
}
EXPORT(float) GetOptionNumberMin(int optIndex)
{
float numMin = -1.0e30f; // FIXME error return should be -1.0f, or use FLOAT_MIN ?
try {
CheckOptionType(optIndex, opt_number);
numMin = options[optIndex].numberMin;
}
UNITSYNC_CATCH_BLOCKS;
return numMin;
}
EXPORT(float) GetOptionNumberMax(int optIndex)
{
float numMax = +1.0e30f; // FIXME error return should be -1.0f, or use FLOAT_MAX ?
try {
CheckOptionType(optIndex, opt_number);
numMax = options[optIndex].numberMax;
}
UNITSYNC_CATCH_BLOCKS;
return numMax;
}
EXPORT(float) GetOptionNumberStep(int optIndex)
{
float numStep = -1.0f;
try {
CheckOptionType(optIndex, opt_number);
numStep = options[optIndex].numberStep;
}
UNITSYNC_CATCH_BLOCKS;
return numStep;
}
// String Options
EXPORT(const char*) GetOptionStringDef(int optIndex)
{
try {
CheckOptionType(optIndex, opt_string);
return GetStr(options[optIndex].stringDef);
}
UNITSYNC_CATCH_BLOCKS;
return NULL;
}
EXPORT(int) GetOptionStringMaxLen(int optIndex)
{
int count = -1;
try {
CheckOptionType(optIndex, opt_string);
count = options[optIndex].stringMaxLen;
}
UNITSYNC_CATCH_BLOCKS;
return count;
}
// List Options
EXPORT(int) GetOptionListCount(int optIndex)
{
int count = -1;
try {
CheckOptionType(optIndex, opt_list);
count = options[optIndex].list.size();
}
UNITSYNC_CATCH_BLOCKS;
return count;
}
EXPORT(const char*) GetOptionListDef(int optIndex)
{
try {
CheckOptionType(optIndex, opt_list);
return GetStr(options[optIndex].listDef);
}
UNITSYNC_CATCH_BLOCKS;
return NULL;
}
EXPORT(const char*) GetOptionListItemKey(int optIndex, int itemIndex)
{
try {
CheckOptionType(optIndex, opt_list);
const std::vector<OptionListItem>& list = options[optIndex].list;
CheckBounds(itemIndex, list.size());
return GetStr(list[itemIndex].key);
}
UNITSYNC_CATCH_BLOCKS;
return NULL;
}
EXPORT(const char*) GetOptionListItemName(int optIndex, int itemIndex)
{
try {
CheckOptionType(optIndex, opt_list);
const vector<OptionListItem>& list = options[optIndex].list;
CheckBounds(itemIndex, list.size());
return GetStr(list[itemIndex].name);
}
UNITSYNC_CATCH_BLOCKS;
return NULL;
}
EXPORT(const char*) GetOptionListItemDesc(int optIndex, int itemIndex)
{
try {
CheckOptionType(optIndex, opt_list);
const std::vector<OptionListItem>& list = options[optIndex].list;
CheckBounds(itemIndex, list.size());
return GetStr(list[itemIndex].desc);
}
UNITSYNC_CATCH_BLOCKS;
return NULL;
}
//////////////////////////
//////////////////////////
static std::vector<std::string> modValidMaps;
static int LuaGetMapList(lua_State* L)
{
lua_newtable(L);
const int mapCount = GetMapCount();
for (int i = 0; i < mapCount; i++) {
lua_pushnumber(L, i + 1);
lua_pushstring(L, GetMapName(i));
lua_rawset(L, -3);
}
return 1;
}
static void LuaPushNamedString(lua_State* L,
const std::string& key, const std::string& value)
{
lua_pushstring(L, key.c_str());
lua_pushstring(L, value.c_str());
lua_rawset(L, -3);
}
static void LuaPushNamedNumber(lua_State* L, const std::string& key, float value)
{
lua_pushstring(L, key.c_str());
lua_pushnumber(L, value);
lua_rawset(L, -3);
}
static int LuaGetMapInfo(lua_State* L)
{
const std::string mapName = luaL_checkstring(L, 1);
InternalMapInfo mi;
if (!internal_GetMapInfo(mapName.c_str(), &mi)) {
LOG_L(L_ERROR, "LuaGetMapInfo: internal_GetMapInfo(\"%s\") failed",
mapName.c_str());
return 0;
}
lua_newtable(L);
LuaPushNamedString(L, "author", mi.author);
LuaPushNamedString(L, "desc", mi.description);
LuaPushNamedNumber(L, "tidal", mi.tidalStrength);
LuaPushNamedNumber(L, "gravity", mi.gravity);
LuaPushNamedNumber(L, "metal", mi.maxMetal);
LuaPushNamedNumber(L, "windMin", mi.minWind);
LuaPushNamedNumber(L, "windMax", mi.maxWind);
LuaPushNamedNumber(L, "mapX", mi.width);
LuaPushNamedNumber(L, "mapY", mi.height);
LuaPushNamedNumber(L, "extractorRadius", mi.extractorRadius);
lua_pushstring(L, "startPos");
lua_newtable(L);
for (size_t p = 0; p < mi.xPos.size(); p++) {
lua_pushnumber(L, p + 1);
lua_newtable(L);
LuaPushNamedNumber(L, "x", mi.xPos[p]);
LuaPushNamedNumber(L, "z", mi.zPos[p]);
lua_rawset(L, -3);
}
lua_rawset(L, -3);
return 1;
}
EXPORT(int) GetModValidMapCount()
{
int count = -1;
try {
CheckInit();
modValidMaps.clear();
LuaParser luaParser("ValidMaps.lua", SPRING_VFS_MOD, SPRING_VFS_MOD);
luaParser.GetTable("Spring");
luaParser.AddFunc("GetMapList", LuaGetMapList);
luaParser.AddFunc("GetMapInfo", LuaGetMapInfo);
luaParser.EndTable();
if (!luaParser.Execute()) {
throw content_error("luaParser.Execute() failed: " + luaParser.GetErrorLog());
}
const LuaTable root = luaParser.GetRoot();
if (!root.IsValid()) {
throw content_error("root table invalid");
}
for (int index = 1; root.KeyExists(index); index++) {
const std::string map = root.GetString(index, "");
if (!map.empty()) {
modValidMaps.push_back(map);
}
}
count = modValidMaps.size();
}
UNITSYNC_CATCH_BLOCKS;
return count;
}
EXPORT(const char*) GetModValidMap(int index)
{
try {
CheckInit();
CheckBounds(index, modValidMaps.size());
return GetStr(modValidMaps[index]);
}
UNITSYNC_CATCH_BLOCKS;
return NULL;
}
//////////////////////////
//////////////////////////
static std::map<int, CFileHandler*> openFiles;
static int nextFile = 0;
static std::vector<std::string> curFindFiles;
static void CheckFileHandle(int file)
{
CheckInit();
if (openFiles.find(file) == openFiles.end())
throw content_error("Unregistered file handle. Pass a file handle returned by OpenFileVFS.");
}
EXPORT(int) OpenFileVFS(const char* name)
{
try {
CheckInit();
CheckNullOrEmpty(name);
LOG_L(L_DEBUG, "OpenFileVFS: %s", name);
CFileHandler* fh = new CFileHandler(name);
if (!fh->FileExists()) {
delete fh;
throw content_error("File '" + std::string(name) + "' does not exist");
}
nextFile++;
openFiles[nextFile] = fh;
return nextFile;
}
UNITSYNC_CATCH_BLOCKS;
return 0;
}
EXPORT(void) CloseFileVFS(int file)
{
try {
CheckFileHandle(file);
LOG_L(L_DEBUG, "CloseFileVFS: %d", file);
delete openFiles[file];
openFiles.erase(file);
}
UNITSYNC_CATCH_BLOCKS;
}
EXPORT(int) ReadFileVFS(int file, unsigned char* buf, int numBytes)
{
try {
CheckFileHandle(file);
CheckNull(buf);
CheckPositive(numBytes);
LOG_L(L_DEBUG, "ReadFileVFS: %d", file);
CFileHandler* fh = openFiles[file];
return fh->Read(buf, numBytes);
}
UNITSYNC_CATCH_BLOCKS;
return -1;
}
EXPORT(int) FileSizeVFS(int file)
{
try {
CheckFileHandle(file);
LOG_L(L_DEBUG, "FileSizeVFS: %d", file);
CFileHandler* fh = openFiles[file];
return fh->FileSize();
}
UNITSYNC_CATCH_BLOCKS;
return -1;
}
EXPORT(int) InitFindVFS(const char* pattern)
{
try {
CheckInit();
CheckNullOrEmpty(pattern);
std::string path = FileSystem::GetDirectory(pattern);
std::string patt = FileSystem::GetFilename(pattern);
LOG_L(L_DEBUG, "InitFindVFS: %s", pattern);
curFindFiles = CFileHandler::FindFiles(path, patt);
return 0;
}
UNITSYNC_CATCH_BLOCKS;
return -1;
}
EXPORT(int) InitDirListVFS(const char* path, const char* pattern, const char* modes)
{
try {
CheckInit();
if (path == NULL) { path = ""; }
if (modes == NULL) { modes = SPRING_VFS_ALL; }
if (pattern == NULL) { pattern = "*"; }
LOG_L(L_DEBUG, "InitDirListVFS: '%s' '%s' '%s'", path, pattern, modes);
curFindFiles = CFileHandler::DirList(path, pattern, modes);
return 0;
}
UNITSYNC_CATCH_BLOCKS;
return -1;
}
EXPORT(int) InitSubDirsVFS(const char* path, const char* pattern, const char* modes)
{
try {
CheckInit();
if (path == NULL) { path = ""; }
if (modes == NULL) { modes = SPRING_VFS_ALL; }
if (pattern == NULL) { pattern = "*"; }
LOG_L(L_DEBUG, "InitSubDirsVFS: '%s' '%s' '%s'", path, pattern, modes);
curFindFiles = CFileHandler::SubDirs(path, pattern, modes);
return 0;
}
UNITSYNC_CATCH_BLOCKS;
return -1;
}
EXPORT(int) FindFilesVFS(int file, char* nameBuf, int size)
{
try {
CheckInit();
CheckNull(nameBuf);
CheckPositive(size);
LOG_L(L_DEBUG, "FindFilesVFS: %d", file);
if ((unsigned)file >= curFindFiles.size()) {
return 0;
}
safe_strzcpy(nameBuf, curFindFiles[file], size);
return file + 1;
}
UNITSYNC_CATCH_BLOCKS;
return 0;
}
//////////////////////////
//////////////////////////
static std::map<int, IArchive*> openArchives;
static int nextArchive = 0;
static void CheckArchiveHandle(int archive)
{
CheckInit();
if (openArchives.find(archive) == openArchives.end()) {
throw content_error("Unregistered archive handle. Pass an archive handle returned by OpenArchive.");
}
}
EXPORT(int) OpenArchive(const char* name)
{
try {
CheckInit();
CheckNullOrEmpty(name);
IArchive* a = archiveLoader.OpenArchive(name);
if (!a) {
throw content_error("Archive '" + std::string(name) + "' could not be opened");
}
nextArchive++;
openArchives[nextArchive] = a;
return nextArchive;
}
UNITSYNC_CATCH_BLOCKS;
return 0;
}
EXPORT(int) OpenArchiveType(const char* name, const char* type)
{
try {
CheckInit();
CheckNullOrEmpty(name);
CheckNullOrEmpty(type);
IArchive* a = archiveLoader.OpenArchive(name, type);
if (!a) {
throw content_error("Archive '" + std::string(name) + "' could not be opened");
}
nextArchive++;
openArchives[nextArchive] = a;
return nextArchive;
}
UNITSYNC_CATCH_BLOCKS;
return 0;
}
EXPORT(void) CloseArchive(int archive)
{
try {
CheckArchiveHandle(archive);
delete openArchives[archive];
openArchives.erase(archive);
}
UNITSYNC_CATCH_BLOCKS;
}
EXPORT(int) FindFilesArchive(int archive, int file, char* nameBuf, int* size)
{
try {
CheckArchiveHandle(archive);
CheckNull(nameBuf);
CheckNull(size);
IArchive* arch = openArchives[archive];
LOG_L(L_DEBUG, "FindFilesArchive: %d", archive);
if (file < arch->NumFiles())
{
const int nameBufSize = *size;
std::string fileName;
int fileSize;
arch->FileInfo(file, fileName, fileSize);
*size = fileSize;
if (nameBufSize > fileName.length())
{
STRCPY(nameBuf, fileName.c_str());
return ++file;
}
else
{
SetLastError("name-buffer is too small");
}
}
return 0;
}
UNITSYNC_CATCH_BLOCKS;
return 0;
}
EXPORT(int) OpenArchiveFile(int archive, const char* name)
{
int fileID = -1;
try {
CheckArchiveHandle(archive);
CheckNullOrEmpty(name);
IArchive* arch = openArchives[archive];
fileID = arch->FindFile(name);
if (fileID == arch->NumFiles()) {
fileID = -2;
}
}
UNITSYNC_CATCH_BLOCKS;
return fileID;
}
EXPORT(int) ReadArchiveFile(int archive, int file, unsigned char* buffer, int numBytes)
{
try {
CheckArchiveHandle(archive);
CheckNull(buffer);
CheckPositive(numBytes);
IArchive* a = openArchives[archive];
std::vector<uint8_t> buf;
if (!a->GetFile(file, buf))
return -1;
std::memcpy(buffer, &buf[0], std::min(buf.size(), (size_t)numBytes));
return std::min(buf.size(), (size_t)numBytes);
}
UNITSYNC_CATCH_BLOCKS;
return -1;
}
EXPORT(void) CloseArchiveFile(int archive, int file)
{
try {
// nuting
}
UNITSYNC_CATCH_BLOCKS;
}
EXPORT(int) SizeArchiveFile(int archive, int file)
{
try {
CheckArchiveHandle(archive);
IArchive* a = openArchives[archive];
std::string name;
int s;
a->FileInfo(file, name, s);
return s;
}
UNITSYNC_CATCH_BLOCKS;
return -1;
}
//////////////////////////
//////////////////////////
char strBuf[STRBUF_SIZE];
/// defined in unitsync.h. Just returning str.c_str() does not work
const char* GetStr(std::string str)
{
if (str.length() + 1 > STRBUF_SIZE) {
sprintf(strBuf, "Increase STRBUF_SIZE (needs "_STPF_" bytes)", str.length() + 1);
} else {
STRCPY(strBuf, str.c_str());
}
return strBuf;
}
void PrintLoadMsg(const char* text)
{
}
//////////////////////////
//////////////////////////
EXPORT(void) SetSpringConfigFile(const char* fileNameAsAbsolutePath)
{
ConfigHandler::Instantiate(fileNameAsAbsolutePath);
}
static void CheckConfigHandler()
{
if (!configHandler)
throw std::logic_error("Unitsync config handler not initialized, check config source.");
}
EXPORT(const char*) GetSpringConfigFile()
{
try {
CheckConfigHandler();
return GetStr(configHandler->GetConfigFile());
}
UNITSYNC_CATCH_BLOCKS;
return NULL;
}
EXPORT(const char*) GetSpringConfigString(const char* name, const char* defValue)
{
try {
CheckConfigHandler();
std::string res = configHandler->IsSet(name) ? configHandler->GetString(name) : defValue;
return GetStr(res);
}
UNITSYNC_CATCH_BLOCKS;
return defValue;
}
EXPORT(int) GetSpringConfigInt(const char* name, const int defValue)
{
try {
CheckConfigHandler();
return configHandler->IsSet(name) ? configHandler->GetInt(name) : defValue;
}
UNITSYNC_CATCH_BLOCKS;
return defValue;
}
EXPORT(float) GetSpringConfigFloat(const char* name, const float defValue)
{
try {
CheckConfigHandler();
return configHandler->IsSet(name) ? configHandler->GetFloat(name) : defValue;
}
UNITSYNC_CATCH_BLOCKS;
return defValue;
}
EXPORT(void) SetSpringConfigString(const char* name, const char* value)
{
try {
CheckConfigHandler();
configHandler->SetString( name, value );
}
UNITSYNC_CATCH_BLOCKS;
}
EXPORT(void) SetSpringConfigInt(const char* name, const int value)
{
try {
CheckConfigHandler();
configHandler->Set(name, value);
}
UNITSYNC_CATCH_BLOCKS;
}
EXPORT(void) SetSpringConfigFloat(const char* name, const float value)
{
try {
CheckConfigHandler();
configHandler->Set(name, value);
}
UNITSYNC_CATCH_BLOCKS;
}
Jump to Line
Something went wrong with that request. Please try again.