mirror of
https://github.com/FunKey-Project/RetroFE.git
synced 2025-12-12 17:58:53 +01:00
Added favorites playlist support for menus and merged/sub collections.
Improved sorting algorithm for playlists. The order now follows the order of the complete lis t. Added favorites playlist display to Aeon Nox theme. Added favorites common artwork. Added reloadableMedia support for playlist display.
This commit is contained in:
parent
596c636eda
commit
304154a4c4
Binary file not shown.
|
After Width: | Height: | Size: 118 KiB |
@ -1970,4 +1970,46 @@
|
||||
</reloadableVideo>
|
||||
|
||||
|
||||
<!----------------------------------------------------------------------------------------------------------------------------------->
|
||||
<!-- Playlist -->
|
||||
<!----------------------------------------------------------------------------------------------------------------------------------->
|
||||
|
||||
|
||||
<reloadableImage type="playlist" mode="common" alpha="0" x="1100" y="430" xOrigin="center" yOrigin="top" maxHeight="40" layer="19">
|
||||
<onEnter>
|
||||
<set duration=".8">
|
||||
<animate type="nop"/>
|
||||
</set>
|
||||
</onEnter>
|
||||
<onExit>
|
||||
<set duration=".25">
|
||||
<animate type="alpha" to="0" algorithm="easeinquadratic"/>
|
||||
</set>
|
||||
</onExit>
|
||||
<onMenuEnter menuIndex="0">
|
||||
<set duration=".8">
|
||||
<animate type="y" to="430" algorithm="easeinquadratic"/>
|
||||
</set>
|
||||
</onMenuEnter>
|
||||
<onMenuEnter menuIndex="1">
|
||||
<set duration=".25">
|
||||
<animate type="y" from="-50" to="2" algorithm="easeinquadratic"/>
|
||||
</set>
|
||||
</onMenuEnter>
|
||||
<onMenuExit>
|
||||
<set duration=".25">
|
||||
<animate type="alpha" to="0" algorithm="easeinquadratic"/>
|
||||
</set>
|
||||
</onMenuExit>
|
||||
<onIdle>
|
||||
<set duration="1">
|
||||
<animate type="alpha" to="0.5" algorithm="easeinquadratic"/>
|
||||
</set>
|
||||
<set duration="1">
|
||||
<animate type="alpha" to="1" algorithm="easeinquadratic"/>
|
||||
</set>
|
||||
</onIdle>
|
||||
</reloadableImage>
|
||||
|
||||
|
||||
</layout>
|
||||
|
||||
@ -1970,4 +1970,46 @@
|
||||
</reloadableVideo>
|
||||
|
||||
|
||||
<!----------------------------------------------------------------------------------------------------------------------------------->
|
||||
<!-- Playlist -->
|
||||
<!----------------------------------------------------------------------------------------------------------------------------------->
|
||||
|
||||
|
||||
<reloadableImage type="playlist" mode="common" alpha="0" x="940" y="505" xOrigin="center" yOrigin="top" maxHeight="40" layer="19">
|
||||
<onEnter>
|
||||
<set duration=".8">
|
||||
<animate type="nop"/>
|
||||
</set>
|
||||
</onEnter>
|
||||
<onExit>
|
||||
<set duration=".25">
|
||||
<animate type="alpha" to="0" algorithm="easeinquadratic"/>
|
||||
</set>
|
||||
</onExit>
|
||||
<onMenuEnter menuIndex="0">
|
||||
<set duration=".8">
|
||||
<animate type="y" to="505" algorithm="easeinquadratic"/>
|
||||
</set>
|
||||
</onMenuEnter>
|
||||
<onMenuEnter menuIndex="1">
|
||||
<set duration=".25">
|
||||
<animate type="y" from="-50" to="2" algorithm="easeinquadratic"/>
|
||||
</set>
|
||||
</onMenuEnter>
|
||||
<onMenuExit>
|
||||
<set duration=".25">
|
||||
<animate type="alpha" to="0" algorithm="easeinquadratic"/>
|
||||
</set>
|
||||
</onMenuExit>
|
||||
<onIdle>
|
||||
<set duration="1">
|
||||
<animate type="alpha" to="0.5" algorithm="easeinquadratic"/>
|
||||
</set>
|
||||
<set duration="1">
|
||||
<animate type="alpha" to="1" algorithm="easeinquadratic"/>
|
||||
</set>
|
||||
</onIdle>
|
||||
</reloadableImage>
|
||||
|
||||
|
||||
</layout>
|
||||
|
||||
@ -1970,4 +1970,46 @@
|
||||
</reloadableVideo>
|
||||
|
||||
|
||||
<!----------------------------------------------------------------------------------------------------------------------------------->
|
||||
<!-- Playlist -->
|
||||
<!----------------------------------------------------------------------------------------------------------------------------------->
|
||||
|
||||
|
||||
<reloadableImage type="playlist" mode="common" alpha="0" x="1100" y="430" xOrigin="center" yOrigin="top" maxHeight="40" layer="19">
|
||||
<onEnter>
|
||||
<set duration=".8">
|
||||
<animate type="nop"/>
|
||||
</set>
|
||||
</onEnter>
|
||||
<onExit>
|
||||
<set duration=".25">
|
||||
<animate type="alpha" to="0" algorithm="easeinquadratic"/>
|
||||
</set>
|
||||
</onExit>
|
||||
<onMenuEnter menuIndex="0">
|
||||
<set duration=".8">
|
||||
<animate type="y" to="430" algorithm="easeinquadratic"/>
|
||||
</set>
|
||||
</onMenuEnter>
|
||||
<onMenuEnter menuIndex="1">
|
||||
<set duration=".25">
|
||||
<animate type="y" from="-50" to="2" algorithm="easeinquadratic"/>
|
||||
</set>
|
||||
</onMenuEnter>
|
||||
<onMenuExit>
|
||||
<set duration=".25">
|
||||
<animate type="alpha" to="0" algorithm="easeinquadratic"/>
|
||||
</set>
|
||||
</onMenuExit>
|
||||
<onIdle>
|
||||
<set duration="1">
|
||||
<animate type="alpha" to="0.5" algorithm="easeinquadratic"/>
|
||||
</set>
|
||||
<set duration="1">
|
||||
<animate type="alpha" to="1" algorithm="easeinquadratic"/>
|
||||
</set>
|
||||
</onIdle>
|
||||
</reloadableImage>
|
||||
|
||||
|
||||
</layout>
|
||||
|
||||
@ -51,16 +51,6 @@ CollectionInfo::CollectionInfo(std::string name,
|
||||
|
||||
CollectionInfo::~CollectionInfo()
|
||||
{
|
||||
// remove items from the subcollections so their destructors do not
|
||||
// delete the items since the parent collection will delete them.
|
||||
std::vector<CollectionInfo *>::iterator subit;
|
||||
for (subit = subcollections_.begin(); subit != subcollections_.end(); subit++)
|
||||
{
|
||||
CollectionInfo *info = *subit;
|
||||
info->items.clear();
|
||||
}
|
||||
|
||||
|
||||
Playlists_T::iterator pit = playlists.begin();
|
||||
|
||||
while(pit != playlists.end())
|
||||
@ -129,7 +119,14 @@ bool CollectionInfo::Save()
|
||||
std::vector<Item *> *saveitems = playlists["favorites"];
|
||||
for(std::vector<Item *>::iterator it = saveitems->begin(); it != saveitems->end(); it++)
|
||||
{
|
||||
filestream << (*it)->name << std::endl;
|
||||
if ((*it)->collectionInfo->name == name)
|
||||
{
|
||||
filestream << (*it)->name << std::endl;
|
||||
}
|
||||
else
|
||||
{
|
||||
filestream << "_" << (*it)->collectionInfo->name << ":" << (*it)->name << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
filestream.close();
|
||||
@ -163,16 +160,9 @@ void CollectionInfo::extensionList(std::vector<std::string> &extensionlist)
|
||||
|
||||
void CollectionInfo::addSubcollection(CollectionInfo *newinfo)
|
||||
{
|
||||
subcollections_.push_back(newinfo);
|
||||
|
||||
items.insert(items.begin(), newinfo->items.begin(), newinfo->items.end());
|
||||
}
|
||||
|
||||
bool CollectionInfo::hasSubcollections()
|
||||
{
|
||||
return (subcollections_.size() > 0);
|
||||
}
|
||||
|
||||
bool CollectionInfo::itemIsLess(Item *lhs, Item *rhs)
|
||||
{
|
||||
if(lhs->leaf && !rhs->leaf) return true;
|
||||
@ -189,3 +179,28 @@ void CollectionInfo::sortItems()
|
||||
std::sort(it->second->begin(), it->second->end(), itemIsLess);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void CollectionInfo::sortFavoriteItems()
|
||||
{
|
||||
|
||||
std::vector<Item *> *allItems = playlists["all"];
|
||||
std::vector<Item *> favItems;
|
||||
for(std::vector <Item *>::iterator itFav = playlists["favorites"]->begin(); itFav != playlists["favorites"]->end(); itFav++)
|
||||
{
|
||||
favItems.push_back((*itFav));
|
||||
}
|
||||
playlists["favorites"]->clear();
|
||||
|
||||
|
||||
for(std::vector <Item *>::iterator itAll = allItems->begin(); itAll != allItems->end(); itAll++)
|
||||
{
|
||||
for(std::vector <Item *>::iterator itFav = favItems.begin(); itFav != favItems.end(); itFav++)
|
||||
{
|
||||
if ((*itAll) == (*itFav))
|
||||
{
|
||||
playlists["favorites"]->push_back((*itAll));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -29,8 +29,8 @@ public:
|
||||
std::string settingsPath() const;
|
||||
bool Save();
|
||||
void sortItems();
|
||||
void sortFavoriteItems();
|
||||
void addSubcollection(CollectionInfo *info);
|
||||
bool hasSubcollections();
|
||||
void extensionList(std::vector<std::string> &extensions);
|
||||
std::string name;
|
||||
std::string listpath;
|
||||
@ -44,7 +44,6 @@ public:
|
||||
|
||||
bool menusort;
|
||||
private:
|
||||
std::vector<CollectionInfo *> subcollections_;
|
||||
std::string metadataPath_;
|
||||
std::string extensions_;
|
||||
static bool itemIsLess(Item *lhs, Item *rhs);
|
||||
|
||||
@ -232,12 +232,9 @@ bool CollectionInfoBuilder::ImportDirectory(CollectionInfo *info, std::string me
|
||||
DIR *dp;
|
||||
struct dirent *dirp;
|
||||
std::string path = info->listpath;
|
||||
std::map<std::string, Item *> allMap;
|
||||
std::map<std::string, Item *> includeFilter;
|
||||
std::map<std::string, Item *> favoritesFilter;
|
||||
std::map<std::string, Item *> excludeFilter;
|
||||
std::string includeFile = Utils::combinePath(Configuration::absolutePath, "collections", info->name, "include.txt");
|
||||
std::string favoritesFile = Utils::combinePath(Configuration::absolutePath, "collections", info->name, "playlists/favorites.txt");
|
||||
std::string excludeFile = Utils::combinePath(Configuration::absolutePath, "collections", info->name, "exclude.txt");
|
||||
|
||||
std::string launcher;
|
||||
@ -344,28 +341,52 @@ bool CollectionInfoBuilder::ImportDirectory(CollectionInfo *info, std::string me
|
||||
excludeFilter.erase(it);
|
||||
}
|
||||
|
||||
for(std::vector<Item *>::iterator it = info->items.begin(); it != info->items.end(); it++) {
|
||||
allMap[(*it)->fullTitle] = *it;
|
||||
}
|
||||
|
||||
|
||||
ImportBasicList(info, favoritesFile, favoritesFilter);
|
||||
info->playlists["all"] = &info->items;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
void CollectionInfoBuilder::addFavorites(CollectionInfo *info)
|
||||
{
|
||||
std::map<std::string, Item *> favoritesFilter;
|
||||
std::string favoritesFile = Utils::combinePath(Configuration::absolutePath, "collections", info->name, "playlists/favorites.txt");
|
||||
ImportBasicList(info, favoritesFile, favoritesFilter);
|
||||
|
||||
info->playlists["favorites"] = new std::vector<Item *>();
|
||||
|
||||
// add the favorites list
|
||||
for(std::map<std::string, Item *>::iterator it = favoritesFilter.begin(); it != favoritesFilter.end(); it++)
|
||||
{
|
||||
std::map<std::string, Item *>::iterator itemit = allMap.find(it->first);
|
||||
|
||||
if(itemit != allMap.end())
|
||||
std::string collectionName = info->name;
|
||||
std::string itemName = it->first;
|
||||
if (itemName.at(0) == '_') // name consists of _<collectionName>:<itemName>
|
||||
{
|
||||
info->playlists["favorites"]->push_back(itemit->second);
|
||||
itemName.erase(0, 1); // Remove _
|
||||
size_t position = itemName.find(":");
|
||||
if (position != std::string::npos )
|
||||
{
|
||||
collectionName = itemName.substr(0, position);
|
||||
itemName = itemName.erase(0, position+1);
|
||||
}
|
||||
}
|
||||
|
||||
for(std::vector<Item *>::iterator it = info->items.begin(); it != info->items.end(); it++)
|
||||
{
|
||||
if( (*it)->name == itemName && (*it)->collectionInfo->name == collectionName)
|
||||
{
|
||||
info->playlists["favorites"]->push_back((*it));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
metaDB_.injectMetadata(info);
|
||||
|
||||
return true;
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
void CollectionInfoBuilder::injectMetadata(CollectionInfo *info)
|
||||
{
|
||||
metaDB_.injectMetadata(info);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -31,6 +31,8 @@ public:
|
||||
virtual ~CollectionInfoBuilder();
|
||||
CollectionInfo *buildCollection(std::string collectionName);
|
||||
CollectionInfo *buildCollection(std::string collectionName, std::string mergedCollectionName);
|
||||
void addFavorites(CollectionInfo *info);
|
||||
void injectMetadata(CollectionInfo *info);
|
||||
static bool createCollectionDirectory(std::string collectionName);
|
||||
|
||||
private:
|
||||
|
||||
@ -276,6 +276,10 @@ void ReloadableMedia::reloadTexture()
|
||||
{
|
||||
basename = selectedItem->score;
|
||||
}
|
||||
else if(typeLC == "playlist")
|
||||
{
|
||||
basename = page.getPlaylistName();
|
||||
}
|
||||
|
||||
Utils::replaceSlashesWithUnderscores(basename);
|
||||
|
||||
|
||||
@ -700,7 +700,6 @@ void Page::draw()
|
||||
void Page::removePlaylist()
|
||||
{
|
||||
if(!selectedItem_) return;
|
||||
if(!selectedItem_->leaf) return;
|
||||
|
||||
MenuInfo_S &info = collections_.back();
|
||||
CollectionInfo *collection = info.collection;
|
||||
@ -711,14 +710,15 @@ void Page::removePlaylist()
|
||||
if(it != items->end())
|
||||
{
|
||||
items->erase(it);
|
||||
collection->sortFavoriteItems();
|
||||
collection->saveRequest = true;
|
||||
}
|
||||
collection->Save();
|
||||
}
|
||||
|
||||
void Page::addPlaylist()
|
||||
{
|
||||
if(!selectedItem_) return;
|
||||
if(!selectedItem_->leaf) return;
|
||||
|
||||
MenuInfo_S &info = collections_.back();
|
||||
CollectionInfo *collection = info.collection;
|
||||
@ -727,9 +727,10 @@ void Page::addPlaylist()
|
||||
if(playlist_->first != "favorites" && std::find(items->begin(), items->end(), selectedItem_) == items->end())
|
||||
{
|
||||
items->push_back(selectedItem_);
|
||||
collection->sortItems();
|
||||
collection->sortFavoriteItems();
|
||||
collection->saveRequest = true;
|
||||
}
|
||||
collection->Save();
|
||||
}
|
||||
|
||||
std::string Page::getCollectionName()
|
||||
|
||||
@ -321,17 +321,21 @@ void RetroFE::run()
|
||||
if(currentPage_)
|
||||
{
|
||||
std::string firstCollection = "Main";
|
||||
bool menuSort = true;
|
||||
|
||||
config_.getProperty("firstCollection", firstCollection);
|
||||
config_.getProperty("collections." + firstCollection + ".list.menuSort", menuSort);
|
||||
config_.setProperty("currentCollection", firstCollection);
|
||||
CollectionInfo *info = getCollection(firstCollection);
|
||||
MenuParser mp;
|
||||
|
||||
mp.buildMenuItems(info, menuSort);
|
||||
|
||||
currentPage_->pushCollection(info);
|
||||
|
||||
bool autoFavorites = true;
|
||||
config_.getProperty("autoFavorites", autoFavorites);
|
||||
|
||||
if (autoFavorites)
|
||||
{
|
||||
currentPage_->selectPlaylist("favorites"); // Switch to favorites playlist
|
||||
}
|
||||
|
||||
currentPage_->onNewItemSelected();
|
||||
currentPage_->reallocateMenuSpritePoints();
|
||||
|
||||
@ -344,6 +348,36 @@ void RetroFE::run()
|
||||
}
|
||||
break;
|
||||
|
||||
case RETROFE_PLAYLIST_REQUEST:
|
||||
currentPage_->highlightExit();
|
||||
currentPage_->setScrolling(Page::ScrollDirectionIdle);
|
||||
state = RETROFE_PLAYLIST_EXIT;
|
||||
break;
|
||||
|
||||
case RETROFE_PLAYLIST_EXIT:
|
||||
if (currentPage_->isIdle())
|
||||
{
|
||||
currentPage_->onNewItemSelected();
|
||||
state = RETROFE_PLAYLIST_LOAD_ART;
|
||||
}
|
||||
break;
|
||||
|
||||
case RETROFE_PLAYLIST_LOAD_ART:
|
||||
if (currentPage_->isIdle())
|
||||
{
|
||||
currentPage_->reallocateMenuSpritePoints();
|
||||
currentPage_->highlightEnter();
|
||||
state = RETROFE_PLAYLIST_ENTER;
|
||||
}
|
||||
break;
|
||||
|
||||
case RETROFE_PLAYLIST_ENTER:
|
||||
if (currentPage_->isIdle())
|
||||
{
|
||||
state = RETROFE_IDLE;
|
||||
}
|
||||
break;
|
||||
|
||||
case RETROFE_HIGHLIGHT_REQUEST:
|
||||
currentPage_->highlightExit();
|
||||
currentPage_->setScrolling(Page::ScrollDirectionIdle);
|
||||
@ -395,6 +429,8 @@ void RetroFE::run()
|
||||
case RETROFE_NEXT_PAGE_MENU_EXIT:
|
||||
if(currentPage_->isIdle())
|
||||
{
|
||||
lastMenuOffsets_[currentPage_->getCollectionName()] = currentPage_->getScrollOffsetIndex();
|
||||
lastMenuPlaylists_[currentPage_->getCollectionName()] = currentPage_->getPlaylistName();
|
||||
// Load new layout if available
|
||||
std::string layoutName;
|
||||
config_.getProperty("layout", layoutName);
|
||||
@ -408,14 +444,10 @@ void RetroFE::run()
|
||||
currentPage_ = page;
|
||||
}
|
||||
|
||||
bool menuSort = true;
|
||||
config_.setProperty("currentCollection", nextPageName);
|
||||
config_.getProperty("collections." + nextPageName + ".list.menuSort", menuSort);
|
||||
|
||||
CollectionInfo *info = getCollection(nextPageName);
|
||||
|
||||
MenuParser mp;
|
||||
mp.buildMenuItems(info, menuSort);
|
||||
currentPage_->pushCollection(info);
|
||||
|
||||
bool rememberMenu = false;
|
||||
@ -502,6 +534,26 @@ void RetroFE::run()
|
||||
currentPage_->popCollection();
|
||||
}
|
||||
config_.setProperty("currentCollection", currentPage_->getCollectionName());
|
||||
|
||||
bool rememberMenu = false;
|
||||
config_.getProperty("rememberMenu", rememberMenu);
|
||||
bool autoFavorites = true;
|
||||
config_.getProperty("autoFavorites", autoFavorites);
|
||||
|
||||
if (rememberMenu && lastMenuPlaylists_.find(currentPage_->getCollectionName()) != lastMenuPlaylists_.end())
|
||||
{
|
||||
currentPage_->selectPlaylist(lastMenuPlaylists_[currentPage_->getCollectionName()]); // Switch to last playlist
|
||||
}
|
||||
else if (autoFavorites)
|
||||
{
|
||||
currentPage_->selectPlaylist("favorites"); // Switch to favorites playlist
|
||||
}
|
||||
|
||||
if(rememberMenu && lastMenuOffsets_.find(currentPage_->getCollectionName()) != lastMenuOffsets_.end())
|
||||
{
|
||||
currentPage_->setScrollOffsetIndex(lastMenuOffsets_[currentPage_->getCollectionName()]);
|
||||
}
|
||||
|
||||
currentPage_->onNewItemSelected();
|
||||
currentPage_->reallocateMenuSpritePoints();
|
||||
state = RETROFE_BACK_MENU_LOAD_ART;
|
||||
@ -671,19 +723,17 @@ RetroFE::RETROFE_STATE RetroFE::processUserInput(Page *page)
|
||||
if(input_.newKeyPressed(UserInput::KeyCodeNextPlaylist))
|
||||
{
|
||||
page->nextPlaylist();
|
||||
page->reallocateMenuSpritePoints();
|
||||
state = RETROFE_HIGHLIGHT_REQUEST;
|
||||
state = RETROFE_PLAYLIST_REQUEST;
|
||||
}
|
||||
if(input_.newKeyPressed(UserInput::KeyCodeRemovePlaylist))
|
||||
{
|
||||
page->removePlaylist();
|
||||
page->onNewItemSelected();
|
||||
page->reallocateMenuSpritePoints();
|
||||
state = RETROFE_PLAYLIST_REQUEST;
|
||||
}
|
||||
if(input_.newKeyPressed(UserInput::KeyCodeAddPlaylist))
|
||||
{
|
||||
page->addPlaylist();
|
||||
page->reallocateMenuSpritePoints();
|
||||
state = RETROFE_PLAYLIST_REQUEST;
|
||||
}
|
||||
if(input_.keystate(UserInput::KeyCodeRandom))
|
||||
{
|
||||
@ -779,6 +829,8 @@ CollectionInfo *RetroFE::getCollection(std::string collectionName)
|
||||
|
||||
CollectionInfoBuilder cib(config_, *metadb_);
|
||||
CollectionInfo *collection = cib.buildCollection(collectionName);
|
||||
cib.injectMetadata(collection);
|
||||
|
||||
DIR *dp;
|
||||
struct dirent *dirp;
|
||||
|
||||
@ -803,11 +855,21 @@ CollectionInfo *RetroFE::getCollection(std::string collectionName)
|
||||
|
||||
CollectionInfo *subcollection = cib.buildCollection(basename, collectionName);
|
||||
collection->addSubcollection(subcollection);
|
||||
cib.injectMetadata(subcollection);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
collection->sortItems();
|
||||
|
||||
bool menuSort = true;
|
||||
config_.getProperty("collections." + collectionName + ".list.menuSort", menuSort);
|
||||
MenuParser mp;
|
||||
mp.buildMenuItems(collection, menuSort);
|
||||
|
||||
cib.addFavorites(collection);
|
||||
collection->sortFavoriteItems();
|
||||
|
||||
return collection;
|
||||
}
|
||||
|
||||
|
||||
@ -55,6 +55,10 @@ private:
|
||||
RETROFE_LOAD_ART,
|
||||
RETROFE_ENTER,
|
||||
RETROFE_SPLASH_EXIT,
|
||||
RETROFE_PLAYLIST_REQUEST,
|
||||
RETROFE_PLAYLIST_EXIT,
|
||||
RETROFE_PLAYLIST_LOAD_ART,
|
||||
RETROFE_PLAYLIST_ENTER,
|
||||
RETROFE_HIGHLIGHT_REQUEST,
|
||||
RETROFE_HIGHLIGHT_EXIT,
|
||||
RETROFE_HIGHLIGHT_LOAD_ART,
|
||||
|
||||
@ -20,7 +20,7 @@
|
||||
|
||||
std::string retrofe_version_major = "0";
|
||||
std::string retrofe_version_minor = "7";
|
||||
std::string retrofe_version_build = "15";
|
||||
std::string retrofe_version_build = "16";
|
||||
|
||||
|
||||
std::string Version::getString()
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user