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"
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";
22 std::ifstream
db(database_name());
25 while(std::getline(db
, line
)) {
27 size_t split
= line
.find_first_of("|");
28 if(split
>= line
.length())
30 std::string hash
= line
.substr(0, split
);
31 std::string filename
= line
.substr(split
+ 1);
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(...) {};
40 our_db
[std::make_pair(filename
, prefix
)] = hash
;
42 our_db
.erase(std::make_pair(filename
, prefix
));
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.
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();
72 itr
= our_db
.lower_bound(std::make_pair(_file
, 0));
73 if(itr
== our_db
.end() || itr
->first
.first
!= _file
)
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
;
85 if(i
.second
== hash
) {
91 std::string
hash_file(const std::string
& file
, uint64_t hsize
)
93 if(!zip::file_exists(file
)) {
94 record_hash_deleted(file
);
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
);
103 } catch(std::exception
& e
) {
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
))
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
)
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
,
131 for(auto j
: extensions
)
132 if((x
= try_basename(hash
, xhash
, dir
+ "/" + hint
+ "." + j
, headersize
)) != "")
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
)
142 std::string romdir
= core
.setcache
->get("rompath");
143 std::string biosdir
= core
.setcache
->get("firmwarepath");
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
)) != "")
147 for(auto j
: retretive_files_by_hash(hash
)) {
148 if((x
= try_basename(hash
, xhash
, j
.first
, headersize
)) != "")
154 void try_guess_rom(rom_request
& req
, unsigned i
)
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
;
162 for(auto j
: req
.cores
) {
163 if(j
->get_image_count() <= i
)
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
,
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
)
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
)
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
);