1
0
mirror of https://github.com/vcmi/vcmi.git synced 2024-12-20 20:23:03 +02:00

- it is possible to edit data of another mod or H3 data via mods

- mods can access only ID's from dependenies, virtual "core" mod and itself (optional for some mods compatibility)
- metadata field for JsonNode, used to track source mod
- moved wog creatures into wog mod
- (linux) convertMP3 option for vcmibuilder for systems where SDL_Mixer can't play mp3's
This commit is contained in:
Ivan Savenko 2013-04-25 14:03:35 +00:00
parent be6aff5173
commit 8273f323b1
24 changed files with 321 additions and 98 deletions

View File

@ -0,0 +1,38 @@
{
"core:castle" :
{
"commander" : "paladin1"
},
"core:conflux" :
{
"commander" : "astralSpirit1"
},
"core:dungeon" :
{
"commander" : "brute1"
},
"core:fortress" :
{
"commander" : "shaman1"
},
"core:inferno" :
{
"commander" : "succubus1"
},
"core:necropolis" :
{
"commander" : "soulEater1"
},
"core:rampart" :
{
"commander" : "hierophant1"
},
"core:stronghold" :
{
"commander" : "ogreLeader1"
},
"core:tower" :
{
"commander" : "templeGuardian1"
}
}

View File

@ -1,6 +1,10 @@
{
"filesystem":
{
"CONFIG/" :
[
{ "type" : "dir", "path" : "/Config"}
],
"DATA/" :
[
{"type" : "lod", "path" : "/Data/hmm35wog.pac"},
@ -34,5 +38,15 @@
},
"name" : "In The Wake of Gods",
"description" : "Unnofficial addon for Heroes of Might and Magic III"
"description" : "Unnofficial addon for Heroes of Might and Magic III",
"creatures" :
[
"config/wog/creatures.json"
],
"factions" :
[
"config/wog/factions.json"
]
}

View File

@ -4417,6 +4417,12 @@ void CArtPlace::setArtifact(const CArtifactInstance *art)
bonusValue = 0;
}
}
else
{
baseType = CComponent::artifact;
type = art->artType->iconIndex;
bonusValue = 0;
}
if (locked) // Locks should appear as empty.
hoverText = CGI->generaltexth->allTexts[507];

View File

