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
9090std::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
129122std::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