Browse Source

Optimize beatmap list loading

Clément Wolf 2 weeks ago
parent
commit
d7145367a5

+ 0 - 4
src/App/Osu/Collections.cpp

@@ -85,8 +85,6 @@ Collection* get_or_create_collection(std::string name) {
     collection->name = name;
     collections.push_back(collection);
 
-    std::sort(collections.begin(), collections.end(), [](Collection* a, Collection* b) { return a->name < b->name; });
-
     return collection;
 }
 
@@ -177,8 +175,6 @@ bool load_collections() {
     }
     free(neosu_collections.memory);
 
-    std::sort(collections.begin(), collections.end(), [](Collection* a, Collection* b) { return a->name < b->name; });
-
     debugLog("collections.db: loading took %f seconds\n", (engine->getTimeReal() - startTime));
     collections_loaded = true;
     return true;

+ 2 - 2
src/App/Osu/Osu.cpp

@@ -889,8 +889,8 @@ void Osu::update() {
             // We didn't select a map; revert to previously selected one
             auto diff2 = m_songBrowser2->m_lastSelectedBeatmap;
             if(diff2 != nullptr) {
-                bancho.room.map_name = UString::format("%s - %s [%s]", diff2->getArtist().toUtf8(),
-                                                       diff2->getTitle().toUtf8(), diff2->getDifficultyName().toUtf8());
+                bancho.room.map_name = UString::format("%s - %s [%s]", diff2->getArtist().c_str(),
+                                                       diff2->getTitle().c_str(), diff2->getDifficultyName().c_str());
                 bancho.room.map_md5 = diff2->getMD5Hash();
                 bancho.room.map_id = diff2->getID();
 

+ 2 - 2
src/App/Osu/OsuBeatmap.cpp

@@ -1409,14 +1409,14 @@ bool OsuBeatmap::isKey2Down() {
 bool OsuBeatmap::isClickHeld() { return isKey1Down() || isKey2Down(); }
 bool OsuBeatmap::isLastKeyDownKey1() { return m_bPrevKeyWasKey1; }
 
-UString OsuBeatmap::getTitle() const {
+std::string OsuBeatmap::getTitle() const {
     if(m_selectedDifficulty2 != NULL)
         return m_selectedDifficulty2->getTitle();
     else
         return "NULL";
 }
 
-UString OsuBeatmap::getArtist() const {
+std::string OsuBeatmap::getArtist() const {
     if(m_selectedDifficulty2 != NULL)
         return m_selectedDifficulty2->getArtist();
     else

+ 2 - 2
src/App/Osu/OsuBeatmap.h

@@ -238,8 +238,8 @@ class OsuBeatmap {
     bool isClickHeld();
     bool isLastKeyDownKey1();
 
-    UString getTitle() const;
-    UString getArtist() const;
+    std::string getTitle() const;
+    std::string getArtist() const;
 
     inline const std::vector<OsuDatabaseBeatmap::BREAK> &getBreaks() const { return m_breaks; }
     unsigned long getBreakDurationTotal() const;

+ 2 - 1
src/App/Osu/OsuChangelog.cpp

@@ -46,7 +46,8 @@ OsuChangelog::OsuChangelog(Osu *osu) : OsuScreenBackable(osu) {
     latest.changes.push_back("- Changed default instant replay key to F2 to avoid conflicts with mod selector");
     latest.changes.push_back("- Disabled score submission when mods are toggled mid-game");
     latest.changes.push_back("- Forced exclusive mode when using WASAPI output");
-    latest.changes.push_back("- Optimized collection processing");
+    latest.changes.push_back("- Optimized beatmap list loading speed");
+    latest.changes.push_back("- Optimized collection processing speed");
     latest.changes.push_back("- Removed support for the Nintendo Switch");
     latest.changes.push_back("- Updated protocol version");
     latest.changes.push_back("- Fixed chat layout updating while chat was hidden");

+ 17 - 18
src/App/Osu/OsuDatabase.cpp

@@ -301,8 +301,6 @@ class OsuDatabaseLoader : public Resource {
             m_bNeedCleanup = true;
             m_toCleanup.swap(m_db->m_databaseBeatmaps);
             m_db->m_databaseBeatmaps.clear();
-
-            m_db->m_fLoadingProgress = 0.25f;
             m_db->loadDB(&db, m_bNeedRawLoad);
         } else
             m_bNeedRawLoad = true;
@@ -1011,7 +1009,7 @@ void OsuDatabase::loadDB(Packet *db, bool &fallbackToRawLoad) {
 
         if(Osu::debug->getBool()) debugLog("Database: Reading beatmap %i/%i ...\n", (i + 1), m_iNumBeatmapsToLoad);
 
-        m_fLoadingProgress = 0.24f + 0.5f * ((float)(i + 1) / (float)m_iNumBeatmapsToLoad);
+        m_fLoadingProgress = ((float)(i + 1) / (float)m_iNumBeatmapsToLoad);
 
         if(m_iVersion < 20191107)  // see https://osu.ppy.sh/home/changelog/stable40/20191107.2
         {
@@ -1022,12 +1020,16 @@ void OsuDatabase::loadDB(Packet *db, bool &fallbackToRawLoad) {
             /*unsigned int size = */ read_int32(db);  // size in bytes of the beatmap entry
         }
 
-        UString artistName = read_string(db).trim();
-        UString artistNameUnicode = read_string(db);
-        UString songTitle = read_string(db).trim();
-        UString songTitleUnicode = read_string(db);
-        UString creatorName = read_string(db).trim();
-        UString difficultyName = read_string(db).trim();
+        std::string artistName = read_stdstring(db);
+        trim(&artistName);
+        std::string artistNameUnicode = read_stdstring(db);
+        std::string songTitle = read_stdstring(db);
+        trim(&songTitle);
+        std::string songTitleUnicode = read_stdstring(db);
+        std::string creatorName = read_stdstring(db);
+        trim(&creatorName);
+        std::string difficultyName = read_stdstring(db);
+        trim(&difficultyName);
         std::string audioFileName = read_stdstring(db);
         auto hash_str = read_stdstring(db);
         MD5Hash md5hash = hash_str.c_str();
@@ -1209,7 +1211,7 @@ void OsuDatabase::loadDB(Packet *db, bool &fallbackToRawLoad) {
                 // diff2->m_sBackgroundImageFileName = "";
 
                 diff2->m_iPreviewTime = previewTime;
-                diff2->m_iLastModificationTime = lastModificationTime;
+                diff2->last_modification_time = lastModificationTime;
 
                 diff2->m_sFullSoundFilePath = beatmapPath;
                 diff2->m_sFullSoundFilePath.append(diff2->m_sAudioFileName);
@@ -1396,7 +1398,7 @@ void OsuDatabase::loadDB(Packet *db, bool &fallbackToRawLoad) {
     // we now have a collection of BeatmapSets (where one set is equal to one beatmap and all of its diffs), build the
     // actual OsuBeatmap objects first, build all beatmaps which have a valid setID (trusting the values from the osu
     // database)
-    std::unordered_map<UString, OsuDatabaseBeatmap *> titleArtistToBeatmap;
+    std::unordered_map<std::string, OsuDatabaseBeatmap *> titleArtistToBeatmap;
     for(int i = 0; i < beatmapSets.size(); i++) {
         if(m_bInterruptLoad.load()) break;  // cancellation point
 
@@ -1414,9 +1416,9 @@ void OsuDatabase::loadDB(Packet *db, bool &fallbackToRawLoad) {
                 }
 
                 // and in the other hashmap
-                UString titleArtist = bm->getTitle();
+                std::string titleArtist = bm->getTitle();
                 titleArtist.append(bm->getArtist());
-                if(titleArtist.length() > 0) titleArtistToBeatmap[UString(titleArtist.toUtf8())] = bm;
+                if(titleArtist.length() > 0) titleArtistToBeatmap[titleArtist] = bm;
             }
         }
     }
@@ -1441,11 +1443,11 @@ void OsuDatabase::loadDB(Packet *db, bool &fallbackToRawLoad) {
                     bool existsAlready = false;
 
                     // new: use hashmap
-                    UString titleArtistCreator = diff2->getTitle();
+                    std::string titleArtistCreator = diff2->getTitle();
                     titleArtistCreator.append(diff2->getArtist());
                     titleArtistCreator.append(diff2->getCreator());
                     if(titleArtistCreator.length() > 0) {
-                        const auto result = titleArtistToBeatmap.find(UString(titleArtistCreator.toUtf8()));
+                        const auto result = titleArtistToBeatmap.find(titleArtistCreator);
                         if(result != titleArtistToBeatmap.end()) {
                             existsAlready = true;
 
@@ -1482,9 +1484,6 @@ void OsuDatabase::loadDB(Packet *db, bool &fallbackToRawLoad) {
     debugLog("Refresh finished, added %i beatmaps in %f seconds.\n", m_databaseBeatmaps.size(),
              m_importTimer->getElapsedTime());
 
-    // signal that we are almost done
-    m_fLoadingProgress = 0.75f;
-
     load_collections();
 
     // signal that we are done

+ 8 - 8
src/App/Osu/OsuDatabaseBeatmap.cpp

@@ -136,7 +136,7 @@ OsuDatabaseBeatmap::OsuDatabaseBeatmap(Osu *osu, std::string filePath, std::stri
 
     // custom data
 
-    m_iLastModificationTime = (std::numeric_limits<long long>::max() / 2) + m_iSortHack;
+    last_modification_time = (std::numeric_limits<long long>::max() / 2) + m_iSortHack;
 
     m_iLocalOffset = 0;
     m_iOnlineOffset = 0;
@@ -1045,25 +1045,25 @@ bool OsuDatabaseBeatmap::loadMetadata(OsuDatabaseBeatmap *databaseBeatmap) {
                         memset(stringBuffer, '\0', 1024);
                         if(sscanf(curLineChar, " Title :%1023[^\n]", stringBuffer) == 1) {
                             databaseBeatmap->m_sTitle = UString(stringBuffer);
-                            databaseBeatmap->m_sTitle = databaseBeatmap->m_sTitle.trim();
+                            trim(&databaseBeatmap->m_sTitle);
                         }
 
                         memset(stringBuffer, '\0', 1024);
                         if(sscanf(curLineChar, " Artist :%1023[^\n]", stringBuffer) == 1) {
                             databaseBeatmap->m_sArtist = UString(stringBuffer);
-                            databaseBeatmap->m_sArtist = databaseBeatmap->m_sArtist.trim();
+                            trim(&databaseBeatmap->m_sArtist);
                         }
 
                         memset(stringBuffer, '\0', 1024);
                         if(sscanf(curLineChar, " Creator :%1023[^\n]", stringBuffer) == 1) {
                             databaseBeatmap->m_sCreator = UString(stringBuffer);
-                            databaseBeatmap->m_sCreator = databaseBeatmap->m_sCreator.trim();
+                            trim(&databaseBeatmap->m_sCreator);
                         }
 
                         memset(stringBuffer, '\0', 1024);
                         if(sscanf(curLineChar, " Version :%1023[^\n]", stringBuffer) == 1) {
                             databaseBeatmap->m_sDifficultyName = UString(stringBuffer);
-                            databaseBeatmap->m_sDifficultyName = databaseBeatmap->m_sDifficultyName.trim();
+                            trim(&databaseBeatmap->m_sDifficultyName);
                         }
 
                         memset(stringBuffer, '\0', 1024);
@@ -1600,7 +1600,7 @@ void OsuDatabaseBeatmap::setDifficulties(std::vector<OsuDatabaseBeatmap *> &diff
         m_iMinBPM = std::numeric_limits<int>::max();
         m_iMaxBPM = 0;
         m_iMostCommonBPM = 0;
-        m_iLastModificationTime = 0;
+        last_modification_time = 0;
         for(size_t i = 0; i < m_difficulties.size(); i++) {
             if(m_difficulties[i]->getLengthMS() > m_iLengthMS) m_iLengthMS = m_difficulties[i]->getLengthMS();
             if(m_difficulties[i]->getCS() > m_fCS) m_fCS = m_difficulties[i]->getCS();
@@ -1612,8 +1612,8 @@ void OsuDatabaseBeatmap::setDifficulties(std::vector<OsuDatabaseBeatmap *> &diff
             if(m_difficulties[i]->getMaxBPM() > m_iMaxBPM) m_iMaxBPM = m_difficulties[i]->getMaxBPM();
             if(m_difficulties[i]->getMostCommonBPM() > m_iMostCommonBPM)
                 m_iMostCommonBPM = m_difficulties[i]->getMostCommonBPM();
-            if(m_difficulties[i]->getLastModificationTime() > m_iLastModificationTime)
-                m_iLastModificationTime = m_difficulties[i]->getLastModificationTime();
+            if(m_difficulties[i]->last_modification_time > last_modification_time)
+                last_modification_time = m_difficulties[i]->last_modification_time;
         }
     }
 }

+ 11 - 13
src/App/Osu/OsuDatabaseBeatmap.h

@@ -144,10 +144,10 @@ class OsuDatabaseBeatmap {
     inline int getID() const { return m_iID; }
     inline int getSetID() const { return m_iSetID; }
 
-    inline const UString &getTitle() const { return m_sTitle; }
-    inline const UString &getArtist() const { return m_sArtist; }
-    inline const UString &getCreator() const { return m_sCreator; }
-    inline const UString &getDifficultyName() const { return m_sDifficultyName; }
+    inline const std::string &getTitle() const { return m_sTitle; }
+    inline const std::string &getArtist() const { return m_sArtist; }
+    inline const std::string &getCreator() const { return m_sCreator; }
+    inline const std::string &getDifficultyName() const { return m_sDifficultyName; }
     inline const std::string &getSource() const { return m_sSource; }
     inline const std::string &getTags() const { return m_sTags; }
     inline const std::string &getBackgroundImageFileName() const { return m_sBackgroundImageFileName; }
@@ -187,7 +187,7 @@ class OsuDatabaseBeatmap {
 
     // custom data
 
-    inline long long getLastModificationTime() const { return m_iLastModificationTime; }
+    long long last_modification_time;
 
     inline long getLocalOffset() const { return m_iLocalOffset; }
     inline long getOnlineOffset() const { return m_iOnlineOffset; }
@@ -202,12 +202,12 @@ class OsuDatabaseBeatmap {
     long m_iID;       // online ID, if uploaded
     int m_iSetID;     // online set ID, if uploaded
 
-    UString m_sTitle;
-    UString m_sArtist;
-    UString m_sCreator;
-    UString m_sDifficultyName;  // difficulty name ("Version")
-    std::string m_sSource;      // only used by search
-    std::string m_sTags;        // only used by search
+    std::string m_sTitle;
+    std::string m_sArtist;
+    std::string m_sCreator;
+    std::string m_sDifficultyName;  // difficulty name ("Version")
+    std::string m_sSource;          // only used by search
+    std::string m_sTags;            // only used by search
     std::string m_sBackgroundImageFileName;
     std::string m_sAudioFileName;
 
@@ -245,8 +245,6 @@ class OsuDatabaseBeatmap {
 
     // custom data (not necessary, not part of the beatmap file, and not precomputed)
 
-    long long m_iLastModificationTime;  // only used for sorting
-
     long m_iLocalOffset;
     long m_iOnlineOffset;
 

+ 0 - 31
src/App/Osu/OsuHUD.cpp

@@ -919,40 +919,9 @@ void OsuHUD::drawPlayfieldBorder(Graphics *g, Vector2 playfieldCenter, Vector2 p
         }
     }
     g->popTransform();
-
-    /*
-    //g->setColor(0x44ffffff);
-    // top
-    g->fillRect(playfieldBorderTopLeft.x, playfieldBorderTopLeft.y, playfieldBorderSize.x + borderSize*2, borderSize);
-
-    // left
-    g->fillRect(playfieldBorderTopLeft.x, playfieldBorderTopLeft.y + borderSize, borderSize, playfieldBorderSize.y);
-
-    // right
-    g->fillRect(playfieldBorderTopLeft.x + playfieldBorderSize.x + borderSize, playfieldBorderTopLeft.y + borderSize,
-    borderSize, playfieldBorderSize.y);
-
-    // bottom
-    g->fillRect(playfieldBorderTopLeft.x, playfieldBorderTopLeft.y+playfieldBorderSize.y + borderSize,
-    playfieldBorderSize.x + borderSize*2, borderSize);
-    */
 }
 
 void OsuHUD::drawLoadingSmall(Graphics *g) {
-    /*
-    McFont *font = engine->getResourceManager()->getFont("FONT_DEFAULT");
-    UString loadingText = "Loading ...";
-    float stringWidth = font->getStringWidth(loadingText);
-
-    g->setColor(0xffffffff);
-    g->pushTransform();
-    g->translate(-stringWidth/2, font->getHeight()/2);
-    g->rotate(engine->getTime()*180, 0, 0, 1);
-    g->translate(0, 0);
-    g->translate(m_osu->getScreenWidth()/2, m_osu->getScreenHeight()/2);
-    g->drawString(font, loadingText);
-    g->popTransform();
-    */
     const float scale = Osu::getImageScale(m_osu, m_osu->getSkin()->getLoadingSpinner(), 29);
 
     g->setColor(0xffffffff);

+ 5 - 3
src/App/Osu/OsuRankingScreen.cpp

@@ -504,8 +504,8 @@ void OsuRankingScreen::setScore(Score score, UString dateTime) {
     m_bIsImportedLegacyScore = score.isImportedLegacyScore;
     m_bIsUnranked = false;
 
-    m_songInfo->setDate(dateTime);
-    m_songInfo->setPlayer(UString(score.playerName.c_str()));
+    m_songInfo->setDate(dateTime.toUtf8());
+    m_songInfo->setPlayer(score.playerName);
 
     m_rankingPanel->setScore(score);
     setGrade(OsuScore::calculateGrade(score.num300s, score.num100s, score.num50s, score.numMisses,
@@ -572,7 +572,9 @@ void OsuRankingScreen::setScore(Score score, UString dateTime) {
 
 void OsuRankingScreen::setBeatmapInfo(OsuBeatmap *beatmap, OsuDatabaseBeatmap *diff2) {
     m_songInfo->setFromBeatmap(beatmap, diff2);
-    m_songInfo->setPlayer(m_bIsUnranked ? "neosu" : convar->getConVarByName("name")->getString());
+
+    UString local_name = convar->getConVarByName("name")->getString();
+    m_songInfo->setPlayer(m_bIsUnranked ? "neosu" : local_name.toUtf8());
 
     // round all here to 2 decimal places
     m_fSpeedMultiplier = std::round(m_osu->getSpeedMultiplier() * 100.0f) / 100.0f;

+ 3 - 3
src/App/Osu/OsuRichPresence.cpp

@@ -119,11 +119,11 @@ void OsuRichPresence::onSongBrowser(Osu *osu) {
 
 void OsuRichPresence::onPlayStart(Osu *osu) {
     UString playingInfo /*= "Playing "*/;
-    playingInfo.append(osu->getSelectedBeatmap()->getSelectedDifficulty2()->getArtist());
+    playingInfo.append(osu->getSelectedBeatmap()->getSelectedDifficulty2()->getArtist().c_str());
     playingInfo.append(" - ");
-    playingInfo.append(osu->getSelectedBeatmap()->getSelectedDifficulty2()->getTitle());
+    playingInfo.append(osu->getSelectedBeatmap()->getSelectedDifficulty2()->getTitle().c_str());
     playingInfo.append(" [");
-    playingInfo.append(osu->getSelectedBeatmap()->getSelectedDifficulty2()->getDifficultyName());
+    playingInfo.append(osu->getSelectedBeatmap()->getSelectedDifficulty2()->getDifficultyName().c_str());
     playingInfo.append("]");
 
     setStatus(osu, playingInfo);

+ 50 - 77
src/App/Osu/OsuSongBrowser.cpp

@@ -4,6 +4,8 @@
 #include <sys/random.h>
 #endif
 
+#include <string.h>
+
 #include "AnimationHandler.h"
 #include "Bancho.h"
 #include "BanchoLeaderboard.h"
@@ -255,11 +257,9 @@ class OsuUISongBrowserNoRecordsSetElement : public CBaseUILabel {
 bool OsuSongBrowser::SortByArtist::operator()(OsuUISongBrowserButton const *a, OsuUISongBrowserButton const *b) const {
     if(a->getDatabaseBeatmap() == NULL || b->getDatabaseBeatmap() == NULL) return a->getSortHack() < b->getSortHack();
 
-    // strict weak ordering!
-    if(a->getDatabaseBeatmap()->getArtist().equalsIgnoreCase(b->getDatabaseBeatmap()->getArtist()))
-        return a->getSortHack() < b->getSortHack();
-
-    return a->getDatabaseBeatmap()->getArtist().lessThanIgnoreCase(b->getDatabaseBeatmap()->getArtist());
+    int res = strcasecmp(a->getDatabaseBeatmap()->getArtist().c_str(), b->getDatabaseBeatmap()->getArtist().c_str());
+    if(res == 0) return a->getSortHack() < b->getSortHack();
+    return res > 0;
 }
 
 bool OsuSongBrowser::SortByBPM::operator()(OsuUISongBrowserButton const *a, OsuUISongBrowserButton const *b) const {
@@ -277,41 +277,25 @@ bool OsuSongBrowser::SortByBPM::operator()(OsuUISongBrowserButton const *a, OsuU
         if(bDiffs[i]->getMostCommonBPM() > bpm2) bpm2 = bDiffs[i]->getMostCommonBPM();
     }
 
-    // strict weak ordering!
     if(bpm1 == bpm2) return a->getSortHack() < b->getSortHack();
-
     return bpm1 < bpm2;
 }
 
 bool OsuSongBrowser::SortByCreator::operator()(OsuUISongBrowserButton const *a, OsuUISongBrowserButton const *b) const {
     if(a->getDatabaseBeatmap() == NULL || b->getDatabaseBeatmap() == NULL) return a->getSortHack() < b->getSortHack();
 
-    // strict weak ordering!
-    if(a->getDatabaseBeatmap()->getCreator().equalsIgnoreCase(b->getDatabaseBeatmap()->getCreator()))
-        return a->getSortHack() < b->getSortHack();
-
-    return a->getDatabaseBeatmap()->getCreator().lessThanIgnoreCase(b->getDatabaseBeatmap()->getCreator());
+    int res = strcasecmp(a->getDatabaseBeatmap()->getCreator().c_str(), b->getDatabaseBeatmap()->getCreator().c_str());
+    if(res == 0) return a->getSortHack() < b->getSortHack();
+    return res > 0;
 }
 
 bool OsuSongBrowser::SortByDateAdded::operator()(OsuUISongBrowserButton const *a,
                                                  OsuUISongBrowserButton const *b) const {
     if(a->getDatabaseBeatmap() == NULL || b->getDatabaseBeatmap() == NULL) return a->getSortHack() < b->getSortHack();
 
-    long long time1 = a->getDatabaseBeatmap()->getLastModificationTime();
-    const std::vector<OsuDatabaseBeatmap *> &aDiffs = a->getDatabaseBeatmap()->getDifficulties();
-    for(size_t i = 0; i < aDiffs.size(); i++) {
-        if(aDiffs[i]->getLastModificationTime() > time1) time1 = aDiffs[i]->getLastModificationTime();
-    }
-
-    long long time2 = b->getDatabaseBeatmap()->getLastModificationTime();
-    const std::vector<OsuDatabaseBeatmap *> &bDiffs = b->getDatabaseBeatmap()->getDifficulties();
-    for(size_t i = 0; i < bDiffs.size(); i++) {
-        if(bDiffs[i]->getLastModificationTime() > time2) time2 = bDiffs[i]->getLastModificationTime();
-    }
-
-    // strict weak ordering!
+    long long time1 = a->getDatabaseBeatmap()->last_modification_time;
+    long long time2 = b->getDatabaseBeatmap()->last_modification_time;
     if(time1 == time2) return a->getSortHack() > b->getSortHack();
-
     return time1 > time2;
 }
 
@@ -384,11 +368,9 @@ bool OsuSongBrowser::SortByLength::operator()(OsuUISongBrowserButton const *a, O
 bool OsuSongBrowser::SortByTitle::operator()(OsuUISongBrowserButton const *a, OsuUISongBrowserButton const *b) const {
     if(a->getDatabaseBeatmap() == NULL || b->getDatabaseBeatmap() == NULL) return a->getSortHack() < b->getSortHack();
 
-    // strict weak ordering!
-    if(a->getDatabaseBeatmap()->getTitle().equalsIgnoreCase(b->getDatabaseBeatmap()->getTitle()))
-        return a->getSortHack() < b->getSortHack();
-
-    return a->getDatabaseBeatmap()->getTitle().lessThanIgnoreCase(b->getDatabaseBeatmap()->getTitle());
+    int res = strcasecmp(a->getDatabaseBeatmap()->getTitle().c_str(), b->getDatabaseBeatmap()->getTitle().c_str());
+    if(res == 0) return a->getSortHack() < b->getSortHack();
+    return res > 0;
 }
 
 OsuSongBrowser::OsuSongBrowser(Osu *osu) : OsuScreenBackable(osu) {
@@ -1635,8 +1617,8 @@ void OsuSongBrowser::onDifficultySelected(OsuDatabaseBeatmap *diff2, bool play)
     // start playing
     if(play) {
         if(bancho.is_in_a_multi_room()) {
-            bancho.room.map_name = UString::format("%s - %s [%s]", diff2->getArtist().toUtf8(),
-                                                   diff2->getTitle().toUtf8(), diff2->getDifficultyName().toUtf8());
+            bancho.room.map_name = UString::format("%s - %s [%s]", diff2->getArtist().c_str(),
+                                                   diff2->getTitle().c_str(), diff2->getDifficultyName().c_str());
             bancho.room.map_md5 = diff2->getMD5Hash();
             bancho.room.map_id = diff2->getID();
 
@@ -1786,9 +1768,9 @@ void OsuSongBrowser::addBeatmap(OsuDatabaseBeatmap *beatmap) {
     {
         // artist
         if(m_artistCollectionButtons.size() == 28) {
-            const UString &artist = beatmap->getArtist();
+            const std::string &artist = beatmap->getArtist();
             if(artist.length() > 0) {
-                const char firstChar = artist.toUtf8()[0];
+                const char firstChar = artist[0];
 
                 const bool isNumber = (firstChar >= '0' && firstChar <= '9');
                 const bool isLowerCase = (firstChar >= 'a' && firstChar <= 'z');
@@ -1821,9 +1803,9 @@ void OsuSongBrowser::addBeatmap(OsuDatabaseBeatmap *beatmap) {
 
         // creator
         if(m_creatorCollectionButtons.size() == 28) {
-            const UString &creator = beatmap->getCreator();
+            const std::string &creator = beatmap->getCreator();
             if(creator.length() > 0) {
-                const char firstChar = creator.toUtf8()[0];
+                const char firstChar = creator[0];
 
                 const bool isNumber = (firstChar >= '0' && firstChar <= '9');
                 const bool isLowerCase = (firstChar >= 'a' && firstChar <= 'z');
@@ -1867,9 +1849,9 @@ void OsuSongBrowser::addBeatmap(OsuDatabaseBeatmap *beatmap) {
 
         // title
         if(m_titleCollectionButtons.size() == 28) {
-            const UString &creator = beatmap->getTitle();
+            const std::string &creator = beatmap->getTitle();
             if(creator.length() > 0) {
-                const char firstChar = creator.toUtf8()[0];
+                const char firstChar = creator[0];
 
                 const bool isNumber = (firstChar >= '0' && firstChar <= '9');
                 const bool isLowerCase = (firstChar >= 'a' && firstChar <= 'z');
@@ -2404,37 +2386,37 @@ bool OsuSongBrowser::searchMatcher(const OsuDatabaseBeatmap *databaseBeatmap,
 
 bool OsuSongBrowser::findSubstringInDifficulty(const OsuDatabaseBeatmap *diff, const UString &searchString) {
     if(diff->getTitle().length() > 0) {
-        if(diff->getTitle().findIgnoreCase(searchString) != -1) return true;
+        if(strcasestr(diff->getTitle().c_str(), searchString.toUtf8()) != NULL) return true;
     }
 
     if(diff->getArtist().length() > 0) {
-        if(diff->getArtist().findIgnoreCase(searchString) != -1) return true;
+        if(strcasestr(diff->getArtist().c_str(), searchString.toUtf8()) != NULL) return true;
     }
 
     if(diff->getCreator().length() > 0) {
-        if(diff->getCreator().findIgnoreCase(searchString) != -1) return true;
+        if(strcasestr(diff->getCreator().c_str(), searchString.toUtf8()) != NULL) return true;
     }
 
     if(diff->getDifficultyName().length() > 0) {
-        if(diff->getDifficultyName().findIgnoreCase(searchString) != -1) return true;
+        if(strcasestr(diff->getDifficultyName().c_str(), searchString.toUtf8()) != NULL) return true;
     }
 
     if(diff->getSource().length() > 0) {
-        auto source = UString(diff->getSource().c_str());
-        if(source.findIgnoreCase(searchString) != -1) return true;
+        if(strcasestr(diff->getSource().c_str(), searchString.toUtf8()) != NULL) return true;
     }
 
     if(diff->getTags().length() > 0) {
-        auto tags = UString(diff->getTags().c_str());
-        if(tags.findIgnoreCase(searchString) != -1) return true;
+        if(strcasestr(diff->getTags().c_str(), searchString.toUtf8()) != NULL) return true;
     }
 
     if(diff->getID() > 0) {
-        if(UString::format("%i", diff->getID()).findIgnoreCase(searchString) != -1) return true;
+        auto id = std::to_string(diff->getID());
+        if(strcasestr(id.c_str(), searchString.toUtf8()) != NULL) return true;
     }
 
     if(diff->getSetID() > 0) {
-        if(UString::format("%i", diff->getSetID()).findIgnoreCase(searchString) != -1) return true;
+        auto set_id = std::to_string(diff->getSetID());
+        if(strcasestr(set_id.c_str(), searchString.toUtf8()) != NULL) return true;
     }
 
     return false;
@@ -3485,18 +3467,12 @@ void OsuSongBrowser::onSortChangeInt(UString text, bool autoScroll) {
 
     // resort Collection buttons (one button for each collection)
     // these are always sorted alphabetically by name
-    {
-        struct COLLECTION_NAME_SORTING_COMPARATOR {
-            bool operator()(OsuUISongBrowserCollectionButton const *a, OsuUISongBrowserCollectionButton const *b) {
-                // strict weak ordering!
-                if(a->getCollectionName() == b->getCollectionName()) return a->getSortHack() < b->getSortHack();
-
-                return a->getCollectionName().lessThanIgnoreCase(b->getCollectionName());
-            }
-        };
-
-        std::sort(m_collectionButtons.begin(), m_collectionButtons.end(), COLLECTION_NAME_SORTING_COMPARATOR());
-    }
+    std::sort(m_collectionButtons.begin(), m_collectionButtons.end(),
+              [](OsuUISongBrowserCollectionButton *a, OsuUISongBrowserCollectionButton *b) {
+                  int res = strcasecmp(a->getCollectionName().c_str(), b->getCollectionName().c_str());
+                  if(res == 0) return a->getSortHack() < b->getSortHack();
+                  return res < 0;
+              });
 
     // resort Collection button array (each group of songbuttons inside each Collection)
     for(size_t i = 0; i < m_collectionButtons.size(); i++) {
@@ -3879,7 +3855,7 @@ void OsuSongBrowser::onSongButtonContextMenu(OsuUISongBrowserSongButton *songBut
             // remove diff from collection
 
             // get collection name by selection
-            UString collectionName;
+            std::string collectionName;
             {
                 for(size_t i = 0; i < m_collectionButtons.size(); i++) {
                     if(m_collectionButtons[i]->isSelected()) {
@@ -3889,8 +3865,7 @@ void OsuSongBrowser::onSongButtonContextMenu(OsuUISongBrowserSongButton *songBut
                 }
             }
 
-            std::string name = collectionName.toUtf8();
-            auto collection = get_or_create_collection(name);
+            auto collection = get_or_create_collection(collectionName);
             collection->remove_map(songButton->getDatabaseBeatmap()->getMD5Hash());
             save_collections();
             updateUIScheduled = true;
@@ -3898,7 +3873,7 @@ void OsuSongBrowser::onSongButtonContextMenu(OsuUISongBrowserSongButton *songBut
             // remove entire set from collection
 
             // get collection name by selection
-            UString collectionName;
+            std::string collectionName;
             {
                 for(size_t i = 0; i < m_collectionButtons.size(); i++) {
                     if(m_collectionButtons[i]->isSelected()) {
@@ -3908,8 +3883,7 @@ void OsuSongBrowser::onSongButtonContextMenu(OsuUISongBrowserSongButton *songBut
                 }
             }
 
-            std::string name = collectionName.toUtf8();
-            auto collection = get_or_create_collection(name);
+            auto collection = get_or_create_collection(collectionName);
             const std::vector<MD5Hash> beatmapSetHashes =
                 CollectionManagementHelper::getBeatmapSetHashesForSongButton(songButton, m_db);
             for(auto hash : beatmapSetHashes) {
@@ -3942,7 +3916,7 @@ void OsuSongBrowser::onSongButtonContextMenu(OsuUISongBrowserSongButton *songBut
 
     if(updateUIScheduled) {
         const float prevScrollPosY = m_songBrowser->getScrollPosY();  // usability
-        const UString previouslySelectedCollectionName =
+        const auto previouslySelectedCollectionName =
             (m_selectionPreviousCollectionButton != NULL ? m_selectionPreviousCollectionButton->getCollectionName()
                                                          : "");  // usability
         {
@@ -3966,10 +3940,11 @@ void OsuSongBrowser::onSongButtonContextMenu(OsuUISongBrowserSongButton *songBut
 
 void OsuSongBrowser::onCollectionButtonContextMenu(OsuUISongBrowserCollectionButton *collectionButton, UString text,
                                                    int id) {
-    if(id == 2)  // delete collection
-    {
+    std::string collection_name = text.toUtf8();
+
+    if(id == 2) {  // delete collection
         for(size_t i = 0; i < m_collectionButtons.size(); i++) {
-            if(m_collectionButtons[i]->getCollectionName() == text) {
+            if(m_collectionButtons[i]->getCollectionName() == collection_name) {
                 // delete UI
                 delete m_collectionButtons[i];
                 m_collectionButtons.erase(m_collectionButtons.begin() + i);
@@ -3977,21 +3952,19 @@ void OsuSongBrowser::onCollectionButtonContextMenu(OsuUISongBrowserCollectionBut
                 // reset UI state
                 m_selectionPreviousCollectionButton = NULL;
 
-                std::string name = text.toUtf8();
-                auto collection = get_or_create_collection(name);
+                auto collection = get_or_create_collection(collection_name);
                 collection->delete_collection();
                 save_collections();
 
                 // update UI
-                { onGroupCollections(false); }
+                onGroupCollections(false);
 
                 break;
             }
         }
-    } else if(id == 3)  // collection has been renamed
-    {
+    } else if(id == 3) {  // collection has been renamed
         // update UI
-        { onSortChangeInt(osu_songbrowser_sortingtype.getString(), false); }
+        onSortChangeInt(osu_songbrowser_sortingtype.getString(), false);
     }
 }
 

+ 11 - 36
src/App/Osu/OsuUIRankingScreenInfoLabel.cpp

@@ -35,18 +35,15 @@ OsuUIRankingScreenInfoLabel::OsuUIRankingScreenInfoLabel(Osu *osu, float xPos, f
 }
 
 void OsuUIRankingScreenInfoLabel::draw(Graphics *g) {
-    // debug bounding box
-    /*
-    g->setColor(0xffffffff);
-    g->drawLine(m_vPos.x, m_vPos.y, m_vPos.x+m_vSize.x, m_vPos.y);
-    g->drawLine(m_vPos.x, m_vPos.y, m_vPos.x, m_vPos.y+m_vSize.y);
-    g->drawLine(m_vPos.x, m_vPos.y+m_vSize.y, m_vPos.x+m_vSize.x, m_vPos.y+m_vSize.y);
-    g->drawLine(m_vPos.x+m_vSize.x, m_vPos.y, m_vPos.x+m_vSize.x, m_vPos.y+m_vSize.y);
-    */
-
     // build strings
-    const UString titleText = buildTitleString();
-    const UString subTitleText = buildSubTitleString();
+    UString titleText = m_sArtist.c_str();
+    titleText.append(" - ");
+    titleText.append(m_sTitle.c_str());
+    titleText.append(" [");
+    titleText.append(m_sDiff.c_str());
+    titleText.append("]");
+    UString subTitleText = "Beatmap by ";
+    subTitleText.append(m_sMapper.c_str());
     const UString playerText = buildPlayerString();
 
     const float globalScale = std::max((m_vSize.y / getMinimumHeight()) * 0.741f, 1.0f);
@@ -106,41 +103,19 @@ void OsuUIRankingScreenInfoLabel::setFromBeatmap(OsuBeatmap *beatmap, OsuDatabas
 
     std::time_t now_c = std::chrono::system_clock::to_time_t(std::chrono::system_clock::now());
     m_sDate = std::ctime(&now_c);
-    m_sDate = m_sDate.trim();
-}
-
-UString OsuUIRankingScreenInfoLabel::buildTitleString() {
-    UString titleString = m_sArtist;
-    titleString.append(" - ");
-    titleString.append(m_sTitle);
-    titleString.append(" [");
-    titleString.append(m_sDiff);
-    titleString.append("]");
-
-    return titleString;
-}
-
-UString OsuUIRankingScreenInfoLabel::buildSubTitleString() {
-    UString subTitleString = "Beatmap by ";
-    subTitleString.append(m_sMapper);
-
-    return subTitleString;
+    trim(&m_sDate);
 }
 
 UString OsuUIRankingScreenInfoLabel::buildPlayerString() {
     UString playerString = "Played by ";
-    playerString.append(m_sPlayer);
+    playerString.append(m_sPlayer.c_str());
     playerString.append(" on ");
-    playerString.append(m_sDate);
+    playerString.append(m_sDate.c_str());
 
     return playerString;
 }
 
 float OsuUIRankingScreenInfoLabel::getMinimumWidth() {
-    /*
-    float titleWidth = m_font->getStringWidth(buildTitleString());
-    float subTitleWidth = m_font->getStringWidth(buildSubTitleString()) * m_fSubTitleScale;
-    */
     float titleWidth = 0;
     float subTitleWidth = 0;
     float playerWidth = m_font->getStringWidth(buildPlayerString()) * m_fSubTitleScale;

+ 12 - 14
src/App/Osu/OsuUIRankingScreenInfoLabel.h

@@ -24,19 +24,17 @@ class OsuUIRankingScreenInfoLabel : public CBaseUIElement {
 
     void setFromBeatmap(OsuBeatmap *beatmap, OsuDatabaseBeatmap *diff2);
 
-    void setArtist(UString artist) { m_sArtist = artist; }
-    void setTitle(UString title) { m_sTitle = title; }
-    void setDiff(UString diff) { m_sDiff = diff; }
-    void setMapper(UString mapper) { m_sMapper = mapper; }
-    void setPlayer(UString player) { m_sPlayer = player; }
-    void setDate(UString date) { m_sDate = date; }
+    void setArtist(std::string artist) { m_sArtist = artist; }
+    void setTitle(std::string title) { m_sTitle = title; }
+    void setDiff(std::string diff) { m_sDiff = diff; }
+    void setMapper(std::string mapper) { m_sMapper = mapper; }
+    void setPlayer(std::string player) { m_sPlayer = player; }
+    void setDate(std::string date) { m_sDate = date; }
 
     float getMinimumWidth();
     float getMinimumHeight();
 
    private:
-    UString buildTitleString();
-    UString buildSubTitleString();
     UString buildPlayerString();
 
     Osu *m_osu;
@@ -45,12 +43,12 @@ class OsuUIRankingScreenInfoLabel : public CBaseUIElement {
     int m_iMargin;
     float m_fSubTitleScale;
 
-    UString m_sArtist;
-    UString m_sTitle;
-    UString m_sDiff;
-    UString m_sMapper;
-    UString m_sPlayer;
-    UString m_sDate;
+    std::string m_sArtist;
+    std::string m_sTitle;
+    std::string m_sDiff;
+    std::string m_sMapper;
+    std::string m_sPlayer;
+    std::string m_sDate;
 };
 
 #endif

+ 0 - 2
src/App/Osu/OsuUISongBrowserButton.cpp

@@ -316,8 +316,6 @@ void OsuUISongBrowserButton::setTargetRelPosY(float targetRelPosY) {
 
 Vector2 OsuUISongBrowserButton::getActualOffset() const {
     const float hd2xMultiplier = m_osu->getSkin()->isMenuButtonBackground2x() ? 2.0f : 1.0f;
-    // const float correctedMarginPixelsX = (2*marginPixelsX +
-    // m_beatmap->getOsu()->getSkin()->getMenuButtonBackground()->getWidth()/hd2xMultiplier - 699)/2.0f;
     const float correctedMarginPixelsY =
         (2 * marginPixelsY + m_osu->getSkin()->getMenuButtonBackground()->getHeight() / hd2xMultiplier - 103.0f) / 2.0f;
     return Vector2((int)(marginPixelsX * m_fScale * hd2xMultiplier),

+ 19 - 28
src/App/Osu/OsuUISongBrowserCollectionButton.cpp

@@ -65,7 +65,21 @@ void OsuUISongBrowserCollectionButton::draw(Graphics *g) {
     const Vector2 size = getActualSize();
 
     // draw title
-    UString titleString = buildTitleString();
+    UString titleString = m_sCollectionName.c_str();
+    int numChildren = 0;
+    {
+        for(size_t c = 0; c < m_children.size(); c++) {
+            const std::vector<OsuUISongBrowserButton *> &childrenChildren = m_children[c]->getChildren();
+            if(childrenChildren.size() > 0) {
+                for(size_t cc = 0; cc < childrenChildren.size(); cc++) {
+                    if(childrenChildren[cc]->isSearchMatch()) numChildren++;
+                }
+            } else if(m_children[c]->isSearchMatch())
+                numChildren++;
+        }
+    }
+    titleString.append(UString::format((numChildren == 1 ? " (%i map)" : " (%i maps)"), numChildren));
+
     int textXOffset = size.x * 0.02f;
     float titleScale = (size.y * m_fTitleScale) / m_font->getHeight();
     g->setColor(m_bSelected ? skin->getSongSelectActiveText() : skin->getSongSelectInactiveText());
@@ -128,7 +142,7 @@ void OsuUISongBrowserCollectionButton::onContextMenu(UString text, int id) {
             spacer->setTextColor(0xff888888);
             spacer->setTextDarkColor(0xff000000);
 
-            m_contextMenu->addTextbox(m_sCollectionName, id)->setCursorPosRight();
+            m_contextMenu->addTextbox(m_sCollectionName.c_str(), id)->setCursorPosRight();
 
             spacer = m_contextMenu->addButton("---");
             spacer->setTextLeft(false);
@@ -172,14 +186,13 @@ void OsuUISongBrowserCollectionButton::onContextMenu(UString text, int id) {
 
 void OsuUISongBrowserCollectionButton::onRenameCollectionConfirmed(UString text, int id) {
     if(text.length() > 0) {
-        std::string old_name = m_sCollectionName.toUtf8();
         std::string new_name = text.toUtf8();
-        auto collection = get_or_create_collection(old_name);
+        auto collection = get_or_create_collection(m_sCollectionName);
         collection->rename_to(new_name);
         save_collections();
 
         // (trigger re-sorting of collection buttons)
-        m_osu->getSongBrowser()->onCollectionButtonContextMenu(this, m_sCollectionName, 3);
+        m_osu->getSongBrowser()->onCollectionButtonContextMenu(this, m_sCollectionName.c_str(), 3);
     }
 }
 
@@ -187,29 +200,7 @@ void OsuUISongBrowserCollectionButton::onDeleteCollectionConfirmed(UString text,
     if(id != 2) return;
 
     // just forward it
-    m_osu->getSongBrowser()->onCollectionButtonContextMenu(this, m_sCollectionName, id);
-}
-
-UString OsuUISongBrowserCollectionButton::buildTitleString() {
-    UString titleString = m_sCollectionName;
-
-    // count children (also with support for search results in collections)
-    int numChildren = 0;
-    {
-        for(size_t c = 0; c < m_children.size(); c++) {
-            const std::vector<OsuUISongBrowserButton *> &childrenChildren = m_children[c]->getChildren();
-            if(childrenChildren.size() > 0) {
-                for(size_t cc = 0; cc < childrenChildren.size(); cc++) {
-                    if(childrenChildren[cc]->isSearchMatch()) numChildren++;
-                }
-            } else if(m_children[c]->isSearchMatch())
-                numChildren++;
-        }
-    }
-
-    titleString.append(UString::format((numChildren == 1 ? " (%i map)" : " (%i maps)"), numChildren));
-
-    return titleString;
+    m_osu->getSongBrowser()->onCollectionButtonContextMenu(this, m_sCollectionName.c_str(), id);
 }
 
 Color OsuUISongBrowserCollectionButton::getActiveBackgroundColor() const {

+ 2 - 4
src/App/Osu/OsuUISongBrowserCollectionButton.h

@@ -24,7 +24,7 @@ class OsuUISongBrowserCollectionButton : public OsuUISongBrowserButton {
     virtual Color getActiveBackgroundColor() const;
     virtual Color getInactiveBackgroundColor() const;
 
-    const UString &getCollectionName() const { return m_sCollectionName; }
+    const std::string &getCollectionName() const { return m_sCollectionName; }
 
    private:
     virtual void onSelected(bool wasSelected, bool autoSelectBottomMostChild, bool wasParentSelected);
@@ -34,9 +34,7 @@ class OsuUISongBrowserCollectionButton : public OsuUISongBrowserButton {
     void onRenameCollectionConfirmed(UString text, int id = -1);
     void onDeleteCollectionConfirmed(UString text, int id = -1);
 
-    UString buildTitleString();
-
-    UString m_sCollectionName;
+    std::string m_sCollectionName;
 
     float m_fTitleScale;
 };

+ 8 - 24
src/App/Osu/OsuUISongBrowserInfoLabel.cpp

@@ -74,8 +74,14 @@ void OsuUISongBrowserInfoLabel::draw(Graphics *g) {
     }
 
     // build strings
-    const UString titleText = buildTitleString();
-    const UString subTitleText = buildSubTitleString();
+    UString titleText = m_sArtist.c_str();
+    titleText.append(" - ");
+    titleText.append(m_sTitle.c_str());
+    titleText.append(" [");
+    titleText.append(m_sDiff.c_str());
+    titleText.append("]");
+    UString subTitleText = "Mapped by ";
+    subTitleText.append(m_sMapper.c_str());
     const UString songInfoText = buildSongInfoString();
     const UString diffInfoText = buildDiffInfoString();
     const UString offsetInfoText = buildOffsetInfoString();
@@ -311,24 +317,6 @@ void OsuUISongBrowserInfoLabel::setFromMissingBeatmap(long beatmapId) {
     setOnlineOffset(0);
 }
 
-UString OsuUISongBrowserInfoLabel::buildTitleString() {
-    UString titleString = m_sArtist;
-    titleString.append(" - ");
-    titleString.append(m_sTitle);
-    titleString.append(" [");
-    titleString.append(m_sDiff);
-    titleString.append("]");
-
-    return titleString;
-}
-
-UString OsuUISongBrowserInfoLabel::buildSubTitleString() {
-    UString subTitleString = "Mapped by ";
-    subTitleString.append(m_sMapper);
-
-    return subTitleString;
-}
-
 UString OsuUISongBrowserInfoLabel::buildSongInfoString() {
     unsigned long lengthMS = m_iLengthMS;
 
@@ -401,10 +389,6 @@ UString OsuUISongBrowserInfoLabel::buildOffsetInfoString() {
 }
 
 float OsuUISongBrowserInfoLabel::getMinimumWidth() {
-    /*
-    float titleWidth = m_font->getStringWidth(buildTitleString());
-    float subTitleWidth = m_font->getStringWidth(buildSubTitleString()) * m_fSubTitleScale;
-    */
     float titleWidth = 0;
     float subTitleWidth = 0;
     float songInfoWidth = m_font->getStringWidth(buildSongInfoString()) * m_fSongInfoScale;

+ 8 - 10
src/App/Osu/OsuUISongBrowserInfoLabel.h

@@ -26,10 +26,10 @@ class OsuUISongBrowserInfoLabel : public CBaseUIButton {
     void setFromBeatmap(OsuBeatmap *beatmap, OsuDatabaseBeatmap *diff2);
     void setFromMissingBeatmap(long beatmapId);
 
-    void setArtist(UString artist) { m_sArtist = artist; }
-    void setTitle(UString title) { m_sTitle = title; }
-    void setDiff(UString diff) { m_sDiff = diff; }
-    void setMapper(UString mapper) { m_sMapper = mapper; }
+    void setArtist(std::string artist) { m_sArtist = artist; }
+    void setTitle(std::string title) { m_sTitle = title; }
+    void setDiff(std::string diff) { m_sDiff = diff; }
+    void setMapper(std::string mapper) { m_sMapper = mapper; }
 
     void setLengthMS(unsigned long lengthMS) { m_iLengthMS = lengthMS; }
     void setBPM(int minBPM, int maxBPM, int mostCommonBPM) {
@@ -54,8 +54,6 @@ class OsuUISongBrowserInfoLabel : public CBaseUIButton {
     long getBeatmapID() const { return m_iBeatmapId; }
 
    private:
-    UString buildTitleString();
-    UString buildSubTitleString();
     UString buildSongInfoString();
     UString buildDiffInfoString();
     UString buildOffsetInfoString();
@@ -73,10 +71,10 @@ class OsuUISongBrowserInfoLabel : public CBaseUIButton {
     float m_fDiffInfoScale;
     float m_fOffsetInfoScale;
 
-    UString m_sArtist;
-    UString m_sTitle;
-    UString m_sDiff;
-    UString m_sMapper;
+    std::string m_sArtist;
+    std::string m_sTitle;
+    std::string m_sDiff;
+    std::string m_sMapper;
 
     unsigned long m_iLengthMS;
     int m_iMinBPM;

+ 30 - 69
src/App/Osu/OsuUISongBrowserSongButton.cpp

@@ -36,7 +36,6 @@ OsuUISongBrowserSongButton::OsuUISongBrowserSongButton(Osu *osu, OsuSongBrowser
                                                        OsuDatabaseBeatmap *databaseBeatmap)
     : OsuUISongBrowserButton(osu, songBrowser, view, contextMenu, xPos, yPos, xSize, ySize, name) {
     m_databaseBeatmap = databaseBeatmap;
-    m_representativeDatabaseBeatmap = NULL;
 
     m_grade = Score::Grade::D;
     m_bHasGrade = false;
@@ -67,7 +66,6 @@ OsuUISongBrowserSongButton::OsuUISongBrowserSongButton(Osu *osu, OsuSongBrowser
         }
     }
 
-    updateRepresentativeDatabaseBeatmap();
     updateLayoutEx();
 }
 
@@ -81,23 +79,33 @@ void OsuUISongBrowserSongButton::draw(Graphics *g) {
     OsuUISongBrowserButton::draw(g);
     if(!m_bVisible) return;
 
+    if(m_vPos.y + m_vSize.y < 0) return;
+    if(m_vPos.y > engine->getScreenHeight()) return;
+
     // draw background image
-    if(m_representativeDatabaseBeatmap != NULL)
-        drawBeatmapBackgroundThumbnail(
-            g, m_osu->getBackgroundImageHandler()->getLoadBackgroundImage(m_representativeDatabaseBeatmap));
+    sortChildren();
+    if(m_databaseBeatmap != NULL && m_children.size() > 0) {
+        // use the bottom child (hardest diff, assuming default sorting, and respecting the current search matches)
+        for(int i = m_children.size() - 1; i >= 0; i--) {
+            // NOTE: if no search is active, then all search matches return true by default
+            if(m_children[i]->isSearchMatch()) {
+                auto representative_beatmap =
+                    dynamic_cast<OsuUISongBrowserSongButton *>(m_children[i])->getDatabaseBeatmap();
 
-    drawTitle(g);
-    drawSubTitle(g);
-}
+                m_sTitle = representative_beatmap->getTitle();
+                m_sArtist = representative_beatmap->getArtist();
+                m_sMapper = representative_beatmap->getCreator();
 
-void OsuUISongBrowserSongButton::mouse_update(bool *propagate_clicks) {
-    if(!m_bVisible) return;
-    OsuUISongBrowserButton::mouse_update(propagate_clicks);
+                drawBeatmapBackgroundThumbnail(
+                    g, m_osu->getBackgroundImageHandler()->getLoadBackgroundImage(representative_beatmap));
 
-    // HACKHACK: calling these two every frame is a bit insane, but too lazy to write delta detection logic atm. (UI
-    // desync is not a problem since parent buttons are invisible while selected, so no resorting happens in that state)
-    sortChildren();
-    updateRepresentativeDatabaseBeatmap();
+                break;
+            }
+        }
+    }
+
+    drawTitle(g);
+    drawSubTitle(g);
 }
 
 void OsuUISongBrowserSongButton::drawBeatmapBackgroundThumbnail(Graphics *g, Image *image) {
@@ -183,12 +191,10 @@ void OsuUISongBrowserSongButton::drawTitle(Graphics *g, float deselectedAlpha, b
 
     g->pushTransform();
     {
+        UString title = m_sTitle.c_str();
         g->scale(titleScale, titleScale);
         g->translate(pos.x + m_fTextOffset, pos.y + size.y * m_fTextMarginScale + m_font->getHeight() * titleScale);
-        g->drawString(m_font, buildTitleString());
-
-        // debugging
-        // g->drawString(m_font, UString::format("%i, %i", m_diff->setID, m_diff->ID));
+        g->drawString(m_font, title);
     }
     g->popTransform();
 }
@@ -206,23 +212,15 @@ void OsuUISongBrowserSongButton::drawSubTitle(Graphics *g, float deselectedAlpha
 
     g->pushTransform();
     {
+        UString subTitleString = m_sArtist.c_str();
+        subTitleString.append(" // ");
+        subTitleString.append(m_sMapper.c_str());
+
         g->scale(subTitleScale, subTitleScale);
         g->translate(pos.x + m_fTextOffset, pos.y + size.y * m_fTextMarginScale + m_font->getHeight() * titleScale +
                                                 size.y * m_fTextSpacingScale +
                                                 m_font->getHeight() * subTitleScale * 0.85f);
-        g->drawString(m_font, buildSubTitleString());
-
-        // debug stuff
-        /*
-        g->translate(-300, 0);
-        long long oldestTime = std::numeric_limits<long long>::min();
-        for (int i=0; i<m_beatmap->getNumDifficulties(); i++)
-        {
-                if ((*m_beatmap->getDifficultiesPointer())[i]->lastModificationTime > oldestTime)
-                        oldestTime = (*m_beatmap->getDifficultiesPointer())[i]->lastModificationTime;
-        }
-        g->drawString(m_font, UString::format("t = %I64d", oldestTime));
-        */
+        g->drawString(m_font, subTitleString);
     }
     g->popTransform();
 }
@@ -293,19 +291,6 @@ void OsuUISongBrowserSongButton::triggerContextMenu(Vector2 pos) {
             m_contextMenu->addButton("[+Set]   Add to Collection", 2);
 
             if(m_osu->getSongBrowser()->getGroupingMode() == OsuSongBrowser::GROUP::GROUP_COLLECTIONS) {
-                // get the collection name for this diff/set
-                UString collectionName;
-                {
-                    const std::vector<OsuUISongBrowserCollectionButton *> &collectionButtons =
-                        m_osu->getSongBrowser()->getCollectionButtons();
-                    for(size_t i = 0; i < collectionButtons.size(); i++) {
-                        if(collectionButtons[i]->isSelected()) {
-                            collectionName = collectionButtons[i]->getCollectionName();
-                            break;
-                        }
-                    }
-                }
-
                 CBaseUIButton *spacer = m_contextMenu->addButton("---");
                 spacer->setTextLeft(false);
                 spacer->setEnabled(false);
@@ -441,27 +426,3 @@ float OsuUISongBrowserSongButton::calculateGradeWidth() {
 
     return grade->getSizeBaseRaw().x * calculateGradeScale();
 }
-
-void OsuUISongBrowserSongButton::updateRepresentativeDatabaseBeatmap() {
-    if(m_databaseBeatmap != NULL && m_children.size() > 0) {
-        const OsuDatabaseBeatmap *previousRepresentativeDatabaseBeatmap = m_representativeDatabaseBeatmap;
-
-        // use the bottom child (hardest diff, assuming default sorting, and respecting the current search matches)
-        for(int i = m_children.size() - 1; i >= 0; i--) {
-            if(m_children[i]
-                   ->isSearchMatch())  // NOTE: if no search is active, then all search matches return true by default
-            {
-                m_representativeDatabaseBeatmap =
-                    dynamic_cast<OsuUISongBrowserSongButton *>(m_children[i])->getDatabaseBeatmap();
-                break;
-            }
-        }
-
-        if(m_representativeDatabaseBeatmap != NULL &&
-           m_representativeDatabaseBeatmap != previousRepresentativeDatabaseBeatmap) {
-            m_sTitle = m_representativeDatabaseBeatmap->getTitle();
-            m_sArtist = m_representativeDatabaseBeatmap->getArtist();
-            m_sMapper = m_representativeDatabaseBeatmap->getCreator();
-        }
-    }
-}

+ 3 - 18
src/App/Osu/OsuUISongBrowserSongButton.h

@@ -22,7 +22,6 @@ class OsuUISongBrowserSongButton : public OsuUISongBrowserButton {
     virtual ~OsuUISongBrowserSongButton();
 
     virtual void draw(Graphics *g);
-    virtual void mouse_update(bool *propagate_clicks);
 
     void triggerContextMenu(Vector2 pos);
 
@@ -49,21 +48,11 @@ class OsuUISongBrowserSongButton : public OsuUISongBrowserButton {
     float calculateGradeScale();
     float calculateGradeWidth();
 
-    UString buildTitleString() { return m_sTitle; }
-
-    UString buildSubTitleString() {
-        UString subTitleString = m_sArtist;
-        subTitleString.append(" // ");
-        subTitleString.append(m_sMapper);
-
-        return subTitleString;
-    }
-
     OsuDatabaseBeatmap *m_databaseBeatmap;
 
-    UString m_sTitle;
-    UString m_sArtist;
-    UString m_sMapper;
+    std::string m_sTitle;
+    std::string m_sArtist;
+    std::string m_sMapper;
     Score::Grade m_grade;
     bool m_bHasGrade;
 
@@ -79,10 +68,6 @@ class OsuUISongBrowserSongButton : public OsuUISongBrowserButton {
     static float thumbnailYRatio;
 
     float m_fThumbnailFadeInTime;
-
-    void updateRepresentativeDatabaseBeatmap();
-
-    OsuDatabaseBeatmap *m_representativeDatabaseBeatmap;
 };
 
 #endif

+ 1 - 1
src/App/Osu/OsuUISongBrowserSongDifficultyButton.cpp

@@ -107,7 +107,7 @@ void OsuUISongBrowserSongDifficultyButton::draw(Graphics *g) {
                      pos.y + size.y * m_fTextMarginScale + m_font->getHeight() * titleScale +
                          size.y * m_fTextSpacingScale + m_font->getHeight() * subTitleScale * 0.85f +
                          size.y * m_fTextSpacingScale + m_fontBold->getHeight() * diffScale * 0.8f);
-        g->drawString(m_fontBold, buildDiffString());
+        g->drawString(m_fontBold, m_sDiff.c_str());
     }
     g->popTransform();
 

+ 1 - 3
src/App/Osu/OsuUISongBrowserSongDifficultyButton.h

@@ -37,9 +37,7 @@ class OsuUISongBrowserSongDifficultyButton : public OsuUISongBrowserSongButton {
 
     virtual void onSelected(bool wasSelected, bool autoSelectBottomMostChild, bool wasParentSelected);
 
-    UString buildDiffString() { return m_sDiff; }
-
-    UString m_sDiff;
+    std::string m_sDiff;
 
     float m_fDiffScale;
     float m_fOffsetPercentAnim;

+ 3 - 3
src/App/Osu/OsuUserStatsScreen.cpp

@@ -396,11 +396,11 @@ void OsuUserStatsScreen::rebuildScoreButtons(UString playerName) {
 
         UString title = "...";
         if(diff != NULL) {
-            title = diff->getArtist();
+            title = diff->getArtist().c_str();
             title.append(" - ");
-            title.append(diff->getTitle());
+            title.append(diff->getTitle().c_str());
             title.append(" [");
-            title.append(diff->getDifficultyName());
+            title.append(diff->getDifficultyName().c_str());
             title.append("]");
         }