JSON-based controller descriptions
[lsnes.git] / src / core / romguess.cpp
blobc3b13801d53e7c8c053b228be0a58d7a08e5b985
1 #include "core/misc.hpp"
2 #include "core/rom.hpp"
3 #include "core/romguess.hpp"
4 #include "core/settings.hpp"
5 #include "interface/romtype.hpp"
6 #include "library/zip.hpp"
7 #include <boost/filesystem.hpp>
8 #include <fstream>
10 #ifdef BOOST_FILESYSTEM3
11 namespace boost_fs = boost::filesystem3;
12 #else
13 namespace boost_fs = boost::filesystem;
14 #endif
16 namespace
18 bool db_loaded;
19 std::map<std::pair<std::string, uint64_t>, std::string> our_db;
20 std::string database_name()
22 return get_config_path() + "/rom.db";
25 void load_db()
27 std::ifstream db(database_name());
28 if(db) {
29 std::string line;
30 while(std::getline(db, line)) {
31 istrip_CR(line);
32 size_t split = line.find_first_of("|");
33 if(split >= line.length())
34 continue;
35 std::string hash = line.substr(0, split);
36 std::string filename = line.substr(split + 1);
37 uint64_t prefix = 0;
38 size_t split2 = hash.find_first_of(":");
39 if(split2 < hash.length()) {
40 std::string _prefix = hash.substr(split2 + 1);
41 hash = hash.substr(0, split2);
42 try { prefix = parse_value<uint64_t>(_prefix); } catch(...) {};
44 if(hash != "") {
45 our_db[std::make_pair(filename, prefix)] = hash;
46 } else {
47 our_db.erase(std::make_pair(filename, prefix));
51 db_loaded = true;
54 void record_hash(const std::string& _file, uint64_t prefix, const std::string& hash)
56 if(!db_loaded) load_db();
57 //Database write. If there is existing entry for file, it is overwritten.
58 std::string file = boost_fs::absolute(boost_fs::path(_file)).string();
59 std::pair<std::string, uint64_t> key = std::make_pair(file, prefix);
60 if(hash == "" && !our_db.count(key))
61 return; //Already correct.
62 if(our_db.count(key) && our_db[key] == hash)
63 return; //Already correct.
64 if(hash != "")
65 our_db[key] = hash;
66 else
67 our_db.erase(key);
68 std::ofstream db(database_name(), std::ios::app);
69 db << hash << ":" << prefix << "|" << file << std::endl;
72 void record_hash_deleted(const std::string& _file)
74 if(!db_loaded) load_db();
75 auto itr = our_db.begin();
76 while(true) {
77 itr = our_db.lower_bound(std::make_pair(_file, 0));
78 if(itr == our_db.end() || itr->first.first != _file)
79 return;
80 record_hash(_file, itr->first.second, "");
84 std::list<std::pair<std::string, uint64_t>> retretive_files_by_hash(const std::string& hash)
86 if(!db_loaded) load_db();
87 //Database read. The read is for keys with given value.
88 std::list<std::pair<std::string, uint64_t>> x;
89 for(auto i : our_db)
90 if(i.second == hash) {
91 x.push_back(i.first);
93 return x;
96 std::string hash_file(const std::string& file, uint64_t hsize)
98 if(!file_exists_zip(file)) {
99 record_hash_deleted(file);
100 return "";
102 try {
103 sha256_future f = lsnes_image_hasher(file, std_headersize_fn(hsize));
104 std::string hash = f.read();
105 uint64_t prefix = f.prefix();
106 record_hash(file, prefix, hash);
107 return hash;
108 } catch(std::exception& e) {
109 return ""; //Error.
113 std::string try_basename(const std::string& hash, const std::string& xhash,
114 const std::string& file, uint64_t headersize)
116 if(!file_exists_zip(file))
117 return "";
118 std::string xfile = file + ".xml";
119 bool has_xfile = file_exists_zip(xfile);
120 if(xhash == "" && has_xfile)
121 return ""; //Markup mismatch.
122 if(xhash != "" && !has_xfile)
123 return ""; //Markup mismatch.
124 if(has_xfile && hash_file(xfile, 0) != xhash)
125 return ""; //Markup mismatch.
126 if(hash_file(file, headersize) == hash)
127 return file;
128 return "";
131 std::string try_scan_hint_dir(const std::string& hint, const std::string& hash,
132 const std::string& xhash, const std::string& dir, const std::set<std::string>& extensions,
133 uint64_t headersize)
135 std::string x;
136 for(auto j : extensions)
137 if((x = try_basename(hash, xhash, dir + "/" + hint + "." + j, headersize)) != "")
138 return x;
139 return "";
142 std::string implode(const std::set<std::string>& s, const std::string& e)
144 bool first = true;
145 std::ostringstream y;
146 for(auto i : s) {
147 if(!first) y << e;
148 y << i;
149 first = false;
151 return y.str();
154 std::string try_guess_rom_core(const std::string& hint, const std::string& hash, const std::string& xhash,
155 const std::set<std::string>& extensions, uint64_t headersize, bool bios)
157 std::string x;
158 std::string romdir = lsnes_vset["rompath"].str();
159 std::string biosdir = lsnes_vset["firmwarepath"].str();
160 if((x = try_scan_hint_dir(hint, hash, xhash, romdir, extensions, headersize)) != "") return x;
161 if(bios && (x = try_scan_hint_dir(hint, hash, xhash, biosdir, extensions, headersize)) != "")
162 return x;
163 for(auto j : retretive_files_by_hash(hash)) {
164 if((x = try_basename(hash, xhash, j.first, headersize)) != "")
165 return x;
167 return "";
170 void try_guess_rom(rom_request& req, unsigned i)
172 std::string x;
173 if(req.guessed[i] || !req.has_slot[i] || req.hash[i] == "")
174 return; //Stay away from nonexistent slots and already guessed ones.
176 std::set<std::string> extensions;
177 uint64_t header = 0;
178 for(auto j : req.cores) {
179 if(j->get_image_count() <= i)
180 continue;
181 for(auto k : j->get_image_info(i).extensions) {
182 extensions.insert(k);
184 header = j->get_image_info(i).headersize;
187 if((x = try_guess_rom_core(req.filename[i], req.hash[i], req.hashxml[i], extensions, header,
188 i == 0)) != "") {
189 req.filename[i] = x;
190 req.guessed[i] = true;
195 std::string try_to_guess_rom(const std::string& hint, const std::string& hash, const std::string& xhash,
196 core_type& type, unsigned i)
198 std::set<std::string> extensions;
199 if(type.get_image_count() <= i)
200 return "";
201 for(auto k : type.get_image_info(i).extensions)
202 extensions.insert(k);
203 uint64_t header = type.get_image_info(i).headersize;
204 return try_guess_rom_core(hint, hash, xhash, extensions, header, type.get_biosname() != "" && i == 0);
207 void try_guess_roms(rom_request& req)
209 try {
210 for(unsigned i = 0; i < ROM_SLOT_COUNT; i++)
211 try_guess_rom(req, i);
212 } catch(std::exception& e) {
216 void record_filehash(const std::string& file, uint64_t prefix, const std::string& hash)
218 record_hash(file, prefix, hash);