Add <functional> to files that use std::function
[lsnes.git] / src / core / romimage.cpp
blob1b3a4ac8990964c1e1769197c0ec78daab250326
1 #include "core/romimage.hpp"
2 #include "core/instance.hpp"
3 #include "core/messages.hpp"
4 #include "core/misc.hpp"
5 #include "core/nullcore.hpp"
6 #include "core/settings.hpp"
7 #include "core/window.hpp"
8 #include "library/memtracker.hpp"
9 #include "library/zip.hpp"
10 #include <functional>
12 fileimage::image rom_image::null_img;
13 fileimage::hash lsnes_image_hasher;
14 rom_image rom_image_handle::null_img;
16 namespace
18 const char* romimage_id = "ROM images";
19 core_type* prompt_core_fallback(const std::vector<core_type*>& choices)
21 if(choices.size() == 0)
22 return NULL;
23 if(choices.size() == 1)
24 return choices[0];
25 rom_request req;
26 req.cores = choices;
27 req.core_guessed = false;
28 req.selected = 0;
29 //Tell that all ROMs have been correctly guessed, leaving only core select.
30 for(unsigned i = 0; i < ROM_SLOT_COUNT; i++) {
31 req.has_slot[i] = false;
32 req.guessed[i] = false;
34 req.canceled = false;
35 graphics_driver_request_rom(req);
36 if(req.canceled)
37 throw std::runtime_error("Canceled ROM loading");
38 if(req.selected < choices.size())
39 return choices[req.selected];
40 return choices[0];
43 core_type* find_core_by_extension(const std::string& ext, const std::string& tmpprefer)
45 std::string key = "ext:" + ext;
46 std::list<core_type*> possible = core_type::get_core_types();
47 core_type* preferred = preferred_core.count(key) ? preferred_core[key] : NULL;
48 std::vector<core_type*> fallbacks;
49 //Tmpprefer overrides normal preferred core.
50 if(tmpprefer != "")
51 for(auto i : possible)
52 if(i->get_core_identifier() == tmpprefer)
53 return i;
54 for(auto i : possible)
55 if(i->is_known_extension(ext)) {
56 fallbacks.push_back(i);
57 if(i == preferred)
58 return i;
60 core_type* fallback = prompt_core_fallback(fallbacks);
61 if(!fallback) throw std::runtime_error("No core available to load the ROM");
62 return fallback;
65 core_type* find_core_by_name(const std::string& name, const std::string& tmpprefer)
67 std::string key = "type:" + name;
68 std::list<core_type*> possible = core_type::get_core_types();
69 std::vector<core_type*> fallbacks;
70 core_type* preferred = preferred_core.count(key) ? preferred_core[key] : NULL;
71 //Tmpprefer overrides normal preferred core.
72 if(tmpprefer != "")
73 for(auto i : possible)
74 if(i->get_iname() == tmpprefer)
75 return i;
76 for(auto i : possible)
77 if(i->get_iname() == name) {
78 fallbacks.push_back(i);
79 if(i == preferred)
80 return i;
82 core_type* fallback = prompt_core_fallback(fallbacks);
83 if(!fallback) throw std::runtime_error("No core available to load the ROM");
84 return fallback;
87 bool filter_by_core(core_type& ctype, const std::string& core)
89 return (core == "" || ctype.get_core_identifier() == core);
92 bool filter_by_type(core_type& ctype, const std::string& type)
94 return (type == "" || ctype.get_iname() == type);
97 bool filter_by_region(core_type& ctype, const std::string& region)
99 if(region == "")
100 return true;
101 for(auto i : ctype.get_regions())
102 if(i->get_iname() == region)
103 return true;
104 return false;
107 bool filter_by_extension(core_type& ctype, const std::string& file)
109 regex_results tmp = regex(".*\\.([^.]*)", file);
110 if(!tmp)
111 return false;
112 std::string ext = tmp[1];
113 return ctype.is_known_extension(ext);
116 bool filter_by_fileset(core_type& ctype, const std::string file[ROM_SLOT_COUNT])
118 uint32_t m = 0, t = 0;
119 for(unsigned i = 0; i < ROM_SLOT_COUNT; i++) {
120 if(i >= ctype.get_image_count() && file[i] != "")
121 return false;
122 auto s = ctype.get_image_info(i);
123 if(file[i] != "")
124 m |= s.mandatory;
125 t |= s.mandatory;
127 return (m == t);
130 core_region* detect_region(core_type* t, const std::string& region)
132 core_region* r = NULL;
133 for(auto i: t->get_regions())
134 if(i->get_iname() == region)
135 r = i;
136 if(!r && region != "")
137 (stringfmt() << "Not a valid system region '" << region << "'").throwex();
138 if(!r) r = &t->get_preferred_region(); //Default region.
139 return r;
142 struct fileimage::image::info get_xml_info()
144 fileimage::image::info i;
145 i.type = fileimage::image::info::IT_MARKUP;
146 i.headersize = 0;
147 return i;
150 struct fileimage::image::info xlate_info(core_romimage_info ri)
152 fileimage::image::info i;
153 if(ri.pass_mode == 0) i.type = fileimage::image::info::IT_MEMORY;
154 if(ri.pass_mode == 1) i.type = fileimage::image::info::IT_FILE;
155 i.headersize = ri.headersize;
156 return i;
159 void record_files(rom_image& rom)
161 for(unsigned i = 0; i < ROM_SLOT_COUNT; i++) {
162 try {
163 auto& j = rom.get_image(i, false);
164 record_filehash(j.filename, j.stripped, j.sha_256.read());
165 } catch(...) {}
166 try {
167 auto& j = rom.get_image(i, true);
168 record_filehash(j.filename, j.stripped, j.sha_256.read());
169 } catch(...) {}
174 void rom_image::setup_region(core_region& reg)
176 if(!orig_region)
177 orig_region = &reg;
180 rom_image::rom_image() throw()
181 : tracker(memtracker::singleton(), romimage_id, sizeof(*this))
183 rtype = &get_null_type();
184 region = orig_region = &get_null_region();
185 account_images();
188 rom_image::rom_image(const std::string& file, core_type& ctype) throw(std::bad_alloc, std::runtime_error)
189 : tracker(memtracker::singleton(), romimage_id, sizeof(*this))
191 rtype = &ctype;
192 orig_region = &rtype->get_preferred_region();
193 unsigned romidx = 0;
194 std::string bios;
195 unsigned pmand = 0, tmand = 0;
196 for(unsigned i = 0; i < ctype.get_image_count(); i++)
197 tmand |= ctype.get_image_info(i).mandatory;
198 if((bios = ctype.get_biosname()) != "") {
199 //This thing has a BIOS.
200 romidx = 1;
201 std::string basename = SET_firmwarepath(*CORE().settings) + "/" + bios;
202 romimg[0] = fileimage::image(lsnes_image_hasher, basename, "", xlate_info(ctype.get_image_info(0)));
203 if(zip::file_exists(basename + ".xml"))
204 romxml[0] = fileimage::image(lsnes_image_hasher, basename + ".xml", "", get_xml_info());
205 pmand |= ctype.get_image_info(0).mandatory;
207 romimg[romidx] = fileimage::image(lsnes_image_hasher, file, "", xlate_info(ctype.get_image_info(romidx)));
208 if(zip::file_exists(file + ".xml"))
209 romxml[romidx] = fileimage::image(lsnes_image_hasher, file + ".xml", "", get_xml_info());
210 pmand |= ctype.get_image_info(romidx).mandatory;
211 msu1_base = zip::resolverel(file, "");
212 record_files(*this);
213 if(pmand != tmand)
214 throw std::runtime_error("Required ROM images missing");
215 account_images();
216 return;
219 rom_image::rom_image(const std::string& file, const std::string& tmpprefer) throw(std::bad_alloc,
220 std::runtime_error)
221 : tracker(memtracker::singleton(), romimage_id, sizeof(*this))
223 std::istream& spec = zip::openrel(file, "");
224 std::string s;
225 std::getline(spec, s);
226 istrip_CR(s);
227 if(!spec || s != "[GAMEPACK FILE]") {
228 delete &spec; //Close the file!
229 //This is a Raw ROM image.
230 regex_results tmp;
231 std::string ext = regex(".*\\.([^.]*)?", file, "Can't read file extension")[1];
232 core_type* coretype = find_core_by_extension(ext, tmpprefer);
233 if(!coretype)
234 (stringfmt() << "Extension '" << ext << "' unknown").throwex();
235 rtype = coretype;
236 region = orig_region = &rtype->get_preferred_region();
237 unsigned romidx = 0;
238 std::string bios;
239 unsigned pmand = 0, tmand = 0;
240 for(unsigned i = 0; i < rtype->get_image_count(); i++)
241 tmand |= rtype->get_image_info(i).mandatory;
242 if((bios = coretype->get_biosname()) != "") {
243 //This thing has a BIOS.
244 romidx = 1;
245 std::string basename = SET_firmwarepath(*CORE().settings) + "/" + bios;
246 romimg[0] = fileimage::image(lsnes_image_hasher, basename, "",
247 xlate_info(coretype->get_image_info(0)));
248 if(zip::file_exists(basename + ".xml"))
249 romxml[0] = fileimage::image(lsnes_image_hasher, basename + ".xml", "",
250 get_xml_info());
251 pmand |= rtype->get_image_info(0).mandatory;
253 romimg[romidx] = fileimage::image(lsnes_image_hasher, file, "",
254 xlate_info(coretype->get_image_info(romidx)));
255 if(zip::file_exists(file + ".xml"))
256 romxml[romidx] = fileimage::image(lsnes_image_hasher, file + ".xml", "", get_xml_info());
257 pmand |= rtype->get_image_info(romidx).mandatory;
258 msu1_base = zip::resolverel(file, "");
259 record_files(*this);
260 if(pmand != tmand)
261 throw std::runtime_error("Required ROM images missing");
262 account_images();
263 return;
265 load_bundle(file, spec, tmpprefer);
266 account_images();
269 void rom_image::load_bundle(const std::string& file, std::istream& spec, const std::string& tmpprefer)
270 throw(std::bad_alloc, std::runtime_error)
272 std::string s;
273 load_filename = file;
274 std::vector<std::string> lines;
275 while(std::getline(spec, s))
276 lines.push_back(strip_CR(s));
277 delete &spec;
278 std::string platname = "";
279 std::string platreg = "";
280 for(auto i : lines) {
281 regex_results tmp;
282 if(tmp = regex("type[ \t]+(.+)", i))
283 platname = tmp[1];
284 if(tmp = regex("region[ \t]+(.+)", i))
285 platreg = tmp[1];
288 //Detect type.
289 rtype = find_core_by_name(platname, tmpprefer);
290 if(!rtype)
291 (stringfmt() << "Not a valid system type '" << platname << "'").throwex();
293 //Detect region.
294 bool goodreg = false;
295 orig_region = &rtype->get_preferred_region();
296 for(auto i: rtype->get_regions())
297 if(i->get_iname() == platreg) {
298 orig_region = i;
299 goodreg = true;
301 if(!goodreg && platreg != "")
302 (stringfmt() << "Not a valid system region '" << platreg << "'").throwex();
303 region = orig_region;
305 //ROM files.
306 std::string cromimg[ROM_SLOT_COUNT];
307 std::string cromxml[ROM_SLOT_COUNT];
308 for(auto i : lines) {
309 regex_results tmp;
310 if(!(tmp = regex("(rom|xml)[ \t]+([^ \t]+)[ \t]+(.*)", i)))
311 continue;
312 size_t idxs = rtype->get_image_count();
313 size_t idx = idxs;
314 for(size_t i = 0; i < idxs; i++)
315 if(rtype->get_image_info(i).iname == tmp[2])
316 idx = i;
317 if(idx == idxs)
318 (stringfmt() << "Not a valid ROM name '" << tmp[2] << "'").throwex();
319 if(tmp[1] == "rom")
320 cromimg[idx] = tmp[3];
321 else
322 cromxml[idx] = tmp[3];
325 //Check ROMs.
326 unsigned mask1 = 0, mask2 = 0;
327 for(size_t i = 0; i < rtype->get_image_count(); i++) {
328 auto ii = rtype->get_image_info(i);
329 mask1 |= ii.mandatory;
330 if(cromimg[i] != "")
331 mask2 |= ii.mandatory;
332 if(cromimg[i] == "" && cromxml[i] != "") {
333 messages << "WARNING: Slot " << ii.iname << ": XML without ROM." << std::endl;
334 cromxml[i] = "";
337 if(mask1 != mask2)
338 throw std::runtime_error("Required ROM missing");
340 //Load ROMs.
341 for(size_t i = 0; i < rtype->get_image_count(); i++) {
342 romimg[i] = fileimage::image(lsnes_image_hasher, cromimg[i], file,
343 xlate_info(rtype->get_image_info(i)));
344 romxml[i] = fileimage::image(lsnes_image_hasher, cromxml[i], file, get_xml_info());
346 record_files(*this); //Have to do this before patching.
348 //Patch ROMs.
349 for(auto i : lines) {
350 regex_results tmp;
351 if(!(tmp = regex("patch([+-][0-9]+)?[ \t]+([^ \t]+)[ \t]+(.*)", i)))
352 continue;
353 size_t idxs = rtype->get_image_count();
354 size_t idx = idxs;
355 for(size_t i = 0; i < idxs; i++)
356 if(rtype->get_image_info(i).iname == tmp[2])
357 idx = i;
358 if(idx == idxs)
359 (stringfmt() << "Not a valid ROM name '" << tmp[2] << "'").throwex();
360 int32_t offset = 0;
361 if(tmp[1] != "")
362 offset = parse_value<int32_t>(tmp[1]);
363 romimg[idx].patch(zip::readrel(tmp[3], file), offset);
366 //MSU-1 base.
367 if(rtype->get_biosname() != "")
368 msu1_base = zip::resolverel(cromimg[1], file);
369 else
370 msu1_base = zip::resolverel(cromimg[0], file);
373 rom_image::rom_image(const std::string& file, const std::string& core, const std::string& type,
374 const std::string& _region)
375 : tracker(memtracker::singleton(), romimage_id, sizeof(*this))
377 core_type* t = NULL;
378 core_region* r = NULL;
379 bool fullspec = (core != "" && type != "");
380 for(auto i : core_type::get_core_types()) {
381 if(!filter_by_core(*i, core))
382 continue;
383 if(!filter_by_type(*i, type))
384 continue;
385 if(!fullspec && !filter_by_region(*i, _region))
386 continue;
387 if(!fullspec && !filter_by_extension(*i, file))
388 continue;
389 t = i;
391 if(!t) throw std::runtime_error("No matching core found");
392 r = detect_region(t, _region);
393 unsigned pmand = 0, tmand = 0;
394 for(unsigned i = 0; i < t->get_image_count(); i++)
395 tmand |= t->get_image_info(i).mandatory;
396 std::string bios = t->get_biosname();
397 unsigned romidx = (bios != "") ? 1 : 0;
398 if(bios != "") {
399 std::string basename = SET_firmwarepath(*CORE().settings) + "/" + bios;
400 romimg[0] = fileimage::image(lsnes_image_hasher, basename, "", xlate_info(t->get_image_info(0)));
401 if(zip::file_exists(basename + ".xml"))
402 romxml[0] = fileimage::image(lsnes_image_hasher, basename + ".xml", "", get_xml_info());
403 pmand |= t->get_image_info(0).mandatory;
405 romimg[romidx] = fileimage::image(lsnes_image_hasher, file, "", xlate_info(t->get_image_info(romidx)));
406 if(zip::file_exists(file + ".xml"))
407 romxml[romidx] = fileimage::image(lsnes_image_hasher, file + ".xml", "", get_xml_info());
408 pmand |= t->get_image_info(romidx).mandatory;
409 msu1_base = zip::resolverel(file, "");
410 record_files(*this);
411 if(pmand != tmand)
412 throw std::runtime_error("Required ROM images missing");
413 rtype = t;
414 orig_region = region = r;
415 account_images();
418 rom_image::rom_image(const std::string file[ROM_SLOT_COUNT], const std::string& core, const std::string& type,
419 const std::string& _region)
420 : tracker(memtracker::singleton(), romimage_id, sizeof(*this))
422 core_type* t = NULL;
423 core_region* r = NULL;
424 bool fullspec = (core != "" && type != "");
425 for(auto i : core_type::get_core_types()) {
426 if(!filter_by_core(*i, core)) {
427 continue;
429 if(!filter_by_type(*i, type)) {
430 continue;
432 if(!fullspec && !filter_by_region(*i, _region)) {
433 continue;
435 if(!fullspec && !filter_by_fileset(*i, file)) {
436 continue;
438 t = i;
440 if(!t) throw std::runtime_error("No matching core found");
441 r = detect_region(t, _region);
442 std::string bios = t->get_biosname();
443 unsigned romidx = (bios != "") ? 1 : 0;
444 unsigned pmand = 0, tmand = 0;
445 for(unsigned i = 0; i < 27; i++) {
446 if(i >= t->get_image_count())
447 continue;
448 if(file[i] != "")
449 pmand |= t->get_image_info(i).mandatory;
450 tmand |= t->get_image_info(i).mandatory;
451 romimg[i] = fileimage::image(lsnes_image_hasher, file[i], "", xlate_info(t->get_image_info(i)));
452 if(zip::file_exists(file[i] + ".xml"))
453 romxml[i] = fileimage::image(lsnes_image_hasher, file[i] + ".xml", "", get_xml_info());
455 msu1_base = zip::resolverel(file[romidx], "");
456 record_files(*this);
457 if(pmand != tmand)
458 throw std::runtime_error("Required ROM images missing");
459 rtype = t;
460 orig_region = region = r;
461 account_images();
464 bool rom_image::is_gamepak(const std::string& filename) throw(std::bad_alloc, std::runtime_error)
466 std::istream* spec = NULL;
467 try {
468 spec = &zip::openrel(filename, "");
469 std::string line;
470 std::getline(*spec, line);
471 istrip_CR(line);
472 bool ret = (line == "[GAMEPACK FILE]");
473 delete spec;
474 return ret;
475 } catch(...) {
476 delete spec;
477 return false;
481 rom_image::~rom_image()
485 void rom_image::account_images()
487 //Hack: don't account any size before main.
488 if(in_global_ctors()) return;
489 size_t memory_usage = sizeof(rom_image) + msu1_base.length() + load_filename.length();
490 for(unsigned i = 0; i < ROM_SLOT_COUNT; i++) {
491 memory_usage += (unsigned)romimg[i];
492 memory_usage += (unsigned)romxml[i];
494 tracker(memory_usage);
497 void set_hasher_callback(std::function<void(uint64_t, uint64_t)> cb)
499 lsnes_image_hasher.set_callback(cb);
502 std::map<std::string, core_type*> preferred_core;