@ -4,7 +4,7 @@
"index" : 0,
"nativeTerrain": "grass",
"alignment" : "good",
"commander" : "paladin1",
"commander" : "zealot",
"creatureBackground" :
{
"120px" : "TPCASCAS",

View File

@ -4,7 +4,7 @@
"index" : 8,
"nativeTerrain": "grass",
"alignment" : "neutral",
"commander" : "astralSpirit1",
"commander" : "iceElemental",
"creatureBackground" :
{
"120px" : "TPCASELE",

View File

@ -4,7 +4,7 @@
"index" : 5,
"nativeTerrain": "subterra",
"alignment" : "evil",
"commander" : "brute1",
"commander" : "medusaQueen",
"creatureBackground" :
{
"120px" : "TPCASDUN",

View File

@ -4,7 +4,7 @@
"index" : 7,
"nativeTerrain": "swamp",
"alignment" : "neutral",
"commander" : "shaman1",
"commander" : "lizardWarrior",
"creatureBackground" :
{
"120px" : "TPCASFOR",

View File

@ -4,7 +4,7 @@
"index" : 3,
"nativeTerrain": "lava",
"alignment" : "evil",
"commander" : "succubus1",
"commander" : "magog",
"creatureBackground" :
{
"120px" : "TPCASINF",

View File

@ -4,7 +4,7 @@
"index" : 4,
"nativeTerrain": "dirt",
"alignment" : "evil",
"commander" : "soulEater1",
"commander" : "powerLich",
"creatureBackground" :
{
"120px" : "TPCASNEC",

View File

@ -4,7 +4,7 @@
"index" : 1,
"nativeTerrain": "grass",
"alignment" : "good",
"commander" : "hierophant1",
"commander" : "grandElf",
"creatureBackground" :
{
"120px" : "TPCASRAM",

View File

@ -4,7 +4,7 @@
"index" : 6,
"nativeTerrain": "rough",
"alignment" : "neutral",
"commander" : "ogreLeader1",
"commander" : "orcChieftain",
"creatureBackground" :
{
"120px" : "TPCASSTR",

View File

@ -4,7 +4,7 @@
"index" : 2,
"nativeTerrain" : "snow",
"alignment" : "good",
"commander" : "templeGuardian1",
"commander" : "archMage",
"creatureBackground" :
{
"120px" : "TPCASTOW",

View File

@ -28,8 +28,7 @@
"config/creatures/conflux.json",
"config/creatures/neutral.json",
"config/creatures/special.json",
"config/creatures/wog.json"
"config/creatures/special.json"
],
"heroes" :

View File

@ -366,7 +366,7 @@ void CArtHandler::loadComponents(CArtifact * art, const JsonNode & node)
art->constituents.reset(new std::vector<CArtifact *>());
BOOST_FOREACH (auto component, node["components"].Vector())
{
VLC->modh->identifiers.requestIdentifier("artifact." + component.String(), [=](si32 id)
VLC->modh->identifiers.requestIdentifier("artifact", component, [=](si32 id)
{
// when this code is called both combinational art as well as component are loaded
// so it is safe to access any of them

View File

@ -179,7 +179,10 @@ CCreatureHandler::CCreatureHandler()
void CCreatureHandler::loadCommanders()
{
const JsonNode config(ResourceID("config/commanders.json"));
JsonNode data(ResourceID("config/commanders.json"));
data.setMeta("core"); // assume that commanders are in core mod (for proper bonuses resolution)
const JsonNode & config = data; // switch to const data accessors
BOOST_FOREACH (auto bonus, config["bonusPerLevel"].Vector())
{
@ -627,14 +630,14 @@ void CCreatureHandler::loadCreatureJson(CCreature * creature, const JsonNode & c
}
}
VLC->modh->identifiers.requestIdentifier(std::string("faction.") + config["faction"].String(), [=](si32 faction)
VLC->modh->identifiers.requestIdentifier("faction", config["faction"], [=](si32 faction)
{
creature->faction = faction;
});
BOOST_FOREACH(const JsonNode &value, config["upgrades"].Vector())
{
VLC->modh->identifiers.requestIdentifier(std::string("creature.") + value.String(), [=](si32 identifier)
VLC->modh->identifiers.requestIdentifier("creature", value, [=](si32 identifier)
{
creature->upgrades.insert(CreatureID(identifier));
});

View File

@ -105,14 +105,14 @@ CHeroClass *CHeroClassHandler::loadFromJson(const JsonNode & node)
{
int value = tavern.second.Float();
VLC->modh->identifiers.requestIdentifier("faction." + tavern.first,
VLC->modh->identifiers.requestIdentifier(tavern.second.meta, "faction", tavern.first,
[=](si32 factionID)
{
heroClass->selectionProbability[factionID] = value;
});
}
VLC->modh->identifiers.requestIdentifier("faction." + node["faction"].String(),
VLC->modh->identifiers.requestIdentifier("faction", node["faction"],
[=](si32 factionID)
{
heroClass->faction = factionID;
@ -237,7 +237,7 @@ CHero * CHeroHandler::loadFromJson(const JsonNode & node)
loadHeroSkills(hero, node);
loadHeroSpecialty(hero, node);
VLC->modh->identifiers.requestIdentifier("heroClass." + node["class"].String(),
VLC->modh->identifiers.requestIdentifier("heroClass", node["class"],
[=](si32 classID)
{
hero->heroClass = classes.heroClasses[classID];
@ -261,7 +261,7 @@ void CHeroHandler::loadHeroArmy(CHero * hero, const JsonNode & node)
assert(hero->initialArmy[i].minAmount <= hero->initialArmy[i].maxAmount);
VLC->modh->identifiers.requestIdentifier("creature." + source["creature"].String(), [=](si32 creature)
VLC->modh->identifiers.requestIdentifier("creature", source["creature"], [=](si32 creature)
{
hero->initialArmy[i].creature = CreatureID(creature);
});
@ -278,7 +278,7 @@ void CHeroHandler::loadHeroSkills(CHero * hero, const JsonNode & node)
size_t currentIndex = hero->secSkillsInit.size();
hero->secSkillsInit.push_back(std::make_pair(SecondarySkill(-1), skillLevel));
VLC->modh->identifiers.requestIdentifier("skill." + set["skill"].String(), [=](si32 id)
VLC->modh->identifiers.requestIdentifier("skill", set["skill"], [=](si32 id)
{
hero->secSkillsInit[currentIndex].first = SecondarySkill(id);
});
@ -300,7 +300,7 @@ void CHeroHandler::loadHeroSkills(CHero * hero, const JsonNode & node)
}
else
{
VLC->modh->identifiers.requestIdentifier("spell." + spell.String(),
VLC->modh->identifiers.requestIdentifier("spell", spell,
[=](si32 spellID)
{
hero->spells.insert(SpellID(spellID));

View File

@ -44,82 +44,142 @@ void CIdentifierStorage::checkIdentifier(std::string & ID)
}
}
void CIdentifierStorage::requestIdentifier(std::string name, const boost::function<void(si32)> & callback)
CIdentifierStorage::ObjectCallback::ObjectCallback(std::string localScope, std::string remoteScope, std::string type,
std::string name, const boost::function<void(si32)> & callback):
localScope(localScope),
remoteScope(remoteScope),
type(type),
name(name),
callback(callback)
{}
static std::pair<std::string, std::string> splitString(std::string input, char separator)
{
checkIdentifier(name);
std::pair<std::string, std::string> ret;
size_t splitPos = input.find(separator);
// old version with immediate callback posibility. Can't be used for some cases
/* auto iter = registeredObjects.find(name);
if (iter != registeredObjects.end())
callback(iter->second); //already registered - trigger callback immediately
if (splitPos == std::string::npos)
{
ret.first.clear();
ret.second = input;
}
else
{
if(boost::algorithm::starts_with(name, "primSkill."))
logGlobal->warnStream() << "incorrect primSkill name requested";
ret.first = input.substr(0, splitPos);
ret.second = input.substr(splitPos + 1);
}
return ret;
}
missingObjects[name].push_back(callback); // queue callback
}*/
void CIdentifierStorage::requestIdentifier(ObjectCallback callback)
{
checkIdentifier(callback.type);
checkIdentifier(callback.name);
missingObjects[name].push_back(callback); // queue callback
assert(!callback.localScope.empty());
scheduledRequests.push_back(callback);
}
void CIdentifierStorage::requestIdentifier(std::string scope, std::string type, std::string name, const boost::function<void(si32)> & callback)
{
auto pair = splitString(name, ':'); // remoteScope:name
requestIdentifier(ObjectCallback(scope, pair.first, type, pair.second, callback));
}
void CIdentifierStorage::requestIdentifier(std::string type, const JsonNode & name, const boost::function<void(si32)> & callback)
{
auto pair = splitString(name.String(), ':'); // remoteScope:name
requestIdentifier(ObjectCallback(name.meta, pair.first, type, pair.second, callback));
}
void CIdentifierStorage::requestIdentifier(const JsonNode & name, const boost::function<void(si32)> & callback)
{
auto pair = splitString(name.String(), ':'); // remoteScope:<type.name>
auto pair2 = splitString(pair.second, '.'); // type.name
requestIdentifier(ObjectCallback(name.meta, pair.first, pair2.first, pair2.second, callback));
}
void CIdentifierStorage::registerObject(std::string scope, std::string type, std::string name, si32 identifier)
{
//TODO: use scope
ObjectData data;
data.scope = scope;
data.id = identifier;
std::string fullID = type + '.' + name;
checkIdentifier(fullID);
// do not allow to register same object twice
assert(registeredObjects.find(fullID) == registeredObjects.end());
registeredObjects[fullID] = identifier;
// old version with immediate callback posibility. Can't be used for some cases
/*auto iter = missingObjects.find(fullID);
if (iter != missingObjects.end())
{
//call all awaiting callbacks
BOOST_FOREACH(auto function, iter->second)
{
function(identifier);
registeredObjects.insert(std::make_pair(fullID, data));
}
missingObjects.erase(iter);
}*/
bool CIdentifierStorage::resolveIdentifier(const ObjectCallback & request)
{
std::set<std::string> allowedScopes;
if (request.remoteScope.empty())
{
// normally ID's from all required mods, own mod and virtual "core" mod are allowed
if (request.localScope != "core")
allowedScopes = VLC->modh->getModData(request.localScope).dependencies;
allowedScopes.insert(request.localScope);
allowedScopes.insert("core");
}
else
{
// //...unless destination mod was specified explicitly
allowedScopes.insert(request.remoteScope);
}
std::string fullID = request.type + '.' + request.name;
auto entries = registeredObjects.equal_range(fullID);
if (entries.first != entries.second)
{
for (auto it = entries.first; it != entries.second; it++)
{
if (vstd::contains(allowedScopes, it->second.scope))
{
request.callback(it->second.id);
return true;
}
}
// error found. Try to generate some debug info
logGlobal->errorStream() << "Unknown identifier " << request.type << "." << request.name << " from mod " << request.localScope;
for (auto it = entries.first; it != entries.second; it++)
{
logGlobal->errorStream() << "\tID is available in mod " << it->second.scope;
}
// temporary code to smooth 0.92->0.93 transition
request.callback(entries.first->second.id);
return true;
}
return false;
}
void CIdentifierStorage::finalize()
{
for (auto it = missingObjects.begin(); it!= missingObjects.end();)
bool errorsFound = false;
BOOST_FOREACH(const ObjectCallback & request, scheduledRequests)
{
auto object = registeredObjects.find(it->first);
if (object != registeredObjects.end())
{
BOOST_FOREACH(auto function, it->second)
{
function(object->second);
}
it = missingObjects.erase(it);
}
else
it++;
errorsFound |= !resolveIdentifier(request);
}
// print list of missing objects and crash
// in future should try to do some cleanup (like returning all id's as 0)
if (!missingObjects.empty())
if (errorsFound)
{
BOOST_FOREACH(auto object, missingObjects)
{
logGlobal->errorStream() << "Error: object " << object.first << " was not found!";
}
BOOST_FOREACH(auto object, registeredObjects)
{
logGlobal->traceStream() << object.first << " -> " << object.second;
logGlobal->traceStream() << object.first << " -> " << object.second.id;
}
logGlobal->errorStream() << "All known identifiers were dumped into log file";
}
assert(missingObjects.empty());
assert(errorsFound == false);
}
CContentHandler::ContentTypeHandler::ContentTypeHandler(IHandlerBase * handler, size_t size, std::string objectName):
@ -127,11 +187,16 @@ CContentHandler::ContentTypeHandler::ContentTypeHandler(IHandlerBase * handler,
objectName(objectName),
originalData(handler->loadLegacyData(size))
{
BOOST_FOREACH(auto & node, originalData)
{
node.setMeta("core");
}
}
void CContentHandler::ContentTypeHandler::preloadModData(std::string modName, std::vector<std::string> fileList)
{
JsonNode data = JsonUtils::assembleFromFiles(fileList);
data.setMeta(modName);
ModInfo & modInfo = modData[modName];
@ -425,6 +490,13 @@ std::vector<std::string> CModHandler::getActiveMods()
return activeMods;
}
CModInfo & CModHandler::getModData(TModID modId)
{
CModInfo & mod = allMods.at(modId);
assert(vstd::contains(activeMods, modId)); // not really necessary but won't hurt
return mod;
}
template<typename Handler>
void CModHandler::handleData(Handler handler, const JsonNode & source, std::string listName, std::string schemaName)
{
@ -447,7 +519,7 @@ void CModHandler::loadGameContent()
CContentHandler content;
logGlobal->infoStream() << "\tInitializing content hander: " << timer.getDiff() << " ms";
// first - load virtual "core" mod tht contains all data
// first - load virtual "core" mod that contains all data
// TODO? move all data into real mods? RoE, AB, SoD, WoG
content.preloadModData("core", JsonNode(ResourceID("config/gameConfig.json")));
logGlobal->infoStream() << "\tParsing original game data: " << timer.getDiff() << " ms";
@ -475,12 +547,11 @@ void CModHandler::loadGameContent()
}
logGlobal->infoStream() << "\tLoading mod data: " << timer.getDiff() << "ms";
logGlobal->infoStream() << "\tDone loading data";
VLC->creh->loadCrExpBon();
VLC->creh->buildBonusTreeForTiers(); //do that after all new creatures are loaded
identifiers.finalize();
logGlobal->infoStream() << "\tResolving identifiers: " << timer.getDiff() << " ms";
logGlobal->infoStream() << "\tAll game content loaded in " << totalTime.getDiff() << " ms";
}

View File

@ -25,15 +25,38 @@ class IHandlerBase;
/// if possible, objects ID's should be in format <type>.<name>, camelCase e.g. "creature.grandElf"
class CIdentifierStorage
{
std::map<std::string, si32 > registeredObjects;
std::map<std::string, std::vector<boost::function<void(si32)> > > missingObjects;
struct ObjectCallback // entry created on ID request
{
std::string localScope; /// scope from which this ID was requested
std::string remoteScope; /// scope in which this object must be found
std::string type; /// type, e.g. creature, faction, hero, etc
std::string name; /// string ID
boost::function<void(si32)> callback;
ObjectCallback(std::string localScope, std::string remoteScope, std::string type, std::string name, const boost::function<void(si32)> & callback);
};
struct ObjectData // entry created on ID registration
{
si32 id;
std::string scope; /// scope in which this ID located
};
std::multimap<std::string, ObjectData > registeredObjects;
std::vector<ObjectCallback> scheduledRequests;
/// Check if identifier can be valid (camelCase, point as separator)
void checkIdentifier(std::string & ID);
void requestIdentifier(ObjectCallback callback);
bool resolveIdentifier(const ObjectCallback & callback);
public:
/// request identifier for specific object name. If ID is not yet resolved callback will be queued
/// and will be called later
void requestIdentifier(std::string name, const boost::function<void(si32)> & callback);
void requestIdentifier(std::string scope, std::string type, std::string name, const boost::function<void(si32)> & callback);
void requestIdentifier(std::string type, const JsonNode & name, const boost::function<void(si32)> & callback);
void requestIdentifier(const JsonNode & name, const boost::function<void(si32)> & callback);
/// registers new object, calls all associated callbacks
void registerObject(std::string scope, std::string type, std::string name, si32 identifier);
@ -143,6 +166,8 @@ public:
/// returns list of mods that should be active with order in which they shoud be loaded
std::vector<std::string> getActiveMods();
CModInfo & getModData(TModID modId);
/// load content from all available mods
void loadGameContent();

View File

@ -828,8 +828,6 @@ void CGHeroInstance::initHero()
commander->setArmyObj (castToArmyObj()); //TODO: separate function for setting commanders
commander->giveStackExp (exp); //after our exp is set
}
else
commander = nullptr;
hoverName = VLC->generaltexth->allTexts[15];
boost::algorithm::replace_first(hoverName,"%s",name);

View File

@ -390,7 +390,7 @@ CTown::ClientInfo::Point JsonToPoint(const JsonNode & node)
void CTownHandler::loadSiegeScreen(CTown &town, const JsonNode & source)
{
town.clientInfo.siegePrefix = source["imagePrefix"].String();
VLC->modh->identifiers.requestIdentifier(std::string("creature.") + source["shooter"].String(), [&town](si32 creature)
VLC->modh->identifiers.requestIdentifier("creature", source["shooter"], [&town](si32 creature)
{
town.clientInfo.siegeShooter = CreatureID(creature);
});
@ -469,7 +469,7 @@ void CTownHandler::loadTown(CTown &town, const JsonNode & source)
else
town.primaryRes = resIter - boost::begin(GameConstants::RESOURCE_NAMES);
VLC->modh->identifiers.requestIdentifier(std::string("creature." + source["warMachine"].String()),
VLC->modh->identifiers.requestIdentifier("creature", source["warMachine"],
[&town](si32 creature)
{
town.warMachine = CArtHandler::creatureToMachineID(CreatureID(creature));
@ -498,7 +498,7 @@ void CTownHandler::loadTown(CTown &town, const JsonNode & source)
for (size_t j=0; j<level.size(); j++)
{
VLC->modh->identifiers.requestIdentifier(std::string("creature.") + level[j].String(), [=, &town](si32 creature)
VLC->modh->identifiers.requestIdentifier("creature", level[j], [=, &town](si32 creature)
{
town.creatures[i][j] = CreatureID(creature);
});
@ -510,7 +510,7 @@ void CTownHandler::loadTown(CTown &town, const JsonNode & source)
{
int chance = node.second.Float();
VLC->modh->identifiers.requestIdentifier("heroClass." + node.first, [=, &town](si32 classID)
VLC->modh->identifiers.requestIdentifier(node.second.meta, "heroClass",node.first, [=, &town](si32 classID)
{
VLC->heroh->classes.heroClasses[classID]->selectionProbability[town.faction->index] = chance;
});
@ -520,7 +520,7 @@ void CTownHandler::loadTown(CTown &town, const JsonNode & source)
{
int chance = node.second.Float();
VLC->modh->identifiers.requestIdentifier("spell." + node.first, [=, &town](si32 spellID)
VLC->modh->identifiers.requestIdentifier(node.second.meta, "spell", node.first, [=, &town](si32 spellID)
{
SpellID(spellID).toSpell()->probabilities[town.faction->index] = chance;
});
@ -568,7 +568,7 @@ CFaction * CTownHandler::loadFromJson(const JsonNode &source)
faction->name = source["name"].String();
VLC->modh->identifiers.requestIdentifier ("creature." + source["commander"].String(),
VLC->modh->identifiers.requestIdentifier ("creature", source["commander"],
[=](si32 commanderID)
{
faction->commander = CreatureID(commanderID);

View File

@ -50,6 +50,7 @@ JsonNode::JsonNode(ResourceID && fileURI):
JsonNode::JsonNode(const JsonNode &copy):
type(DATA_NULL)
{
meta = copy.meta;
setType(copy.getType());
switch(type)
{
@ -70,6 +71,7 @@ JsonNode::~JsonNode()
void JsonNode::swap(JsonNode &b)
{
using std::swap;
swap(meta, b.meta);
swap(data, b.data);
swap(type, b.type);
}
@ -107,6 +109,31 @@ JsonNode::JsonType JsonNode::getType() const
return type;
}
void JsonNode::setMeta(std::string metadata, bool recursive)
{
meta = metadata;
if (recursive)
{
switch (type)
{
break; case DATA_VECTOR:
{
BOOST_FOREACH(auto & node, Vector())
{
node.setMeta(metadata);
}
}
break; case DATA_STRUCT:
{
BOOST_FOREACH(auto & node, Struct())
{
node.second.setMeta(metadata);
}
}
}
}
}
void JsonNode::setType(JsonType Type)
{
if (type == Type)
@ -1183,17 +1210,16 @@ const T & parseByMap(const std::map<std::string, T> & map, const JsonNode * val,
void JsonUtils::resolveIdentifier (si32 &var, const JsonNode &node, std::string name)
{
const JsonNode *value;
value = &node[name];
if (!value->isNull())
const JsonNode &value = node[name];
if (!value.isNull())
{
switch (value->getType())
switch (value.getType())
{
case JsonNode::DATA_FLOAT:
var = value->Float();
var = value.Float();
break;
case JsonNode::DATA_STRING:
VLC->modh->identifiers.requestIdentifier (value->String(), [&](si32 identifier)
VLC->modh->identifiers.requestIdentifier(value, [&](si32 identifier)
{
var = identifier;
});
@ -1212,7 +1238,7 @@ void JsonUtils::resolveIdentifier (const JsonNode &node, si32 &var)
var = node.Float();
break;
case JsonNode::DATA_STRING:
VLC->modh->identifiers.requestIdentifier (node.String(), [&](si32 identifier)
VLC->modh->identifiers.requestIdentifier (node, [&](si32 identifier)
{
var = identifier;
});
@ -1301,7 +1327,7 @@ Bonus * JsonUtils::parseBonus (const JsonNode &ability)
{
shared_ptr<CCreatureTypeLimiter> l2 = make_shared<CCreatureTypeLimiter>(); //TODO: How the hell resolve pointer to creature?
const JsonVector vec = limiter["parameters"].Vector();
VLC->modh->identifiers.requestIdentifier(std::string("creature.") + vec[0].String(), [=](si32 creature)
VLC->modh->identifiers.requestIdentifier("creature", vec[0], [=](si32 creature)
{
l2->setCreature (CreatureID(creature));
});
@ -1522,11 +1548,19 @@ void JsonUtils::merge(JsonNode & dest, JsonNode & source)
switch (source.getType())
{
break; case JsonNode::DATA_NULL: dest.clear();
break; case JsonNode::DATA_BOOL: std::swap(dest.Bool(), source.Bool());
break; case JsonNode::DATA_FLOAT: std::swap(dest.Float(), source.Float());
break; case JsonNode::DATA_STRING: std::swap(dest.String(), source.String());
break; case JsonNode::DATA_VECTOR:
case JsonNode::DATA_NULL:
{
dest.clear();
break;
}
case JsonNode::DATA_BOOL:
case JsonNode::DATA_FLOAT:
case JsonNode::DATA_STRING:
{
std::swap(dest, source);
break;
}
case JsonNode::DATA_VECTOR:
{
size_t total = std::min(source.Vector().size(), dest.Vector().size());
@ -1541,8 +1575,9 @@ void JsonUtils::merge(JsonNode & dest, JsonNode & source)
std::move(source.Vector().begin(), source.Vector().end(),
std::back_inserter(dest.Vector()));
}
break;
}
break; case JsonNode::DATA_STRUCT:
case JsonNode::DATA_STRUCT:
{
//recursively merge all entries from struct
BOOST_FOREACH(auto & node, source.Struct())

View File

@ -46,6 +46,9 @@ private:
JsonData data;
public:
/// free to use metadata field
std::string meta;
//Create empty node
JsonNode(JsonType Type = DATA_NULL);
//Create tree from Json-formatted input
@ -63,6 +66,8 @@ public:
bool operator == (const JsonNode &other) const;
bool operator != (const JsonNode &other) const;
void setMeta(std::string metadata, bool recursive = true);
/// Convert node to another type. Converting to NULL will clear all data
void setType(JsonType Type);
JsonType getType() const;
@ -101,6 +106,7 @@ public:
template <typename Handler> void serialize(Handler &h, const int version)
{
h & meta;
// simple saving - save json in its string interpretation
if (h.saving)
{

View File

@ -30,6 +30,7 @@ do
--dest) dest_dir=$2 ; shift 2 ;;
--wog) wog_archive=$2 ; shift 2 ;;
--vcmi) vcmi_archive=$2 ; shift 2 ;;
--convertMP3) useffmpeg=true; shift 1 ;;
--download) download=true ; shift 1 ;;
--validate) validate=true ; shift 1 ;;
*) print_help=true ; shift 1 ;;
@ -57,12 +58,15 @@ then
echo " --vcmi ARCHIVE " "Path to manually downloaded VCMI data package"
echo " " "Requires unzip"
echo
echo " --download " "If specified vcmibuilder will download packages using wget"
echo " --convertMP3 " "Convert all mp3 files into ogg/vorbis"
echo " " "Requires ffmpeg"
echo
echo " --download " "Automatically download requied packages using wget"
echo " " "Requires wget and Internet connection"
echo
echo " --dest DIRECTORY " "Path where resulting data will be placed. Default is ~/.vcmi"
echo
echo " --validate " "If specified vcmibuilder will run basic validness checks"
echo " --validate " "Run basic validness checks"
exit 0
fi
@ -110,6 +114,11 @@ then
fi
fi
if [[ -n "$useffmpeg" ]]
then
test_utility "ffmpeg" "-version"
fi
if [[ -n "$cd1_dir" ]]
then
test_utility "unshield" "-V"
@ -251,6 +260,25 @@ then
unzip -qo "$vcmi_archive" -d "$dest_dir" || fail "Error: failed to extract VCMI archive!"
fi
if [[ -n "$useffmpeg" ]]
then
# now when all music files (including WoG theme) were installed convert them to ogg
echo "Converting mp3 files..."
OIFS="$IFS"
IFS=$'\n'
for file in `find "$dest_dir" -type f -name "*.mp3"`
do
echo "Converting $file"
ogg=${file%.*}
ffmpeg -y -i "$file" -acodec libvorbis "$ogg".ogg 2>/dev/null || fail "Error: failed to convert $file to ogg/vorbis using ffmpeg" "rm -f "$ogg".ogg"
rm -f $file
done
IFS="$OIFS"
fi
if [[ -n "$validate" ]]
then
test -f "$dest_dir"/Data/H3bitmap.lod || fail "Error: Heroes 3 data files are missing!"