lsnes rr2-β24
[lsnes.git] / src / core / romguess.cpp
blob1c69543c7e59e554bd494a7b30310c2b748cce29
1 #include "core/instance.hpp"
2 #include "core/rom.hpp"
3 #include "core/settings.hpp"
4 #include "core/window.hpp"
5 #include "interface/romtype.hpp"
6 #include "library/directory.hpp"
7 #include "library/zip.hpp"
9 #include <fstream>
11 namespace
13 bool db_loaded;
14 std::map<std::pair<std::string, uint64_t>, std::string> our_db;
15 std::string database_name()
17 return get_config_path() + "/rom.db";
20 void load_db()
22 std::ifstream db(database_name());
23 if(db) {
24 std::string line;
25 while(std::getline(db, line)) {
26 istrip_CR(line);
27 size_t split = line.find_first_of("|");
28 if(split >= line.length())
29 continue;
30 std::string hash = line.substr(0, split);
31 std::string filename = line.substr(split + 1);
32 uint64_t prefix = 0;
33 size_t split2 = hash.find_first_of(":");
34 if(split2 < hash.length()) {
35 std::string _prefix = hash.substr(split2 + 1);
36 hash = hash.substr(0, split2);
37 try { prefix = parse_value<uint64_t>(_prefix); } catch(...) {};
39 if(hash != "") {
40 our_db[std::make_pair(filename, prefix)] = hash;
41 } else {
42 our_db.erase(std::make_pair(filename, prefix));
46 db_loaded = true;
49 void record_hash(const std::string& _file, uint64_t prefix, const std::string& hash)
51 if(!db_loaded) load_db();
52 //Database write. If there is existing entry for file, it is overwritten.
53 std::string file = directory::absolute_path(_file);
54 std::pair<std::string, uint64_t> key = std::make_pair(file, prefix);
55 if(hash == "" && !our_db.count(key))
56 return; //Already correct.
57 if(our_db.count(key) && our_db[key] == hash)
58 return; //Already correct.
59 if(hash != "")
60 our_db[key] = hash;
61 else
62 our_db.erase(key);
63 std::ofstream db(database_name(), std::ios::app);
64 db << hash << ":" << prefix << "|" << file << std::endl;
67 void record_hash_deleted(const std::string& _file)
69 if(!db_loaded) load_db();
70 auto itr = our_db.begin();
71 while(true) {
72 itr = our_db.lower_bound(std::make_pair(_file, 0));
73 if(itr == our_db.end() || itr->first.first != _file)
74 return;
75 record_hash(_file, itr->first.second, "");
79 std::list<std::pair<std::string, uint64_t>> retretive_files_by_hash(const std::string& hash)
81 if(!db_loaded) load_db();
82 //Database read. The read is for keys with given value.
83 std::list<std::pair<std::string, uint64_t>> x;
84 for(auto i : our_db)
85 if(i.second == hash) {
86 x.push_back(i.first);
88 return x;
91 std::string hash_file(const std::string& file, uint64_t hsize)
93 if(!zip::file_exists(file)) {
94 record_hash_deleted(file);
95 return "";
97 try {
98 fileimage::hashval f = lsnes_image_hasher(file, fileimage::std_headersize_fn(hsize));
99 std::string hash = f.read();
100 uint64_t prefix = f.prefix();
101 record_hash(file, prefix, hash);
102 return hash;
103 } catch(std::exception& e) {
104 return ""; //Error.
108 std::string try_basename(const std::string& hash, const std::string& xhash,
109 const std::string& file, uint64_t headersize)
111 if(!zip::file_exists(file))
112 return "";
113 std::string xfile = file + ".xml";
114 bool has_xfile = zip::file_exists(xfile);
115 if(xhash == "" && has_xfile)
116 return ""; //Markup mismatch.
117 if(xhash != "" && !has_xfile)
118 return ""; //Markup mismatch.
119 if(has_xfile && hash_file(xfile, 0) != xhash)
120 return ""; //Markup mismatch.
121 if(hash_file(file, headersize) == hash)
122 return file;
123 return "";
126 std::string try_scan_hint_dir(const std::string& hint, const std::string& hash,
127 const std::string& xhash, const std::string& dir, const std::set<std::string>& extensions,
128 uint64_t headersize)
130 std::string x;
131 for(auto j : extensions)
132 if((x = try_basename(hash, xhash, dir + "/" + hint + "." + j, headersize)) != "")
133 return x;
134 return "";
137 std::string try_guess_rom_core(const std::string& hint, const std::string& hash, const std::string& xhash,
138 const std::set<std::string>& extensions, uint64_t headersize, bool bios)
140 auto& core = CORE();
141 std::string x;
142 std::string romdir = SET_rompath(*core.settings);
143 std::string biosdir = SET_firmwarepath(*core.settings);
144 if((x = try_scan_hint_dir(hint, hash, xhash, romdir, extensions, headersize)) != "") return x;
145 if(bios && (x = try_scan_hint_dir(hint, hash, xhash, biosdir, extensions, headersize)) != "")
146 return x;
147 for(auto j : retretive_files_by_hash(hash)) {
148 if((x = try_basename(hash, xhash, j.first, headersize)) != "")
149 return x;
151 return "";
154 void try_guess_rom(rom_request& req, unsigned i)
156 std::string x;
157 if(req.guessed[i] || !req.has_slot[i] || req.hash[i] == "")
158 return; //Stay away from nonexistent slots and already guessed ones.
160 std::set<std::string> extensions;
161 uint64_t header = 0;
162 for(auto j : req.cores) {
163 if(j->get_image_count() <= i)
164 continue;
165 for(auto k : j->get_image_info(i).extensions) {
166 extensions.insert(k);
168 header = j->get_image_info(i).headersize;
171 if((x = try_guess_rom_core(req.filename[i], req.hash[i], req.hashxml[i], extensions, header,
172 i == 0)) != "") {
173 req.filename[i] = x;
174 req.guessed[i] = true;
179 std::string try_to_guess_rom(const std::string& hint, const std::string& hash, const std::string& xhash,
180 core_type& type, unsigned i)
182 std::set<std::string> extensions;
183 if(type.get_image_count() <= i)
184 return "";
185 for(auto k : type.get_image_info(i).extensions)
186 extensions.insert(k);
187 uint64_t header = type.get_image_info(i).headersize;
188 return try_guess_rom_core(hint, hash, xhash, extensions, header, type.get_biosname() != "" && i == 0);
191 void try_guess_roms(rom_request& req)
193 try {
194 for(unsigned i = 0; i < ROM_SLOT_COUNT; i++)
195 try_guess_rom(req, i);
196 } catch(std::exception& e) {
200 void record_filehash(const std::string& file, uint64_t prefix, const std::string& hash)
202 record_hash(file, prefix, hash);