Add option to transform gamenames

Configuration file $XDG_CONFIG_HOME/lgogdownloader/transformations.json can be used to set rules for transforming gamenames.
New subdir templates: %gamename_transformed% and %gamename_transformed_firstletter%
"--list transformations" can be used to show transformed gamenames
This commit is contained in:
Sude 2023-04-15 22:19:07 +03:00
parent 8a2a6d2071
commit dabfcfc180
6 changed files with 113 additions and 14 deletions

View File

@ -282,6 +282,7 @@ class Config
std::string sIgnorelistFilePath;
std::string sGameHasDLCListFilePath;
std::string sReportFilePath;
std::string sTransformConfigFilePath;
std::string sXMLFile;
@ -321,6 +322,8 @@ class Config
int iProgressInterval;
int iMsgLevel;
unsigned int iListFormat;
Json::Value transformationsJSON;
};
#endif // CONFIG_H__

View File

@ -118,17 +118,19 @@ namespace GlobalConstants
{ CDN_AKAMAI, "akamai_edgecast_proxy", "Akamai", "akamai|akamai_cdn|akamai_ec|akamai_edgecast_proxy" }
};
const unsigned int LIST_FORMAT_GAMES = 1 << 0;
const unsigned int LIST_FORMAT_DETAILS_TEXT = 1 << 1;
const unsigned int LIST_FORMAT_DETAILS_JSON = 1 << 2;
const unsigned int LIST_FORMAT_TAGS = 1 << 3;
const unsigned int LIST_FORMAT_GAMES = 1 << 0;
const unsigned int LIST_FORMAT_DETAILS_TEXT = 1 << 1;
const unsigned int LIST_FORMAT_DETAILS_JSON = 1 << 2;
const unsigned int LIST_FORMAT_TAGS = 1 << 3;
const unsigned int LIST_FORMAT_TRANSFORMATIONS = 1 << 4;
const std::vector<optionsStruct> LIST_FORMAT =
{
{ LIST_FORMAT_GAMES, "games", "Games", "g|games" },
{ LIST_FORMAT_DETAILS_TEXT, "details", "Details", "d|details" },
{ LIST_FORMAT_DETAILS_JSON, "json", "JSON", "j|json" },
{ LIST_FORMAT_TAGS, "tags", "Tags", "t|tags" }
{ LIST_FORMAT_TAGS, "tags", "Tags", "t|tags" },
{ LIST_FORMAT_TRANSFORMATIONS, "transform", "Transformations", "tr|transform|transformations" }
};
}

View File

@ -79,6 +79,7 @@ int main(int argc, char *argv[])
Globals::globalConfig.sBlacklistFilePath = Globals::globalConfig.sConfigDirectory + "/blacklist.txt";
Globals::globalConfig.sIgnorelistFilePath = Globals::globalConfig.sConfigDirectory + "/ignorelist.txt";
Globals::globalConfig.sGameHasDLCListFilePath = Globals::globalConfig.sConfigDirectory + "/game_has_dlc.txt";
Globals::globalConfig.sTransformConfigFilePath = Globals::globalConfig.sConfigDirectory + "/transformations.json";
Globals::galaxyConf.setFilepath(Globals::globalConfig.sConfigDirectory + "/galaxy_tokens.json");
@ -151,7 +152,7 @@ int main(int argc, char *argv[])
std::string check_orphans_text = "Check for orphaned files (files found on local filesystem that are not found on GOG servers). Sets regular expression filter (Perl syntax) for files to check. If no argument is given then the regex defaults to '" + orphans_regex_default + "'";
// Help text for subdir options
std::string subdir_help_text = "\nTemplates:\n- %platform%\n- %gamename%\n- %gamename_firstletter%\n- %dlcname%";
std::string subdir_help_text = "\nTemplates:\n- %platform%\n- %gamename%\n- %gamename_firstletter%\n- %dlcname%\n- %gamename_transformed%\n- %gamename_transformed_firstletter%";
// Help text for include and exclude options
std::string include_options_text;
@ -432,6 +433,25 @@ int main(int argc, char *argv[])
}
}
if (boost::filesystem::exists(Globals::globalConfig.sTransformConfigFilePath))
{
std::ifstream ifs(Globals::globalConfig.sTransformConfigFilePath, std::ifstream::binary);
Json::Value json;
try {
ifs >> json;
if (!json.empty())
{
Globals::globalConfig.transformationsJSON = json;
}
} catch (const Json::Exception& exc) {
std::cerr << "Failed to parse " << Globals::globalConfig.sTransformConfigFilePath<< std::endl;
std::cerr << exc.what() << std::endl;
}
if (ifs)
ifs.close();
}
if (!bUseDLCList)
Globals::globalConfig.sGameHasDLCList = "";

