void CMissionManager::LoadModListFromXml(const XmlDocumentPtr& doc) { assert(doc != NULL); /* Example XML Snippet <mission id="11" title="Living Expenses" releaseDate="2010-01-02" size="5.9" author="Sonosuke"> <downloadLocation language="English" url="http://www.bloodgate.com/mirrors/tdm/pub/pk4/fms/living_expenses.pk4"/> <downloadLocation language="German" url="http://www.bloodgate.com/mirrors/tdm/pub/pk4/fms/living_expenses_de.pk4"/> </mission> */ pugi::xpath_node_set nodes = doc->select_nodes("//tdm/availableMissions//mission"); const char* fs_currentfm = cvarSystem->GetCVarString("fs_currentfm"); // Tels: #3419 - After game start the sequence is always the same, so set a random seed time_t seconds = time(NULL); gameLocal.random.SetSeed( static_cast<int>(seconds) ); for (pugi::xpath_node_set::const_iterator i = nodes.begin(); i != nodes.end(); ++i) { pugi::xml_node node = i->node(); DownloadableMod mission; mission.title = node.attribute("title").value(); mission.id = node.attribute("id").as_int(); mission.sizeMB = node.attribute("size").as_float(); mission.author = node.attribute("author").value(); mission.releaseDate = node.attribute("releaseDate").value(); mission.type = idStr::Icmp(node.attribute("type").value(), "multi") == 0 ? DownloadableMod::Multi : DownloadableMod::Single; mission.modName = node.attribute("internalName").value(); // Tels #3294: We need to clean the server-side modName of things like uppercase letters, trailing ".pk4" etc. // Otherwise we get duplicated entries in the MissionDB like "broads.pk4" and "broads" or "VFAT1" and "vfat1": mission.modName.ToLower(); mission.modName.StripTrailingOnce(".pk4"); // Clean modName string from any weird characters int modNameLen = mission.modName.Length(); for (int i = 0; i < modNameLen; ++i) { if (idStr::CharIsAlpha(mission.modName[i]) || idStr::CharIsNumeric(mission.modName[i])) continue; mission.modName[i] = '_'; // replace non-ASCII keys with underscores } mission.version = node.attribute("version").as_int(); mission.isUpdate = false; mission.needsL10NpackDownload = false; // gnartsch if (idStr::Cmp(mission.modName.c_str(), fs_currentfm) == 0) { DM_LOG(LC_MAINMENU, LT_DEBUG)LOGSTRING("Removing currently installed mission %s from the list of downloadable missions.\r", fs_currentfm); continue; } bool missionExists = false; // Check if this mission is already downloaded for (int j = 0; j < _availableMods.Num(); ++j) { if (idStr::Icmp(_availableMods[j], mission.modName) == 0) { missionExists = true; break; } } if (missionExists) { // Check mod version, there might be an update available if (_missionDB->ModInfoExists(mission.modName)) { CModInfoPtr missionInfo = _missionDB->GetModInfo(mission.modName); idStr versionStr = missionInfo->GetKeyValue("downloaded_version", "1"); int existingVersion = atoi(versionStr.c_str()); if (existingVersion >= mission.version) { // gnartsch : Skip to next mission only in case the localization pack // for the current mission had been downloaded already as well if (missionInfo->isL10NpackInstalled) { continue; // Our version is up to date } } else { mission.isUpdate = true; } } } // gnartsch : Process mission download locations only if the mission itself is not // present or not up to date, otherwise skip to the localization pack if (!missionExists || mission.isUpdate) { // Mission download links pugi::xpath_node_set downloadLocations = node.select_nodes("downloadLocation"); for (pugi::xpath_node_set::const_iterator loc = downloadLocations.begin(); loc != downloadLocations.end(); ++loc) { pugi::xml_node locNode = loc->node(); // Only accept English downloadlinks if (idStr::Icmp(locNode.attribute("language").value(), "english") != 0) continue; // Tels: #3419: Randomize the order of download URLs by inserting at a random place (+2 to avoid the first URL always being placed last) mission.missionUrls.Insert(locNode.attribute("url").value(), gameLocal.random.RandomInt( mission.missionUrls.Num() + 2 ) ); } } // Localisation packs // gnartsch: Process only if mission is either present locally or at least a download link for the mission is available. if (missionExists || mission.missionUrls.Num() > 0) { pugi::xpath_node_set l10PackNodes = node.select_nodes("localisationPack"); for (pugi::xpath_node_set::const_iterator loc = l10PackNodes.begin(); loc != l10PackNodes.end(); ++loc) { pugi::xml_node locNode = loc->node(); // Tels: #3419: Randomize the order of l10n URLs by inserting at a random place (+2 to avoid the first URL always being placed last) mission.l10nPackUrls.Insert(locNode.attribute("url").value(), gameLocal.random.RandomInt( mission.l10nPackUrls.Num() + 2 ) ); // gnartsch: Found a localization pack url for download mission.needsL10NpackDownload = true; } } // Only add missions with valid locations // gnartsch: add the mission in case localization pack needs to be downloaded if (mission.missionUrls.Num() > 0 || mission.l10nPackUrls.Num() > 0) { // Copy-construct the local mission struct into the heap-allocated one _downloadableMods.Append(new DownloadableMod(mission)); } } SortDownloadableMods(); }