Skip to content

Commit e5f90e0

Browse files
committed
perf: O(1) series data extraction and other speed ups
1 parent deffcfc commit e5f90e0

1 file changed

Lines changed: 61 additions & 86 deletions

File tree

source/Utils.cpp

Lines changed: 61 additions & 86 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@
77
#include <cstring>
88
#include <fstream>
99
#include <iomanip>
10+
#include <unordered_map>
11+
#include <unordered_set>
1012

1113
#include <AmiigoSettings.h>
1214
#include <AmiigoUI.h>
@@ -51,23 +53,23 @@ std::vector<AmiiboEntry> scanForAmiibo(const char* path) {
5153
dirent* entry;
5254
while ((entry = readdir(folder)) != nullptr) {
5355
AmiiboEntry amiibo;
54-
std::string flagPath = std::string(path) + "/" + entry->d_name;
55-
amiibo.path = flagPath;
56-
flagPath += "/amiibo.flag";
57-
amiibo.isCategory = !checkIfFileExists(flagPath.c_str());
56+
amiibo.path = std::string(path) + "/" + entry->d_name;
57+
std::string flagCheckPath = amiibo.path + "/amiibo.flag";
58+
amiibo.isCategory = !checkIfFileExists(flagCheckPath.c_str());
5859
amiibo.name = getAmiiboDisplayName(amiibo.path, amiibo.isCategory);
5960
amiibos.push_back(amiibo);
6061
}
6162
closedir(folder);
63+
6264
std::sort(amiibos.begin(), amiibos.end(), [](const AmiiboEntry& a, const AmiiboEntry& b) {
6365
return caseInsensitiveSort(a.name, b.name);
6466
});
67+
6568
if (!strcmp(path, "sdmc:/emuiibo/amiibo")) {
6669
amiibos.insert(amiibos.begin(), {U"★Favorites", true, "Favorites"});
6770
} else {
68-
std::string upDir = path;
69-
upDir = upDir.substr(0, upDir.find_last_of("/"));
70-
amiibos.insert(amiibos.begin(), {U"← Back", true, upDir});
71+
std::string cleanPath(path);
72+
amiibos.insert(amiibos.begin(), {U"← Back", true, cleanPath.substr(0, cleanPath.find_last_of("/"))});
7173
}
7274
}
7375