View File

@ -109,6 +109,42 @@ Must be in the following format:
.br
}
.TP
\fI$XDG_CONFIG_HOME/lgogdownloader/transformations.json\fP
JSON formatted file. Used to transform gamenames.
.br
Must be in the following format:
.br
{
<string> :
{
"regex" : <string>,
"replacement" : <string>,
},
<string> :
{
"regex" : <string>,
"replacement" : <string>,
},
.br
}
.br
Member names are used to match the gamename.
If it matches then \fBregex\fP is used for the actual replacement using the value in \fBreplacement\fP.
.br
\fBExample:\fP
.br
match all games beginning with "\fBb\fP" and if they end with "\fB_the\fP" then remove "\fB_the\fP" at the end and prefix it with "\fBthe_\fP"
.br
{
"^b" :
{
"regex" : "(.*)_the$",
"replacement" : "the_\\\\1",
},
.br
}
[priorities]
Separating values with "," when using \fBlanguage\fP and \fBplatform\fP switches enables a priority-based mode: only the first matching one will be downloaded.
.PP

View File

@ -526,7 +526,8 @@ int Downloader::getGameDetails()
int Downloader::listGames()
{
if (Globals::globalConfig.iListFormat == GlobalConstants::LIST_FORMAT_GAMES)
if (Globals::globalConfig.iListFormat == GlobalConstants::LIST_FORMAT_GAMES ||
Globals::globalConfig.iListFormat == GlobalConstants::LIST_FORMAT_TRANSFORMATIONS)
{
if (gameItems.empty())
this->getGameList();
@ -534,15 +535,24 @@ int Downloader::listGames()
for (unsigned int i = 0; i < gameItems.size(); ++i)
{
std::string gamename = gameItems[i].name;
if (gameItems[i].updates > 0)
if (Globals::globalConfig.iListFormat == GlobalConstants::LIST_FORMAT_TRANSFORMATIONS)
{
gamename += " [" + std::to_string(gameItems[i].updates) + "]";
if (Globals::globalConfig.bColor)
gamename = "\033[32m" + gamename + "\033[0m";
std::string str = "%gamename% -> %gamename_transformed%";
Util::filepathReplaceReservedStrings(str, gamename);
std::cout << str << std::endl;
}
else
{
if (gameItems[i].updates > 0)
{
gamename += " [" + std::to_string(gameItems[i].updates) + "]";
if (Globals::globalConfig.bColor)
gamename = "\033[32m" + gamename + "\033[0m";
}
std::cout << gamename << std::endl;
for (unsigned int j = 0; j < gameItems[i].dlcnames.size(); ++j)
std::cout << "+> " << gameItems[i].dlcnames[j] << std::endl;
}
std::cout << gamename << std::endl;
for (unsigned int j = 0; j < gameItems[i].dlcnames.size(); ++j)
std::cout << "+> " << gameItems[i].dlcnames[j] << std::endl;
}
}
else if (Globals::globalConfig.iListFormat == GlobalConstants::LIST_FORMAT_TAGS)

View File

@ -438,6 +438,34 @@ void Util::filepathReplaceReservedStrings(std::string& str, const std::string& g
gamename_firstletter = gamename.front();
}
std::string gamename_transformed = gamename;
if (str.find("%gamename_transformed%") != std::string::npos || str.find("%gamename_transformed_firstletter%") != std::string::npos)
{
for (auto transformMatch : Globals::globalConfig.transformationsJSON.getMemberNames())
{
boost::regex expression(transformMatch);
boost::match_results<std::string::const_iterator> what;
if (boost::regex_search(gamename, what, expression))
{
boost::regex transformRegex(Globals::globalConfig.transformationsJSON[transformMatch]["regex"].asString());
std::string transformReplacement = Globals::globalConfig.transformationsJSON[transformMatch]["replacement"].asString();
gamename_transformed = boost::regex_replace(gamename, transformRegex, transformReplacement);
}
}
std::string gamename_transformed_firstletter;
if (!gamename_transformed.empty())
{
if (std::isdigit(gamename_transformed.front()))
gamename_transformed_firstletter = "0";
else
gamename_transformed_firstletter = gamename_transformed.front();
}
while (Util::replaceString(str, "%gamename_transformed%", gamename_transformed));
while (Util::replaceString(str, "%gamename_transformed_firstletter%", gamename_transformed_firstletter));
}
while (Util::replaceString(str, "%gamename_firstletter%", gamename_firstletter));
while (Util::replaceString(str, "%gamename%", gamename));
while (Util::replaceString(str, "%dlcname%", dlcname));