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"
12 fileimage::image
rom_image::null_img
;
13 fileimage::hash lsnes_image_hasher
;
14 rom_image
rom_image_handle::null_img
;
18 const char* romimage_id
= "ROM images";
19 core_type
* prompt_core_fallback(const std::vector
<core_type
*>& choices
)
21 if(choices
.size() == 0)
23 if(choices
.size() == 1)
27 req
.core_guessed
= false;
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;
35 graphics_driver_request_rom(req
);
37 throw std::runtime_error("Canceled ROM loading");
38 if(req
.selected
< choices
.size())
39 return choices
[req
.selected
];
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.
51 for(auto i
: possible
)
52 if(i
->get_core_identifier() == tmpprefer
)
54 for(auto i
: possible
)
55 if(i
->is_known_extension(ext
)) {
56 fallbacks
.push_back(i
);
60 core_type
* fallback
= prompt_core_fallback(fallbacks
);
61 if(!fallback
) throw std::runtime_error("No core available to load the ROM");
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.
73 for(auto i
: possible
)
74 if(i
->get_iname() == tmpprefer
)
76 for(auto i
: possible
)
77 if(i
->get_iname() == name
) {
78 fallbacks
.push_back(i
);
82 core_type
* fallback
= prompt_core_fallback(fallbacks
);
83 if(!fallback
) throw std::runtime_error("No core available to load the ROM");
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
)
101 for(auto i
: ctype
.get_regions())
102 if(i
->get_iname() == region
)
107 bool filter_by_extension(core_type
& ctype
, const std::string
& file
)
109 regex_results tmp
= regex(".*\\.([^.]*)", file
);
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
] != "")
122 auto s
= ctype
.get_image_info(i
);
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
)
136 if(!r
&& region
!= "")
137 (stringfmt() << "Not a valid system region '" << region
<< "'").throwex();
138 if(!r
) r
= &t
->get_preferred_region(); //Default region.
142 struct fileimage::image::info
get_xml_info()
144 fileimage::image::info i
;
145 i
.type
= fileimage::image::info::IT_MARKUP
;
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
;
159 void record_files(rom_image
& rom
)
161 for(unsigned i
= 0; i
< ROM_SLOT_COUNT
; i
++) {
163 auto& j
= rom
.get_image(i
, false);
164 record_filehash(j
.filename
, j
.stripped
, j
.sha_256
.read());
167 auto& j
= rom
.get_image(i
, true);
168 record_filehash(j
.filename
, j
.stripped
, j
.sha_256
.read());
174 void rom_image::setup_region(core_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();
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))
192 orig_region
= &rtype
->get_preferred_region();
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.
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
, "");
214 throw std::runtime_error("Required ROM images missing");
219 rom_image::rom_image(const std::string
& file
, const std::string
& tmpprefer
) throw(std::bad_alloc
,
221 : tracker(memtracker::singleton(), romimage_id
, sizeof(*this))
223 std::istream
& spec
= zip::openrel(file
, "");
225 std::getline(spec
, s
);
227 if(!spec
|| s
!= "[GAMEPACK FILE]") {
228 delete &spec
; //Close the file!
229 //This is a Raw ROM image.
231 std::string ext
= regex(".*\\.([^.]*)?", file
, "Can't read file extension")[1];
232 core_type
* coretype
= find_core_by_extension(ext
, tmpprefer
);
234 (stringfmt() << "Extension '" << ext
<< "' unknown").throwex();
236 region
= orig_region
= &rtype
->get_preferred_region();
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.
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", "",
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
, "");
261 throw std::runtime_error("Required ROM images missing");
265 load_bundle(file
, spec
, tmpprefer
);
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
)
273 load_filename
= file
;
274 std::vector
<std::string
> lines
;
275 while(std::getline(spec
, s
))
276 lines
.push_back(strip_CR(s
));
278 std::string platname
= "";
279 std::string platreg
= "";
280 for(auto i
: lines
) {
282 if(tmp
= regex("type[ \t]+(.+)", i
))
284 if(tmp
= regex("region[ \t]+(.+)", i
))
289 rtype
= find_core_by_name(platname
, tmpprefer
);
291 (stringfmt() << "Not a valid system type '" << platname
<< "'").throwex();
294 bool goodreg
= false;
295 orig_region
= &rtype
->get_preferred_region();
296 for(auto i
: rtype
->get_regions())
297 if(i
->get_iname() == platreg
) {
301 if(!goodreg
&& platreg
!= "")
302 (stringfmt() << "Not a valid system region '" << platreg
<< "'").throwex();
303 region
= orig_region
;
306 std::string cromimg
[ROM_SLOT_COUNT
];
307 std::string cromxml
[ROM_SLOT_COUNT
];
308 for(auto i
: lines
) {
310 if(!(tmp
= regex("(rom|xml)[ \t]+([^ \t]+)[ \t]+(.*)", i
)))
312 size_t idxs
= rtype
->get_image_count();
314 for(size_t i
= 0; i
< idxs
; i
++)
315 if(rtype
->get_image_info(i
).iname
== tmp
[2])
318 (stringfmt() << "Not a valid ROM name '" << tmp
[2] << "'").throwex();
320 cromimg
[idx
] = tmp
[3];
322 cromxml
[idx
] = tmp
[3];
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
;
331 mask2
|= ii
.mandatory
;
332 if(cromimg
[i
] == "" && cromxml
[i
] != "") {
333 messages
<< "WARNING: Slot " << ii
.iname
<< ": XML without ROM." << std::endl
;
338 throw std::runtime_error("Required ROM missing");
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.
349 for(auto i
: lines
) {
351 if(!(tmp
= regex("patch([+-][0-9]+)?[ \t]+([^ \t]+)[ \t]+(.*)", i
)))
353 size_t idxs
= rtype
->get_image_count();
355 for(size_t i
= 0; i
< idxs
; i
++)
356 if(rtype
->get_image_info(i
).iname
== tmp
[2])
359 (stringfmt() << "Not a valid ROM name '" << tmp
[2] << "'").throwex();
362 offset
= parse_value
<int32_t>(tmp
[1]);
363 romimg
[idx
].patch(zip::readrel(tmp
[3], file
), offset
);
367 if(rtype
->get_biosname() != "")
368 msu1_base
= zip::resolverel(cromimg
[1], file
);
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))
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
))
383 if(!filter_by_type(*i
, type
))
385 if(!fullspec
&& !filter_by_region(*i
, _region
))
387 if(!fullspec
&& !filter_by_extension(*i
, file
))
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;
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
, "");
412 throw std::runtime_error("Required ROM images missing");
414 orig_region
= region
= r
;
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))
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
)) {
429 if(!filter_by_type(*i
, type
)) {
432 if(!fullspec
&& !filter_by_region(*i
, _region
)) {
435 if(!fullspec
&& !filter_by_fileset(*i
, file
)) {
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())
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
], "");
458 throw std::runtime_error("Required ROM images missing");
460 orig_region
= region
= r
;
464 bool rom_image::is_gamepak(const std::string
& filename
) throw(std::bad_alloc
, std::runtime_error
)
466 std::istream
* spec
= NULL
;
468 spec
= &zip::openrel(filename
, "");
470 std::getline(*spec
, line
);
472 bool ret
= (line
== "[GAMEPACK FILE]");
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
;