/*************************************************************************** * __________ __ ___. * Open \______ \ ____ ____ | | _\_ |__ _______ ___ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ * \/ \/ \/ \/ \/ * * Copyright (C) 2020 by Dominik Riebeling * * All files in this archive are subject to the GNU General Public License. * See the file COPYING in the source tree root for full license agreement. * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY * KIND, either express or implied. * ****************************************************************************/ #include "playerbuildinfo.h" #include "rbsettings.h" #include "Logger.h" PlayerBuildInfo* PlayerBuildInfo::infoInstance = nullptr; PlayerBuildInfo* PlayerBuildInfo::instance() { if (infoInstance == nullptr) { infoInstance = new PlayerBuildInfo(); } return infoInstance; } // server infos const static struct { PlayerBuildInfo::BuildInfo item; const char* name; } ServerInfoList[] = { { PlayerBuildInfo::BuildVoiceLangs, "voices/:version:" }, { PlayerBuildInfo::BuildVersion, ":build:/:target:" }, { PlayerBuildInfo::BuildUrl, ":build:/build_url" }, { PlayerBuildInfo::BuildVoiceUrl, ":build:/voice_url" }, { PlayerBuildInfo::BuildManualUrl, ":build:/manual_url" }, { PlayerBuildInfo::BuildSourceUrl, ":build:/source_url" }, { PlayerBuildInfo::BuildFontUrl, ":build:/font_url" }, // other URLs -- those are not directly related to the build, but handled here. { PlayerBuildInfo::DoomUrl, "other/doom_url" }, { PlayerBuildInfo::Duke3DUrl, "other/duke3d_url" }, { PlayerBuildInfo::PuzzFontsUrl, "other/puzzfonts_url" }, { PlayerBuildInfo::QuakeUrl, "other/quake_url" }, { PlayerBuildInfo::Wolf3DUrl, "other/wolf3d_url" }, { PlayerBuildInfo::XWorldUrl, "other/xworld_url" }, { PlayerBuildInfo::MidiPatchsetUrl, "other/patcheset_url" }, }; const static struct { PlayerBuildInfo::DeviceInfo item; const char* name; } PlayerInfoList[] = { { PlayerBuildInfo::BuildStatus, "status/:target:" }, { PlayerBuildInfo::DisplayName, ":target:/name" }, { PlayerBuildInfo::BootloaderMethod, ":target:/bootloadermethod" }, { PlayerBuildInfo::BootloaderName, ":target:/bootloadername" }, { PlayerBuildInfo::BootloaderFile, ":target:/bootloaderfile" }, { PlayerBuildInfo::BootloaderFilter, ":target:/bootloaderfilter" }, { PlayerBuildInfo::Encoder, ":target:/encoder" }, { PlayerBuildInfo::Brand, ":target:/brand" }, { PlayerBuildInfo::PlayerPicture, ":target:/playerpic" }, { PlayerBuildInfo::TargetNamesAll, "_targets/all" }, { PlayerBuildInfo::TargetNamesEnabled, "_targets/enabled" }, { PlayerBuildInfo::LanguageInfo, "languages/:target:" }, { PlayerBuildInfo::LanguageList, "_languages/list" }, }; const static struct { PlayerBuildInfo::SystemUrl item; const char* name; } PlayerSystemUrls[] = { { PlayerBuildInfo::BootloaderUrl, "bootloader/download_url" }, { PlayerBuildInfo::BuildInfoUrl, "build_info_url" }, { PlayerBuildInfo::GenlangUrl, "genlang_url" }, { PlayerBuildInfo::ThemesUrl, "themes_url" }, { PlayerBuildInfo::ThemesInfoUrl, "themes_info_url" }, { PlayerBuildInfo::RbutilUrl, "rbutil_url" }, }; PlayerBuildInfo::PlayerBuildInfo() : serverInfo(nullptr), playerInfo(":/ini/rbutil.ini", QSettings::IniFormat) { } void PlayerBuildInfo::setBuildInfo(QString file) { if (serverInfo) delete serverInfo; LOG_INFO() << "updated:" << file; serverInfo = new QSettings(file, QSettings::IniFormat); } QVariant PlayerBuildInfo::value(BuildInfo item, BuildType type) { // locate setting item in server info file int i = 0; while(ServerInfoList[i].item != item) i++; // split of variant for target. // we can have an optional variant part in the target string. // For build info we don't use that. QString target = RbSettings::value(RbSettings::CurrentPlatform).toString().split('.').at(0); QString s = ServerInfoList[i].name; s.replace(":target:", target); QString v; switch(type) { case TypeRelease: v = "release"; break; case TypeCandidate: v = "release-candidate"; break; case TypeDaily: v = "daily"; break; case TypeDevel: v = "development"; break; } QVariant result = QString(); if (!serverInfo) return result; QStringList version = serverInfo->value(v + "/" + target, "").toStringList(); s.replace(":build:", v); s.replace(":version:", version.at(0)); // get value from server build-info // we need to get a version string, otherwise the data is invalid. // For invalid data return an empty string. if(version.at(0).isEmpty()) { LOG_INFO() << s << "(version invalid)"; return result; } if(!s.isEmpty()) result = serverInfo->value(s); // depending on the actual value we need more replacements. switch(item) { case BuildVersion: result = result.toStringList().at(0); break; case BuildUrl: if(version.size() > 1) { // version info has an URL appended. Takes precendence. result = version.at(1); } break; case BuildVoiceLangs: if (type == TypeDaily) s = "voices/daily"; result = serverInfo->value(s); break; case BuildManualUrl: { // special case: if playerInfo has a non-empty manualname entry for the // target, use that as target for the manual name. QString manualtarget = playerInfo.value(target + "/manualname", "").toString(); if(!manualtarget.isEmpty()) target = manualtarget; break; } default: break; } // if the value is a string we can replace some patterns. // if we cannot convert it (f.e. for a QStringList) we leave as-is, since // the conversion would return an empty type. if (result.canConvert(QMetaType::QString)) result = result.toString() .replace("%TARGET%", target) .replace("%VERSION%", version.at(0)); LOG_INFO() << "B:" << s << result; return result; } QVariant PlayerBuildInfo::value(DeviceInfo item, QString target) { // locate setting item in server info file int i = 0; while(PlayerInfoList[i].item != item) i++; // split of variant for target. // we can have an optional variant part in the target string. // For device info we use this. if (target.isEmpty()) target = RbSettings::value(RbSettings::CurrentPlatform).toString(); QVariant result = QString(); QString s = PlayerInfoList[i].name; s.replace(":target:", target); switch(item) { case BuildStatus: { // build status is the only value that doesn't depend on the version // but the selected target instead. bool ok = false; if (serverInfo) result = serverInfo->value(s).toInt(&ok); if (!ok) result = -1; break; } case TargetNamesAll: // list of all internal target names. Doesn't depend on the passed target. result = targetNames(true); break; case TargetNamesEnabled: // list of all non-disabled target names. Doesn't depend on the passed target. result = targetNames(false); break; case LanguageList: // Return a map (language, display string). { // need to use (QString, QVariant) here, so we can put the map into // a QVariant by itself. QMap m; playerInfo.beginGroup("languages"); QStringList a = playerInfo.childKeys(); for(int i = 0; i < a.size(); i++) { QStringList v = playerInfo.value(a.at(i)).toStringList(); m[v.at(0)] = v.at(1); } playerInfo.endGroup(); result = m; } break; default: result = playerInfo.value(s); break; } LOG_INFO() << "T:" << s << result; return result; } QVariant PlayerBuildInfo::value(SystemUrl item) { // locate setting item in server info file int i = 0; while(PlayerSystemUrls[i].item != item) i++; QVariant result = playerInfo.value(PlayerSystemUrls[i].name); LOG_INFO() << "U:" << PlayerSystemUrls[i].name << result; return result; } QString PlayerBuildInfo::statusAsString(QString platform) { QString result; switch(value(BuildStatus, platform).toInt()) { case STATUS_RETIRED: result = tr("Stable (Retired)"); break; case STATUS_UNUSABLE: result = tr("Unusable"); break; case STATUS_UNSTABLE: result = tr("Unstable"); break; case STATUS_STABLE: result = tr("Stable"); break; default: result = tr("Unknown"); break; } return result; } QStringList PlayerBuildInfo::targetNames(bool all) { QStringList result; playerInfo.beginGroup("platforms"); QStringList a = playerInfo.childKeys(); playerInfo.endGroup(); for(int i = 0; i < a.size(); i++) { QString target = playerInfo.value("platforms/" + a.at(i), "null").toString(); if(playerInfo.value(target + "/status").toString() != "disabled" || all) { result.append(target); } } result.removeDuplicates(); return result; }