Files
commandergenius/project/jni/application/enigma/src/lev/Proxy.cpp
2010-10-15 14:23:30 +03:00

1173 lines
47 KiB
C++

/*
* Copyright (C) 2006 Ronald Lamprecht
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "lev/Proxy.hh"
#include "ecl_system.hh"
#include "errors.hh"
#include "gui/ErrorMenu.hh"
#include "lua.hh"
#include "main.hh"
#include "nls.hh"
#include "oxyd_internal.hh"
#include "server.hh"
#include "utilXML.hh"
#include "Utf8ToXML.hh"
#include "XMLtoUtf8.hh"
#include "lev/Index.hh"
#include <cassert>
#include <fstream>
#include <iostream>
#include <sstream>
#include <xercesc/dom/DOM.hpp>
#include <xercesc/util/XMLException.hpp>
#include <xercesc/util/XMLDouble.hpp>
#include <xercesc/util/XMLString.hpp>
#include <xercesc/util/XMLUniDefs.hpp>
#include <xercesc/framework/MemBufInputSource.hpp>
#include <xercesc/framework/Wrapper4InputSource.hpp>
#ifndef CXXLUA
extern "C" {
#include "lua.h"
#include "lauxlib.h"
}
#else
#include "lua.h"
#include "lauxlib.h"
#endif
using namespace std;
using namespace enigma;
XERCES_CPP_NAMESPACE_USE
namespace enigma { namespace lev {
// http://enigma-game.org/schema/level/1
const XMLCh Proxy::levelNS[] = {
chLatin_h, chLatin_t, chLatin_t, chLatin_p, chColon, chForwardSlash,
chForwardSlash, chLatin_e, chLatin_n, chLatin_i, chLatin_g, chLatin_m,
chLatin_a, chDash, chLatin_g, chLatin_a, chLatin_m, chLatin_e,
chPeriod, chLatin_o, chLatin_r, chLatin_g, chForwardSlash,
chLatin_s, chLatin_c, chLatin_h, chLatin_e, chLatin_m, chLatin_a,
chForwardSlash, chLatin_l, chLatin_e, chLatin_v, chLatin_e,
chLatin_l, chForwardSlash, chDigit_1, chNull
};
std::map<std::string, Proxy *> Proxy::cache;
std::vector<Proxy *> Proxy::loadedLibs;
std::vector<Proxy *> Proxy::registeredLibs;
void Proxy::releaseLibs() {
for (int i = 0; i << loadedLibs.size(); i++)
delete loadedLibs[i];
loadedLibs.clear();
for (int i = 0; i << registeredLibs.size(); i++)
delete registeredLibs[i];
registeredLibs.clear();
}
Proxy *Proxy::currentLevel = NULL;
Proxy *Proxy::loadedLevel() {
return currentLevel;
}
Proxy * Proxy::registerLevel(std::string levelPath, std::string indexPath,
std::string levelId, std::string levelTitle, std::string levelAuthor,
int levelScoreVersion, int levelRelease, bool levelHasEasymode,
GameType levelCompatibilty, levelStatusType status, int levelRevision) {
Proxy *theProxy;
pathType thePathType = pt_resource;
std::string theNormLevelPath;
// Normalize level path
if (indexPath == "#commandline") {
if (levelPath.find("://") != std::string::npos) {
thePathType = pt_url; // http, ftp, https
}
else
thePathType = pt_absolute;
theNormLevelPath = levelPath;
} else {
if (levelPath.find("://") != std::string::npos) {
thePathType = pt_url; // http, ftp, https
theNormLevelPath = levelPath;
} else if (!levelPath.empty() && levelPath[0] == '#'){
thePathType = pt_oxyd;
theNormLevelPath = levelPath;
} else {
thePathType = pt_resource;
if (levelPath.find("./") == 0) {
assert(indexPath != "#history"); // no relative paths on history
// relative level path
if (indexPath.empty())
// levelpack on data/levels directory - depreceated
theNormLevelPath = levelPath.substr(2);
else
// a subdirectory or zip
theNormLevelPath = indexPath + levelPath.substr(1);
} else {
theNormLevelPath = levelPath;
}
}
}
// search for existing proxy
// we use a combined key to support pack indices to reference
// different releases of a level at the same path -- this is an
// inconsistency in the users level collection as different releases
// should have different filenames -- but we have to handle it:
// we generate proxys for each registration and decide on level load
// evaluating the level metadata which proxy was right.
char txt[5];
snprintf(txt, sizeof(txt), "%d", levelRelease);
std::string cacheKey = theNormLevelPath + levelId + txt;
std::map<std::string, Proxy *>::iterator i = cache.find(cacheKey);
if (i != cache.end()) {
Proxy * candidate = i->second;
return candidate;
}
// create new proxy
theProxy = new Proxy(false, thePathType, theNormLevelPath, levelId, levelTitle,
levelAuthor, levelScoreVersion, levelRelease, levelHasEasymode,
levelCompatibilty, status, levelRevision);
cache.insert(std::make_pair(cacheKey, theProxy));
return theProxy;
}
Proxy * Proxy::autoRegisterLevel(std::string indexPath, std::string filename) {
Proxy *theProxy = new Proxy(false, pt_resource, indexPath + "/" + filename , "", "",
"unknown", 1, 0, false, GAMET_UNKNOWN, STATUS_UNKNOWN);
try {
theProxy->loadMetadata(true);
}
catch (XLevelLoading &err) {
Log << "autoRegisterLevel load error on '" << indexPath << "/"<< filename << "\n";
std::string message = _("Error on auto registration of levelfile: ");
message += indexPath + "/" + filename + ".[xml/lua]\n\n";
message += _("Note: the level will not show up in the \"Auto Folder\" levelpack!\n\n");
message += err.what();
gui::ErrorMenu m(message, N_("Continue"));
m.manage();
}
if (theProxy->getId().empty() || theProxy->isLibraryFlag) {
delete theProxy;
theProxy = NULL;
} else {
// eliminate duplicates and register
// Log << "autoRegisterLevel register '" << indexPath << "/"<< filename << " Title: " << theProxy->getTitle() <<"\n";
std::string cacheKey = theProxy->getNormLevelPath() + theProxy->getId() +
ecl::strf("%d", theProxy->getReleaseVersion());
std::map<std::string, Proxy *>::iterator i = cache.find(cacheKey);
if (i != cache.end()) {
delete theProxy;
theProxy = i->second;
} else {
cache.insert(std::make_pair(cacheKey, theProxy));
}
}
return theProxy;
}
struct LowerCaseString {
std::string low;
LowerCaseString(const std::string& s) : low(s) {
for (std::string::iterator i = low.begin(); i != low.end(); ++i)
*i = tolower(*i);
}
bool containedBy(LowerCaseString other) const {
return other.low.find(low) != string::npos;
}
};
Index * searchIndex;
LowerCaseString searchText("");
void do_search(const std::map<std::string, Proxy *>::value_type pair) {
Proxy * candidate = pair.second;
if (searchText.containedBy(candidate->getNormLevelPath()) ||
searchText.containedBy(candidate->getTitle()) ||
searchText.containedBy(candidate->getId()) ||
searchText.containedBy(candidate->getAuthor())) {
searchIndex->appendProxy(candidate);
// Log << "Search result: " << pair.first << " - is - " << candidate->getTitle() << "\n";
}
}
std::string Proxy::search(std::string text) {
searchIndex = Index::findIndex("Search Result");
// assert searchIndex
searchIndex->clear();
searchText = LowerCaseString(text);
std::for_each(cache.begin(), cache.end(), do_search);
return (searchIndex->size() > 0) ? searchIndex->getName() : "";
}
void Proxy::countLevels() {
int countProxy = 0;
int countLegacy = 0;
int countStable = 0;
int countReleased = 0;
map<std::string, short> ids;
for (std::map<std::string, Proxy *>::iterator i= cache.begin(); i != cache.end(); i++) {
Proxy * candidate = (*i).second;
countProxy++;
if (candidate->getNormPathType() == pt_oxyd)
countLegacy++;
else if (candidate->getLevelStatus() == STATUS_STABLE)
ids[candidate->getId()];
else if (candidate->getLevelStatus() == STATUS_RELEASED)
++ids[candidate->getId()];
}
for (std::map<std::string, short>::iterator i= ids.begin(); i != ids.end(); i++) {
if ((*i).second == 0)
countStable++;
else
countReleased++;
}
Log << "Proxy count: " << countReleased <<" released, "
<< countStable << " stable, " << countLegacy << " legacy, "
<< countProxy << " total\n";
}
std::set<std::string> Proxy::getLevelIds(bool withEasy) {
std::set<std::string> result;
for (std::map<std::string, Proxy *>::iterator i= cache.begin(); i != cache.end(); i++) {
if ((*i).second->hasEasymode() == withEasy)
result.insert((*i).second->getId());
}
return result;
}
std::set<Proxy *> Proxy::getProxies() {
std::set<Proxy *> result;
for (std::map<std::string, Proxy *>::iterator i= cache.begin(); i != cache.end(); i++) {
result.insert((*i).second);
}
return result;
}
Proxy::Proxy(bool proxyIsLibrary, pathType thePathType, std::string theNormLevelPath,
std::string levelId, std::string levelTitle, std::string levelAuthor,
int levelScoreVersion, int levelRelease, bool levelHasEasymode,
GameType levelCompatibilty,levelStatusType status, int levelRevision) :
isLibraryFlag (proxyIsLibrary), normPathType(thePathType), normLevelPath(theNormLevelPath),
id(levelId), title(levelTitle), author(levelAuthor),
scoreVersion(levelScoreVersion), releaseVersion(levelRelease),
revisionNumber(levelRevision), hasEasymodeFlag(levelHasEasymode),
engineCompatibility(levelCompatibilty), levelStatus (status),
scoreUnit (duration), doc(NULL) {
}
Proxy::~Proxy() {
this->release();
}
void Proxy::release() {
if (doc != NULL) {
doc->release();
doc = NULL;
}
if (this == currentLevel) {
currentLevel = NULL;
releaseLibs();
}
}
std::string Proxy::getNormLevelPath() {
return normLevelPath;
}
std::string Proxy::getLocalSubstitutionLevelPath() {
std::string result = getNormLevelPath();
std::string::size_type pos;
// substitute url protocol "http://" by "http/"
pos = result.find("://");
if (pos != std::string::npos)
result.replace(pos, 2,"");
// substitute all filesystem uncommon chars to '_'
const std::string validChars("\\_-/.~#0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ");
pos = result.find_first_not_of(validChars, 0);
while (pos != std::string::npos) {
result.replace(pos, 1,"~");
pos = result.find_first_not_of(validChars, pos+1);
}
return result;
}
Proxy::pathType Proxy::getNormPathType() {
return normPathType;
}
std::string Proxy::getAbsLevelPath() {
return absLevelPath;
}
void Proxy::loadLevel() {
load(false, true);
}
Proxy * Proxy::copy(std::string newBasePath, std::string newPackPath, bool backup) {
bool useFileLoader = false;
bool isXML = true;
std::auto_ptr<std::istream> isptr;
ByteVec levelCode;
std::string absLevelPath = "";
std::string filename;
std::string filenameBase; // without extension
if (normPathType == pt_oxyd) {
return NULL;
// resolve resource path to filepath
} else if (normPathType == pt_absolute || normPathType == pt_url) {
absLevelPath = normLevelPath;
} else if(normPathType == pt_resource) {
if(!app.resourceFS->findFile ("levels/" + normLevelPath + ".xml",
absLevelPath, isptr) &&
!app.resourceFS->findFile ("levels/" + normLevelPath + ".lua",
absLevelPath, isptr)) {
return NULL;
}
} else
// error unknown type
return NULL;
size_t lastSlash = absLevelPath.rfind ('/');
if (lastSlash == std::string::npos) {
filename = absLevelPath;
} else {
filename = absLevelPath.substr(lastSlash + 1);
}
// xml or lua
size_t extbegin = filename.rfind('.');
if (extbegin != string::npos) {
std::string ext = filename.substr(extbegin);
if ( ext != ".lua" && ext!= ".ell" && ext != ".xml" && ext != ".elx") {
return NULL;
} else {
filenameBase = filename.substr(0, extbegin);
}
} else {
return NULL;
}
// load
if (normPathType != pt_url) {
// preload plain Lua file or zipped level
if (isptr.get() != NULL) {
// zipped file
Readfile (*isptr, levelCode);
} else {
// plain file
basic_ifstream<char> ifs(absLevelPath.c_str(), ios::binary | ios::in);
Readfile (ifs, levelCode);
}
std::string oPath = newBasePath + "/" + newPackPath + "/" + filename;
if (backup) {
std::remove((oPath + "~").c_str());
std::rename(oPath.c_str(), (oPath + "~").c_str());
}
basic_ofstream<char> ofs(oPath.c_str(), ios::binary | ios::out);
for (int i = 0; i < levelCode.size(); i++) {
ofs << levelCode[i];
}
ofs.close();
} else {
// load XML via Xerces
return NULL;
}
// create new proxy
return registerLevel(std::string("./")+filenameBase, newPackPath, id, title,
author, scoreVersion, releaseVersion, hasEasymodeFlag,
engineCompatibility, levelStatus, revisionNumber);
}
void Proxy::loadMetadata(bool expectLevel) {
load(true, expectLevel);
}
void Proxy::load(bool onlyMetadata, bool expectLevel) {
if (doc != NULL) {
if (onlyMetadata)
// doc exists - metadata are loaded
return;
if (!isLibraryFlag != expectLevel)
throw XLevelLoading(ecl::strf("Level - Library mismatch on %s", normLevelPath.c_str()));
// doc exists - we can directly load
loadDoc();
return;
}
bool useFileLoader = false;
bool isXML = true;
std::auto_ptr<std::istream> isptr;
ByteVec levelCode;
std::string errMessage;
absLevelPath = "";
// release current proxy
if (!isLibraryFlag) {
if (currentLevel != NULL)
currentLevel->release();
currentLevel = this;
}
// handle oxyd first
if (normPathType == pt_oxyd) {
if(onlyMetadata)
return;
// use oxyd loader
std::string::size_type posSecondHash = normLevelPath.find('#',1);
if (posSecondHash == string::npos)
throw XLevelLoading("Bad filename for oxyd level: " + normLevelPath );
std::string packName = normLevelPath.substr(1, posSecondHash -1);
std::string levelNumber = normLevelPath.substr(posSecondHash + 1);
if (Index * oxydIndex = Index::findIndex(packName)) {
dynamic_cast<oxyd::LevelPack_Oxyd *>(oxydIndex)->load_oxyd_level(atoi(levelNumber.c_str()));
} else {
throw XLevelLoading("Missing oxyd levelpack for: " + normLevelPath);
}
return;
// resolve resource path to filepath
} else if (normPathType == pt_absolute || normPathType == pt_url) {
absLevelPath = normLevelPath;
} else if(normPathType == pt_resource) {
if(!app.resourceFS->findFile ("levels/" + normLevelPath + ".xml",
absLevelPath, isptr) &&
!app.resourceFS->findFile ("levels/" + normLevelPath + ".lua",
absLevelPath, isptr)) {
std::string type = isLibraryFlag ? "library " : "level ";
throw XLevelLoading("Could not find " + type + normLevelPath );
}
} else
// error unknown type
return;
// xml or lua
size_t extbegin = absLevelPath.rfind ('.');
if (extbegin != string::npos) {
string ext = absLevelPath.substr (extbegin);
if (normPathType != pt_url && (ext == ".lua" || ext == ".ell")) {
useFileLoader = true;
isXML = false; // preliminary - may still be lua commented xml
} else if (ext == ".xml" || ext == ".elx") {
isXML = true;
// use file loader only for zipped xml files
useFileLoader = (isptr.get() != NULL) ? true : false;
} else {
throw XLevelLoading ("Unknown file extension in " + absLevelPath);
}
} else {
throw XLevelLoading ("Unknown file extension in " + absLevelPath);
}
// load
if (useFileLoader) {
// preload plain Lua file or zipped level
if (isptr.get() != NULL) {
// zipped file
Readfile (*isptr, levelCode);
} else {
// plain file
basic_ifstream<char> ifs(absLevelPath.c_str(), ios::binary | ios::in);
Readfile (ifs, levelCode);
}
if(!isXML) {
if(levelCode.size() >= 8 && std::string("--xml-- ").compare(
0, 8, &(levelCode[0]), 8) == 0) {
isXML = true;
// delete lua "--xml-- " comments in level code
int s = levelCode.size();
int i = 0;
int j = 0;
for (i=8, j=0; i < s;) {
char c = levelCode[j++] = levelCode[i++];
if(c == '\n') {
if(s >= i+8 && std::string("--xml-- ").compare(
0, 8, &(levelCode[i]), 8) == 0) {
i += 8;
}
}
}
levelCode.resize(j);
} else if (!onlyMetadata){
// handle pure lua
// load plain lua file
doc = NULL;
const char *buffer = reinterpret_cast<const char *>(&levelCode[0]);
// add debugging info to lua code
std::string luaCode = "--@" + absLevelPath + "\n" +
buffer;
lua_State *L = lua::LevelState();
if (luaL_dostring(L, luaCode.c_str() ) != 0) {
lua_setglobal (L, "_LASTERROR");
throw XLevelLoading(lua::LastError(L));
}
} else {
// ensure that metadata are consistent - called for all
// new commanline lua levels
if (releaseVersion == 0)
releaseVersion = 1;
}
}
}
if (isXML) {
try {
std::ostringstream errStream;
app.domParserErrorHandler->resetErrors();
app.domParserErrorHandler->reportToOstream(&errStream);
app.domParserSchemaResolver->resetResolver();
app.domParserSchemaResolver->addSchemaId("level.xsd","level.xsd");
if (!useFileLoader) {
// local xml file or URL
doc = app.domParser->parseURI(absLevelPath.c_str());
} else {
// preloaded lua-commented xml or zipped xml
#if _XERCES_VERSION >= 30000
std::auto_ptr<DOMLSInput> domInputLevelSource ( new Wrapper4InputSource(
new MemBufInputSource(reinterpret_cast<const XMLByte *>(&(levelCode[0])),
levelCode.size(), absLevelPath.c_str(), false)));
doc = app.domParser->parse(domInputLevelSource.get());
#else
std::auto_ptr<Wrapper4InputSource> domInputLevelSource ( new Wrapper4InputSource(
new MemBufInputSource(reinterpret_cast<const XMLByte *>(&(levelCode[0])),
levelCode.size(), absLevelPath.c_str(), false)));
doc = app.domParser->parse(*domInputLevelSource);
#endif
}
if (doc != NULL && !app.domParserErrorHandler->getSawErrors()) {
infoElem = reinterpret_cast<DOMElement *>(doc->getElementsByTagNameNS(
levelNS, Utf8ToXML("info").x_str())->item(0));
stringList = doc->getElementsByTagNameNS(levelNS,
Utf8ToXML("string").x_str());
}
if(app.domParserErrorHandler->getSawErrors()) {
errMessage = errStream.str();
}
app.domParserErrorHandler->reportToNull(); // do not report to errStream any more
}
catch (...) {
errMessage = "Unexpected XML Exception on load of level\n";
}
if (!errMessage.empty()) {
release(); // empty or errornous doc
Log << errMessage; // make long error messages readable
throw XLevelLoading (errMessage);
// todo: check metadata, handle shadowed levels
} else {
// check metadata - currently just overwrite
isLibraryFlag = (getType() == "library") ? true : false;
if (!updateReleaseVersion()) {
release(); // avoid load success on a second read attempt
throw XLevelLoading(ecl::strf("Release version mismatch on %s: requested %d\n", normLevelPath.c_str(), releaseVersion));
}
if (!updateId()) {
release(); // avoid load success on a second read attempt
throw XLevelLoading(ecl::strf("Id mismatch on %s: requested %s\n", normLevelPath.c_str(), id.c_str()));
}
getTitle();
getScoreVersion();
getRevisionNumber();
getLevelStatus();
getAuthor();
hasEasymode();
getScoreUnit();
getEngineCompatibility();
if (!onlyMetadata){
if (!isLibraryFlag != expectLevel)
throw XLevelLoading(ecl::strf("Level - Library mismatch on %s", normLevelPath.c_str()));
loadDoc();
}
}
}
}
void Proxy::loadDoc() {
if (getEnigmaCompatibility() > ENIGMACOMPATIBITLITY)
throw XLevelLoading(ecl::strf("Level is incompatible: %s requires Enigma %.2f or above",
absLevelPath.c_str(), getEnigmaCompatibility()));
if (this == currentLevel) {
server::SetCompatibility(this);
}
processDependencies();
loadLuaCode();
}
void Proxy::processDependencies() {
// cleanup on level but not on libs
if (this == currentLevel) {
// cleanup all lib proxies loaded by previous load
releaseLibs();
}
if (doc != NULL) {
DOMNodeList *depList = infoElem->getElementsByTagNameNS(
levelNS, Utf8ToXML("dependency").x_str());
for (int i = 0, l = depList->getLength(); i < l; i++) {
DOMElement *depElem = reinterpret_cast<DOMElement *>(depList->item(i));
std::string depPath;
std::string depId;
int depRelease;
bool depPreload;
std::string depUrl;
depPath = XMLtoUtf8(depElem->getAttributeNS(levelNS,
Utf8ToXML("path").x_str())).c_str();
depId = XMLtoUtf8(depElem->getAttributeNS(levelNS,
Utf8ToXML("id").x_str())).c_str();
depRelease = XMLString::parseInt(depElem->getAttributeNS(levelNS,
Utf8ToXML("release").x_str()));
depPreload = boolValue(depElem->getAttributeNS(levelNS,
Utf8ToXML("preload").x_str()));
depUrl = XMLtoUtf8(depElem->getAttributeNS(levelNS,
Utf8ToXML("url").x_str())).c_str();
// Log << "Deps: Path="<<depPath<<" Id="<< depId <<" Rel="<< depRelease <<" Prel="<< depPreload<< " Url=" << depUrl<<"\n";
// load every dependency just once and break circular dependencies
// by central load via Level
currentLevel->registerPreloadDependency(depPath, depId, depRelease,
depPreload, depUrl);
}
}
}
void Proxy::registerPreloadDependency(std::string depPath, std::string depId,
int depRelease, bool depPreload, std::string depUrl) {
// check if lib is already registered
for (int i=0 ; i<loadedLibs.size(); i++) {
if (loadedLibs[i]->getId() == depId)
if (loadedLibs[i]->getReleaseVersion() != depRelease)
throw XLevelLoading(ecl::strf("declaration of incompatible library version: '%s' release %d - release %d already loaded",
depId.c_str(), depRelease, loadedLibs[i]->getReleaseVersion()));
else
return;
}
for (int i=0 ; i<registeredLibs.size(); i++) {
if (registeredLibs[i]->getId() == depId)
if (registeredLibs[i]->getReleaseVersion() != depRelease) {
throw XLevelLoading(ecl::strf("declaration of incompatible library version: '%s' release %d - release %d already registered",
depId.c_str(), depRelease, registeredLibs[i]->getReleaseVersion()));
} else if (depPreload == true) {
// same lib but now load it directly - delete the no preload entry
registeredLibs[i] = registeredLibs[registeredLibs.size() - 1];
registeredLibs.pop_back();
break; // end search and do load lib
} else {
// same lib and again no preload
return;
}
}
// resolve relative lib paths
if (depPath.find("./") == 0) {
// relative lib path
std::string levelDir;
std::string levelFilename;
if (ecl::split_path(normLevelPath, &levelDir, &levelFilename))
// the level is on subdirectory or in a zip
depPath = levelDir + "/" + depPath.substr(2);
else
// level on data/levels directory - depreceated
depPath = depPath.substr(2);
}
// find lib in requested release version without or with release number
// in filename
Proxy * depProxy = new Proxy(true, depPath.empty() ? pt_url : pt_resource,
(depPath.empty() ? depUrl : depPath) + ecl::strf("_%d",depRelease),
depId, "", "", 0, depRelease, false, GAMET_ENIGMA, STATUS_UNKNOWN);
try {
depProxy->loadMetadata(false);
} catch (XLevelLoading &err) {
delete depProxy;
depProxy = new Proxy(true, depPath.empty() ? pt_url : pt_resource,
depPath.empty() ? depUrl : depPath, depId, "", "", 0, depRelease,
false, GAMET_ENIGMA, STATUS_UNKNOWN);
try {
depProxy->loadMetadata(false);
} catch (XLevelLoading &err) {
delete depProxy;
throw err;
}
}
// load and register lib
if (depPreload) {
loadedLibs.push_back(depProxy);
depProxy->load(false, false);
} else {
registeredLibs.push_back(depProxy);
}
}
void Proxy::loadDependency(std::string depId) {
// check if lib is already loaded
for (int i=0 ; i<loadedLibs.size(); i++) {
if (loadedLibs[i]->getId() == depId)
// library is already loaded
return;
}
for (int i=0 ; i<registeredLibs.size(); i++) {
if (registeredLibs[i]->getId() == depId) {
// library is registered to be loaded - do it
loadedLibs.push_back(registeredLibs[i]);
registeredLibs[i]->load(false, false);
// delete form not yet loaded list
registeredLibs[i] = registeredLibs[registeredLibs.size() - 1];
registeredLibs.pop_back();
return;
}
}
if (doc == NULL) {
// if (true) {
// Log << "loadDependency " << depId << "\n";
// handling for legacy Lua levels that did not register dependencies
Proxy * depProxy = new Proxy(true, pt_resource, depId, depId, "", "", 0, 1,
false, GAMET_ENIGMA, STATUS_UNKNOWN);
loadedLibs.push_back(depProxy);
depProxy->load(false, false);
return;
} else
// xml levels have to register used libraries as dependencies
throw XLevelLoading("load attempt of undeclared library");
}
void Proxy::loadLuaCode() {
lua_State *L = lua::LevelState();
DOMNodeList * luamainList = doc->getElementsByTagNameNS(levelNS, Utf8ToXML("luamain").x_str());
if (luamainList->getLength() == 1) {
DOMElement *luamain = reinterpret_cast<DOMElement *>(luamainList->item(0));
// add debugging info to lua code
std::string luaCode = "--@" + absLevelPath + "\n" +
XMLtoUtf8(luamain->getTextContent()).c_str();
if (luaL_dostring(L, luaCode.c_str() ) != 0) {
lua_setglobal (L, "_LASTERROR");
throw XLevelLoading(lua::LastError(L));
}
} else {
throw XLevelLoading("not implemented");
}
}
std::string Proxy::getLocalizedString(const std::string &key) {
std::string english = key;
std::string translation;
std::string lang = ecl::GetLanguageCode (app.language);
// add handling for calls besides level run: info from cache
if (doc == NULL) {
if (key == "title")
return title;
else
return key;
}
if (key == "title" || key == "subtitle") {
// get the english originals from the identity attributes
DOMElement *identityElem =
reinterpret_cast<DOMElement *>(infoElem->getElementsByTagNameNS(
levelNS, Utf8ToXML("identity").x_str())->item(0));
english = XMLtoUtf8(identityElem->getAttributeNS(levelNS,
Utf8ToXML(&key).x_str())).c_str();
if (key == "title")
title = english; // update cash
}
bool translFound = false;
bool keyFound = false;
bool protectedString = true;
for (int i = 0, l = stringList-> getLength(); i < l && !translFound; i++) {
DOMElement *stringElem = reinterpret_cast<DOMElement *>(stringList->item(i));
if (key == XMLtoUtf8(stringElem->getAttributeNS(levelNS,
Utf8ToXML("key").x_str())).c_str()) {
keyFound = true;
// 2 strings with matching key may be found:
// first the protected, then the public one
if (protectedString) {
// resolve english text from string->english subelement
DOMElement *englishElem =
reinterpret_cast<DOMElement *>(stringElem->getElementsByTagNameNS(
levelNS, Utf8ToXML("english").x_str())->item(0));
std::string tmp = XMLtoUtf8(englishElem->getTextContent()).c_str();
if (!tmp.empty()) {
// overwrite the english key only if text is provided
english = tmp;
}
// we got the final english string
if (lang == "en") {
translation = english;
translFound = true;
}
}
if (!translFound) {
// protected and public translations
DOMNodeList *translList = stringElem->getElementsByTagNameNS(
levelNS, Utf8ToXML("translation").x_str());
for (int i = 0, l = translList-> getLength(); i < l && !translFound; i++) {
DOMElement *translElem = reinterpret_cast<DOMElement *>(translList->item(i));
if (lang == XMLtoUtf8(translElem->getAttributeNS(levelNS,
Utf8ToXML("lang").x_str())).c_str()) {
translation = XMLtoUtf8(translElem->getTextContent()).c_str();
translFound = true;
}
}
}
if (!translFound && protectedString) {
// gettext
std::string tmp = _(english.c_str());
if (tmp != english) {
translation = tmp;
translFound = true;
}
}
protectedString = false; // the next matching string is public
}
}
if (!keyFound && !english.empty()) {
// string may originate from a lib - still try gettext
std::string tmp = _(english.c_str());
if (tmp != english) {
translation = tmp;
translFound = true;
}
}
return (translFound ? translation : english);
}
std::string Proxy::getType() {
if (doc != NULL) {
return XMLtoUtf8(infoElem->getAttributeNS(levelNS,
Utf8ToXML("type").x_str())).c_str();
} else
return "";
}
bool Proxy::updateId() {
if (doc != NULL) {
DOMElement *identityElem =
reinterpret_cast<DOMElement *>(infoElem->getElementsByTagNameNS(
levelNS, Utf8ToXML("identity").x_str())->item(0));
std::string docId = XMLtoUtf8(identityElem->getAttributeNS(levelNS,
Utf8ToXML("id").x_str())).c_str();
if (id.empty() || id[0] == '_') {
// set yet undetermined id (auto folder, command line,...)
id = docId;
return true;
} else if (id != docId)
return false;
}
return true;
}
std::string Proxy::getId() {
return id;
}
int Proxy::getScoreVersion() {
if (doc != NULL) {
DOMElement *versionElem =
reinterpret_cast<DOMElement *>(infoElem->getElementsByTagNameNS(
levelNS, Utf8ToXML("version").x_str())->item(0));
scoreVersion = XMLString::parseInt(versionElem->getAttributeNS(levelNS,
Utf8ToXML("score").x_str()));
}
return scoreVersion;
}
bool Proxy::updateReleaseVersion() {
if (doc != NULL) {
DOMElement *versionElem =
reinterpret_cast<DOMElement *>(infoElem->getElementsByTagNameNS(
levelNS, Utf8ToXML("version").x_str())->item(0));
int docRelease = XMLString::parseInt(versionElem->getAttributeNS(levelNS,
Utf8ToXML("release").x_str()));
if (releaseVersion == 0) {
// set yet undetermined version
releaseVersion = docRelease;
return true;
} else if (releaseVersion != docRelease) {
return false;
}
}
return true;
}
int Proxy::getReleaseVersion() {
return releaseVersion;
}
int Proxy::getRevisionNumber() {
if (doc != NULL) {
DOMElement *versionElem =
reinterpret_cast<DOMElement *>(infoElem->getElementsByTagNameNS(
levelNS, Utf8ToXML("version").x_str())->item(0));
const XMLCh * revision = versionElem->getAttributeNS(levelNS,
Utf8ToXML("revision").x_str());
unsigned int len = XMLString::stringLen(revision);
int start = -1;
int end = -1;
for (int i = 0; i<len; i++) {
if(XMLString::isDigit(revision[i])) {
if (start == -1)
start = i;
} else if (start != -1 && end == -1)
end = i;
}
if (start != -1) {
if (end == -1)
end = len;
XMLCh * revisionDigits = XMLString::replicate(revision);
XMLString::subString(revisionDigits, revision, start, end);
revisionNumber = XMLString::parseInt(revisionDigits);
XMLString::release(&revisionDigits);
} else {
// "$Revision$" - level not stored in repository yet
revisionNumber = 0;
}
}
return revisionNumber;
}
levelStatusType Proxy::getLevelStatus() {
if (doc != NULL) {
DOMElement *versionElem =
reinterpret_cast<DOMElement *>(infoElem->getElementsByTagNameNS(
levelNS, Utf8ToXML("version").x_str())->item(0));
std::string status = XMLtoUtf8(versionElem->getAttributeNS(levelNS,
Utf8ToXML("status").x_str())).c_str();
if (status == "released")
levelStatus = STATUS_RELEASED;
else if (status == "stable")
levelStatus = STATUS_STABLE;
else if (status == "test")
levelStatus = STATUS_TEST;
else if (status == "experimental")
levelStatus = STATUS_EXPERIMENTAL;
}
return levelStatus;
}
std::string Proxy::getAuthor() {
if (doc != NULL) {
DOMElement *authorElem =
reinterpret_cast<DOMElement *>(infoElem->getElementsByTagNameNS(
levelNS, Utf8ToXML("author").x_str())->item(0));
author = XMLtoUtf8(authorElem->getAttributeNS(levelNS,
Utf8ToXML("name").x_str())).c_str();
}
return author;
}
std::string Proxy::getTitle() {
if (doc != NULL) {
DOMElement *identityElem =
reinterpret_cast<DOMElement *>(infoElem->getElementsByTagNameNS(
levelNS, Utf8ToXML("identity").x_str())->item(0));
title = XMLtoUtf8(identityElem->getAttributeNS(levelNS,
Utf8ToXML("title").x_str())).c_str();
}
return title;
}
bool Proxy::hasEasymode() {
if (doc != NULL) {
DOMElement *modesElem =
reinterpret_cast<DOMElement *>(infoElem->getElementsByTagNameNS(
levelNS, Utf8ToXML("modes").x_str())->item(0));
hasEasymodeFlag = boolValue(modesElem->getAttributeNS(levelNS,
Utf8ToXML("easy").x_str()));
}
return hasEasymodeFlag;
}
std::string Proxy::getContact() {
std::string contact;
if (doc != NULL) {
DOMElement *authorElem =
reinterpret_cast<DOMElement *>(infoElem->getElementsByTagNameNS(
levelNS, Utf8ToXML("author").x_str())->item(0));
contact = XMLtoUtf8(authorElem->getAttributeNS(levelNS,
Utf8ToXML("email").x_str())).c_str();
}
return contact;
}
std::string Proxy::getHomepage() {
std::string homepage;
if (doc != NULL) {
DOMElement *authorElem =
reinterpret_cast<DOMElement *>(infoElem->getElementsByTagNameNS(
levelNS, Utf8ToXML("author").x_str())->item(0));
homepage = XMLtoUtf8(authorElem->getAttributeNS(levelNS,
Utf8ToXML("homepage").x_str())).c_str();
}
return homepage;
}
double Proxy::getEnigmaCompatibility() {
double value = 0.92;
if (doc != NULL) {
DOMElement *compatibilityElem =
reinterpret_cast<DOMElement *>(infoElem->getElementsByTagNameNS(
levelNS, Utf8ToXML("compatibility").x_str())->item(0));
XMLDouble * result = new XMLDouble(compatibilityElem->getAttributeNS(levelNS,
Utf8ToXML("enigma").x_str()));
value = result->getValue();
delete result;
}
return value;
}
GameType Proxy::getEngineCompatibility() {
if (doc != NULL) {
DOMElement *authorElem =
reinterpret_cast<DOMElement *>(infoElem->getElementsByTagNameNS(
levelNS, Utf8ToXML("compatibility").x_str())->item(0));
engineCompatibility = GetGameType(XMLtoUtf8(authorElem->getAttributeNS(levelNS,
Utf8ToXML("engine").x_str())).c_str());
}
return engineCompatibility;
}
controlType Proxy::getControl() {
controlType control = force;
if (doc != NULL) {
DOMElement *authorElem =
reinterpret_cast<DOMElement *>(infoElem->getElementsByTagNameNS(
levelNS, Utf8ToXML("modes").x_str())->item(0));
std::string controlString = XMLtoUtf8(authorElem->getAttributeNS(levelNS,
Utf8ToXML("control").x_str())).c_str();
if (controlString == "balance")
control = balance;
else if (controlString == "key")
control = key;
else if (controlString == "other")
control = other;
}
return control;
}
scoreUnitType Proxy::getScoreUnit() {
if (doc != NULL) {
DOMElement *modesElem =
reinterpret_cast<DOMElement *>(infoElem->getElementsByTagNameNS(
levelNS, Utf8ToXML("modes").x_str())->item(0));
std::string txt = XMLtoUtf8(modesElem->getAttributeNS(levelNS,
Utf8ToXML("scoreunit").x_str())).c_str();
if (txt == "number")
scoreUnit = number;
else
// default
scoreUnit = duration;
}
return scoreUnit;
}
std::string Proxy::getScoreTarget() {
std::string title = "time";
if (doc != NULL) {
DOMElement *modesElem =
reinterpret_cast<DOMElement *>(infoElem->getElementsByTagNameNS(
levelNS, Utf8ToXML("modes").x_str())->item(0));
title = XMLtoUtf8(modesElem->getAttributeNS(levelNS,
Utf8ToXML("scoretarget").x_str())).c_str();
}
return title;
}
std::string Proxy::getCredits(bool infoUsage) {
std::string credits;
std::string attribute = infoUsage ? "showinfo" : "showstart";
if (doc != NULL) {
DOMElement *creditsElem =
reinterpret_cast<DOMElement *>(infoElem->getElementsByTagNameNS(
levelNS, Utf8ToXML("credits").x_str())->item(0));
if (creditsElem != NULL) // element is optional
if (boolValue(creditsElem->getAttributeNS(levelNS,
Utf8ToXML(attribute.c_str()).x_str())))
credits = XMLtoUtf8(creditsElem->getTextContent()).c_str();
}
return credits;
}
std::string Proxy::getDedication(bool infoUsage) {
std::string dedication;
std::string attribute = infoUsage ? "showinfo" : "showstart";
if (doc != NULL) {
DOMElement *dedicationElem =
reinterpret_cast<DOMElement *>(infoElem->getElementsByTagNameNS(
levelNS, Utf8ToXML("dedication").x_str())->item(0));
if (dedicationElem != NULL) // element is optional
if (boolValue(dedicationElem->getAttributeNS(levelNS,
Utf8ToXML(attribute.c_str()).x_str())))
dedication = XMLtoUtf8(dedicationElem->getTextContent()).c_str();
}
return dedication;
}
int Proxy::getEasyScore() {
int score = -1;
if (doc != NULL) {
DOMElement *versionElem =
reinterpret_cast<DOMElement *>(infoElem->getElementsByTagNameNS(
levelNS, Utf8ToXML("score").x_str())->item(0));
score = scoreText2Int(XMLtoUtf8(versionElem->getAttributeNS(levelNS,
Utf8ToXML("easy").x_str())).c_str());
}
return score;
}
int Proxy::getDifficultScore() {
int score = -1;
if (doc != NULL) {
DOMElement *versionElem =
reinterpret_cast<DOMElement *>(infoElem->getElementsByTagNameNS(
levelNS, Utf8ToXML("score").x_str())->item(0));
score = scoreText2Int(XMLtoUtf8(versionElem->getAttributeNS(levelNS,
Utf8ToXML("difficult").x_str())).c_str());
}
return score;
}
int Proxy::scoreText2Int(std::string text) {
if (text == "-")
return -1;
else {
std::string::size_type colon = text.find(':');
if (colon == std::string::npos)
return atoi(text.c_str());
else
return atoi(text.substr(0,colon).c_str()) * 60 +
atoi(text.substr(colon+1).c_str());
}
}
}} // namespace enigma::lev