@@ -76,10 +78,8 @@ std::vector<AmiiboEntry> scanForAmiibo(const char* path) {
7678
std::string tempLine;
7779
std::ifstream fileStream("sdmc:/emuiibo/overlay/favorites.txt");
7880
while (getline(fileStream, tempLine)) {
79-
char flagPath[512] = "";
80-
strcat(flagPath, tempLine.c_str());
81-
strcat(flagPath, "/amiibo.flag");
82-
bool isCategory = !checkIfFileExists(flagPath);
81+
std::string flagCheckPath = tempLine + "/amiibo.flag";
82+
bool isCategory = !checkIfFileExists(flagCheckPath.c_str());
8383
amiibos.push_back({getAmiiboDisplayName(tempLine, isCategory), isCategory, tempLine});
8484
}
8585
fileStream.close();
@@ -88,35 +88,28 @@ std::vector<AmiiboEntry> scanForAmiibo(const char* path) {
8888
}
8989

9090
std::vector<std::string> getListOfSeries() {
91+
if (!checkIfFileExists("sdmc:/config/amiigo/API.json")) return {"Error, no API cache!"};
92+
93+
JsonDoc APIJson = loadJsonFile("sdmc:/config/amiigo/API.json");
94+
if (APIJson.is_discarded()) {
95+
printf("API cache is corrupt\n");
96+
remove("sdmc:/config/amiigo/API.json");
97+
return {"Error, API cache corrupt!", "Try updating cache in settings!"};
98+
}
99+
100+
std::unordered_set<std::string> seen;
91101
std::vector<std::string> series;
92-
if (checkIfFileExists("sdmc:/config/amiigo/API.json")) {
93-
JsonDoc APIJson = loadJsonFile("sdmc:/config/amiigo/API.json");
94-
if (APIJson.is_discarded()) {
95-
printf("API cache is corrupt\n");
96-
remove("sdmc:/config/amiigo/API.json");
97-
return {"Error, API cache corrupt!", "Try updating cache in settings!"};
98-
}
99-
for (int i = 0; i < APIJson["amiibo"].size(); i++) {
100-
bool isKnown = false;
101-
std::string seriesName = APIJson["amiibo"][i]["amiiboSeries"].get<std::string>();
102-
for (size_t j = 0; j < series.size(); j++) {
103-
if (series[j] == seriesName) {
104-
isKnown = true;
105-
break;
106-
}
107-
}
108-
if (!isKnown) series.push_back(seriesName);
109-
}
110-
} else {
111-
return {"Error, no API cache!"};
102+
for (int i = 0; i < APIJson["amiibo"].size(); i++) {
103+
std::string seriesName = APIJson["amiibo"][i]["amiiboSeries"].get<std::string>();
104+
if (seen.insert(seriesName).second) series.push_back(seriesName);
112105
}
113106

114107
std::sort(series.begin(), series.end(), caseInsensitiveSort<std::string>);
115108
return series;
116109
}
117110

118111
// Written on 27/01/2021 for Kronos, can't remember how it works but does magic bit shifting
119-
unsigned short shiftAndDec(std::string input) {
112+
unsigned short shiftAndDec(const std::string& input) {
120113
unsigned short value = std::stoi(input, nullptr, 16);
121114
unsigned short a = value & 0xFF00;
122115
a = 0x00FF & (a >> 8);
@@ -127,38 +120,35 @@ unsigned short shiftAndDec(std::string input) {
127120
}
128121

129122
std::vector<AmiiboCreatorData> getAmiibosFromSeries(const std::string& series) {
123+
if (!checkIfFileExists("sdmc:/config/amiigo/API.json")) return {{U"Error, API cache vanished?", U"", U"", 0, 0, 0}};
124+
JsonDoc APIJson = loadJsonFile("sdmc:/config/amiigo/API.json");
125+
130126
std::vector<AmiiboCreatorData> amiibos;
131-
if (checkIfFileExists("sdmc:/config/amiigo/API.json")) {
132-
JsonDoc APIJson = loadJsonFile("sdmc:/config/amiigo/API.json");
133-
for (int i = 0; i < APIJson["amiibo"].size(); i++) {
134-
if (APIJson["amiibo"][i]["amiiboSeries"].get<std::string>() == series) {
135-
// Process the API data the same way Emutool does
136-
// https://github.com/XorTroll/emuiibo/blob/90cbc54a95c0aa4a9ceb6dd55b633de206763094/emutool/emutool/AmiiboUtils.cs#L144
137-
AmiiboCreatorData newAmiibo;
138-
std::wstring_convert<std::codecvt_utf8<char32_t>, char32_t> ASCIIToUnicodeConverter;
139-
newAmiibo.name = ASCIIToUnicodeConverter.from_bytes(APIJson["amiibo"][i]["name"].get<std::string>().c_str());
140-
std::string fullID = APIJson["amiibo"][i]["head"].get<std::string>() + APIJson["amiibo"][i]["tail"].get<std::string>();
141-
// Var names taken from emutool
142-
std::string character_game_id_str = fullID.substr(0, 4);
143-
std::string character_variant_str = fullID.substr(4, 2);
144-
std::string figure_type_str = fullID.substr(6, 2);
145-
std::string model_no_str = fullID.substr(8, 4);
146-
std::string series_str = fullID.substr(12, 2);
147-
// Swap endianess for game ID
148-
newAmiibo.game_character_id = shiftAndDec(character_game_id_str);
149-
newAmiibo.character_variant = static_cast<char>(stoi(character_variant_str, nullptr, 16));
150-
newAmiibo.figure_type = static_cast<char>(stoi(figure_type_str, nullptr, 16));
151-
newAmiibo.model_number = (unsigned short)stoi(model_no_str, nullptr, 16);
152-
newAmiibo.series = static_cast<char>(stoi(series_str, nullptr, 16));
153-
newAmiibo.gameName = ASCIIToUnicodeConverter.from_bytes(APIJson["amiibo"][i]["gameSeries"].get<std::string>().c_str()); // only used for categorization
154-
newAmiibo.amiiboSeries = ASCIIToUnicodeConverter.from_bytes(APIJson["amiibo"][i]["amiiboSeries"].get<std::string>().c_str()); // only used for categorization
155-
newAmiibo.imageURL = APIJson["amiibo"][i]["image"].get<std::string>();
156-
157-
amiibos.push_back(newAmiibo);
158-
}
159-
}
160-
} else {
161-
return {{U"Error, API cache vanished?", U"", U"", 0, 0, 0}};
127+
for (int i = 0; i < APIJson["amiibo"].size(); i++) {
128+
if (APIJson["amiibo"][i]["amiiboSeries"].get<std::string>() != series) continue;
129+
// Process the API data the same way Emutool does
130+
// https://github.com/XorTroll/emuiibo/blob/90cbc54a95c0aa4a9ceb6dd55b633de206763094/emutool/emutool/AmiiboUtils.cs#L144
131+
AmiiboCreatorData newAmiibo;
132+
std::wstring_convert<std::codecvt_utf8<char32_t>, char32_t> ASCIIToUnicodeConverter;
133+
newAmiibo.name = ASCIIToUnicodeConverter.from_bytes(APIJson["amiibo"][i]["name"].get<std::string>().c_str());
134+
std::string fullID = APIJson["amiibo"][i]["head"].get<std::string>() + APIJson["amiibo"][i]["tail"].get<std::string>();
135+
// Var names taken from emutool
136+
std::string character_game_id_str = fullID.substr(0, 4);
137+
std::string character_variant_str = fullID.substr(4, 2);
138+
std::string figure_type_str = fullID.substr(6, 2);
139+
std::string model_no_str = fullID.substr(8, 4);
140+
std::string series_str = fullID.substr(12, 2);
141+
// Swap endianess for game ID
142+
newAmiibo.game_character_id = shiftAndDec(character_game_id_str);
143+
newAmiibo.character_variant = static_cast<char>(stoi(character_variant_str, nullptr, 16));
144+
newAmiibo.figure_type = static_cast<char>(stoi(figure_type_str, nullptr, 16));
145+
newAmiibo.model_number = (unsigned short)stoi(model_no_str, nullptr, 16);
146+
newAmiibo.series = static_cast<char>(stoi(series_str, nullptr, 16));
147+
newAmiibo.gameName = ASCIIToUnicodeConverter.from_bytes(APIJson["amiibo"][i]["gameSeries"].get<std::string>().c_str()); // only used for categorization
148+
newAmiibo.amiiboSeries = ASCIIToUnicodeConverter.from_bytes(APIJson["amiibo"][i]["amiiboSeries"].get<std::string>().c_str()); // only used for categorization
149+
newAmiibo.imageURL = APIJson["amiibo"][i]["image"].get<std::string>();
150+
151+
amiibos.push_back(newAmiibo);
162152
}
163153

164154
std::sort(amiibos.begin(), amiibos.end(), [](const AmiiboCreatorData& a, const AmiiboCreatorData& b) {
@@ -167,30 +157,15 @@ std::vector<AmiiboCreatorData> getAmiibosFromSeries(const std::string& series) {
167157
return amiibos;
168158
}
169159

170-
std::string sanitizeAmiiboName(std::u32string path) {
160+
std::string sanitizeAmiiboName(const std::u32string& name) {
161+
static const std::unordered_map<char32_t, char> replacements = {
162+
{U'é', 'e'}, {U'ō', 'o'}, {U'É', 'E'}, {U'', '\''}, {U'', '\''}
163+
};
171164
std::string output;
172-
for (char32_t c: path) {
173-
switch (c)
174-
{
175-
case U'é':
176-
output += 'e';
177-
break;
178-
case U'ō':
179-
output += (char)'o';
180-
break;
181-
case U'É':
182-
output += 'E';
183-
break;
184-
case U'':
185-
output += '\'';
186-
break;
187-
case U'':
188-
output += '\'';
189-
break;
190-
default:
191-
output += (char)c;
192-
break;
193-
}
165+
output.reserve(name.size());
166+
for (char32_t c : name) {
167+
auto it = replacements.find(c);
168+
output += (it != replacements.end()) ? it->second : static_cast<char>(c);
194169
}
195170
return output;
196171
}

0 commit comments

Comments
 (0)