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>
10 #ifdef BOOST_FILESYSTEM3
11 namespace boost_fs
= boost::filesystem3
;
13 namespace boost_fs
= boost::filesystem
;
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";
27 std::ifstream
db(database_name());
30 while(std::getline(db
, line
)) {
32 size_t split
= line
.find_first_of("|");
33 if(split
>= line
.length())
35 std::string hash
= line
.substr(0, split
);
36 std::string filename
= line
.substr(split
+ 1);
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(...) {};
45 our_db
[std::make_pair(filename
, prefix
)] = hash
;
47 our_db
.erase(std::make_pair(filename
, prefix
));
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.
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();
77 itr
= our_db
.lower_bound(std::make_pair(_file
, 0));
78 if(itr
== our_db
.end() || itr
->first
.first
!= _file
)
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
;
90 if(i
.second
== hash
) {
96 std::string
hash_file(const std::string
& file
, uint64_t hsize
)
98 if(!file_exists_zip(file
)) {
99 record_hash_deleted(file
);
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
);
108 } catch(std::exception
& e
) {
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
))
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
)
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
,
136 for(auto j
: extensions
)
137 if((x
= try_basename(hash
, xhash
, dir
+ "/" + hint
+ "." + j
, headersize
)) != "")
142 std::string
implode(const std::set
<std::string
>& s
, const std::string
& e
)
145 std::ostringstream y
;
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
)
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
)) != "")
163 for(auto j
: retretive_files_by_hash(hash
)) {
164 if((x
= try_basename(hash
, xhash
, j
.first
, headersize
)) != "")
170 void try_guess_rom(rom_request
& req
, unsigned i
)
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
;
178 for(auto j
: req
.cores
) {
179 if(j
->get_image_count() <= i
)
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
,
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
)
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
)
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
);