More cleanup via initializer lists
[lsnes.git] / src / core / rom.cpp
blobf93868667863a0a01d9cebd642790b10aeff9762
1 #include "lsnes.hpp"
3 #include "core/command.hpp"
4 #include "core/dispatch.hpp"
5 #include "core/framerate.hpp"
6 #include "core/mainloop.hpp"
7 #include "core/memorymanip.hpp"
8 #include "core/misc.hpp"
9 #include "core/rom.hpp"
10 #include "core/settings.hpp"
11 #include "core/window.hpp"
12 #include "interface/cover.hpp"
13 #include "interface/romtype.hpp"
14 #include "interface/callbacks.hpp"
15 #include "library/pixfmt-rgb16.hpp"
16 #include "library/controller-data.hpp"
17 #include "library/patch.hpp"
18 #include "library/sha256.hpp"
19 #include "library/string.hpp"
20 #include "library/zip.hpp"
22 #include <stdexcept>
24 #include <sstream>
25 #include <iomanip>
26 #include <cstdint>
27 #include <set>
28 #include <boost/iostreams/categories.hpp>
29 #include <boost/iostreams/copy.hpp>
30 #include <boost/iostreams/stream.hpp>
31 #include <boost/iostreams/stream_buffer.hpp>
32 #include <boost/iostreams/filter/symmetric.hpp>
33 #include <boost/iostreams/filter/zlib.hpp>
34 #include <boost/iostreams/filtering_stream.hpp>
35 #include <boost/iostreams/device/back_inserter.hpp>
36 #include <boost/filesystem.hpp>
38 #ifdef BOOST_FILESYSTEM3
39 namespace boost_fs = boost::filesystem3;
40 #else
41 namespace boost_fs = boost::filesystem;
42 #endif
44 namespace
46 const char* null_chars = "F";
47 uint16_t null_cover_fbmem[512 * 448];
49 //Framebuffer.
50 struct framebuffer_info null_fbinfo = {
51 &_pixel_format_bgr16, //Format.
52 (char*)null_cover_fbmem, //Memory.
53 512, 448, 1024, //Physical size.
54 512, 448, 1024, //Logical size.
55 0, 0 //Offset.
58 port_index_triple sync_triple = {true, 0, 0, 0 };
60 struct interface_device_reg null_registers[] = {
61 {NULL, NULL, NULL}
64 struct _core_null : public core_core, public core_type, public core_region, public core_sysregion
66 _core_null() : core_core({{{}}}), core_type({{
67 .iname = "null",
68 .hname = "(null)",
69 .id = 9999,
70 .sysname = "System",
71 .extensions = "",
72 .bios = NULL,
73 .regions = {this},
74 .images = {},
75 .settings = {},
76 .core = this,
77 }}), core_region({{"null", "(null)", 0, 0, false, {1, 60}, {0}}}),
78 core_sysregion("null", *this, *this) {}
79 std::string c_core_identifier() { return "null core"; }
80 bool c_set_region(core_region& reg) { return true; }
81 std::pair<unsigned, unsigned> c_video_rate() { return std::make_pair(60, 1); }
82 std::pair<unsigned, unsigned> c_audio_rate() { return std::make_pair(48000, 1); }
83 std::map<std::string, std::vector<char>> c_save_sram() throw (std::bad_alloc) {
84 std::map<std::string, std::vector<char>> x;
85 return x;
87 void c_load_sram(std::map<std::string, std::vector<char>>& sram) throw (std::bad_alloc) {}
88 void c_serialize(std::vector<char>& out) { out.clear(); }
89 void c_unserialize(const char* in, size_t insize) {}
90 core_region& c_get_region() { return *this; }
91 void c_power() {}
92 void c_unload_cartridge() {}
93 std::pair<uint32_t, uint32_t> c_get_scale_factors(uint32_t width, uint32_t height) {
94 return std::make_pair(1, 1);
96 void c_install_handler() {}
97 void c_uninstall_handler() {}
98 void c_emulate() {}
99 void c_runtosave() {}
100 bool c_get_pflag() { return false; }
101 void c_set_pflag(bool pflag) {}
102 framebuffer_raw& c_draw_cover() {
103 static framebuffer_raw x(null_fbinfo);
104 for(size_t i = 0; i < sizeof(null_cover_fbmem)/sizeof(null_cover_fbmem[0]); i++)
105 null_cover_fbmem[i] = 0x0000;
106 std::string message = "NO ROM LOADED";
107 cover_render_string(null_cover_fbmem, 204, 220, message, 0xFFFF, 0x0000, 512, 448, 1024, 2);
108 return x;
110 std::string c_get_core_shortname() { return "null"; }
111 void c_pre_emulate_frame(controller_frame& cf) {}
112 void c_execute_action(unsigned id, const std::vector<interface_action_paramval>& p) {}
113 const interface_device_reg* c_get_registers() { return null_registers; }
114 int t_load_rom(core_romimage* img, std::map<std::string, std::string>& settings,
115 uint64_t secs, uint64_t subsecs)
117 return 0;
119 controller_set t_controllerconfig(std::map<std::string, std::string>& settings)
121 controller_set x;
122 x.ports.push_back(&get_default_system_port_type());
123 x.portindex.indices.push_back(sync_triple);
124 return x;
126 std::pair<uint64_t, uint64_t> t_get_bus_map() { return std::make_pair(0ULL, 0ULL); }
127 std::list<core_vma_info> t_vma_list() { return std::list<core_vma_info>(); }
128 std::set<std::string> t_srams() { return std::set<std::string>(); }
129 unsigned c_action_flags(unsigned id) { return 0; }
130 int c_reset_action(bool hard) { return -1; }
131 } core_null;
133 core_type* current_rom_type = &core_null;
134 core_region* current_region = &core_null;
136 core_type* find_core_by_extension(const std::string& ext, const std::string& tmpprefer)
138 std::string key = "ext:" + ext;
139 std::list<core_type*> possible = core_type::get_core_types();
140 core_type* fallback = NULL;
141 core_type* preferred = preferred_core.count(key) ? preferred_core[key] : NULL;
142 //Tmpprefer overrides normal preferred core.
143 if(tmpprefer != "")
144 for(auto i : possible)
145 if(i->get_iname() == tmpprefer)
146 preferred = i;
147 for(auto i : possible)
148 if(i->is_known_extension(ext)) {
149 fallback = i;
150 if(!preferred && i->get_core_shortname() == preferred_core_default)
151 return i;
152 if(i == preferred)
153 return i;
155 return fallback;
158 core_type* find_core_by_name(const std::string& name, const std::string& tmpprefer)
160 std::string key = "type:" + name;
161 std::list<core_type*> possible = core_type::get_core_types();
162 core_type* fallback = NULL;
163 core_type* preferred = preferred_core.count(key) ? preferred_core[key] : NULL;
164 //Tmpprefer overrides normal preferred core.
165 if(tmpprefer != "")
166 for(auto i : possible)
167 if(i->get_iname() == tmpprefer)
168 preferred = i;
169 for(auto i : possible)
170 if(i->get_iname() == name) {
171 fallback = i;
172 if(!preferred && i->get_core_shortname() == preferred_core_default)
173 return i;
174 if(i == preferred)
175 return i;
177 return fallback;
180 bool file_exists(const std::string& name)
182 try {
183 delete &open_file_relative(name, "");
184 return true;
185 } catch(...) {
186 return false;
191 loaded_slot::loaded_slot() throw(std::bad_alloc)
193 valid = false;
194 xml = false;
195 sha_256 = "";
196 filename_flag = false;
199 loaded_slot::loaded_slot(const std::string& filename, const std::string& base,
200 const struct core_romimage_info& imginfo, bool xml_flag) throw(std::bad_alloc, std::runtime_error)
202 unsigned headered = 0;
203 xml = xml_flag;
204 if(filename == "") {
205 valid = false;
206 sha_256 = "";
207 filename_flag = (!xml && imginfo.pass_mode);
208 return;
210 //XMLs are always loaded, no matter what.
211 if(!xml && imginfo.pass_mode) {
212 std::string _filename = filename;
213 //Translate the passed filename to absolute one.
214 _filename = resolve_file_relative(_filename, base);
215 _filename = boost_fs::absolute(boost_fs::path(_filename)).string();
216 filename_flag = true;
217 data.resize(_filename.length());
218 std::copy(_filename.begin(), _filename.end(), data.begin());
219 //Compute the SHA-256.
220 std::istream& s = open_file_relative(filename, "");
221 sha256 hash;
222 char buffer[8192];
223 size_t block;
224 while((block = s.readsome(buffer, 8192)))
225 hash.write(buffer, block);
226 sha_256 = hash.read();
227 delete &s;
228 valid = true;
229 return;
231 filename_flag = false;
232 valid = true;
233 data = read_file_relative(filename, base);
234 if(!xml && imginfo.headersize)
235 headered = ((data.size() % (2 * imginfo.headersize)) == imginfo.headersize) ? imginfo.headersize : 0;
236 if(headered && !xml) {
237 if(data.size() >= headered) {
238 memmove(&data[0], &data[headered], data.size() - headered);
239 data.resize(data.size() - headered);
240 } else {
241 data.resize(0);
244 sha_256 = sha256::hash(data);
245 if(xml) {
246 size_t osize = data.size();
247 data.resize(osize + 1);
248 data[osize] = 0;
252 void loaded_slot::patch(const std::vector<char>& patch, int32_t offset) throw(std::bad_alloc, std::runtime_error)
254 if(filename_flag)
255 throw std::runtime_error("CD images can't be patched on the fly");
256 try {
257 std::vector<char> data2 = data;
258 if(xml && valid)
259 data2.resize(data2.size() - 1);
260 data2 = do_patch_file(data2, patch, offset);
261 //Mark the slot as valid and update hash.
262 valid = true;
263 std::string new_sha256 = sha256::hash(data2);
264 if(xml) {
265 size_t osize = data2.size();
266 data2.resize(osize + 1);
267 data2[osize] = 0;
269 data = data2;
270 sha_256 = new_sha256;
271 } catch(...) {
272 throw;
276 std::pair<core_type*, core_region*> get_current_rom_info() throw()
278 return std::make_pair(current_rom_type, current_region);
281 loaded_rom::loaded_rom() throw()
283 rtype = &core_null;
284 region = orig_region = &core_null;
287 loaded_rom::loaded_rom(const std::string& file, core_type& ctype) throw(std::bad_alloc, std::runtime_error)
289 rtype = &ctype;
290 region = orig_region = &rtype->get_preferred_region();
291 unsigned romidx = 0;
292 std::string bios;
293 if((bios = ctype.get_biosname()) != "") {
294 //This thing has a BIOS.
295 romidx = 1;
296 std::string basename = lsnes_vset["firmwarepath"].str() + "/" + bios;
297 romimg[0] = loaded_slot(basename, "", ctype.get_image_info(0), false);
298 if(file_exists(basename + ".xml"))
299 romxml[0] = loaded_slot(basename + ".xml", "", ctype.get_image_info(0), true);
301 romimg[romidx] = loaded_slot(file, "", ctype.get_image_info(romidx), false);
302 if(file_exists(file + ".xml"))
303 romxml[romidx] = loaded_slot(file + ".xml", "", ctype.get_image_info(romidx), true);
304 msu1_base = resolve_file_relative(file, "");
305 return;
308 loaded_rom::loaded_rom(const std::string& file, const std::string& tmpprefer) throw(std::bad_alloc,
309 std::runtime_error)
311 std::istream& spec = open_file_relative(file, "");
312 std::string s;
313 std::getline(spec, s);
314 istrip_CR(s);
315 load_filename = file;
316 if(!spec || s != "[GAMEPACK FILE]") {
317 //This is a Raw ROM image.
318 regex_results tmp;
319 std::string ext = regex(".*\\.([^.]*)?", file, "Unknown ROM file type")[1];
320 core_type* coretype = find_core_by_extension(ext, tmpprefer);
321 if(!coretype)
322 throw std::runtime_error("Unknown ROM file type");
323 rtype = coretype;
324 region = orig_region = &rtype->get_preferred_region();
325 unsigned romidx = 0;
326 std::string bios;
327 if((bios = coretype->get_biosname()) != "") {
328 //This thing has a BIOS.
329 romidx = 1;
330 std::string basename = lsnes_vset["firmwarepath"].str() + "/" + bios;
331 romimg[0] = loaded_slot(basename, "", coretype->get_image_info(0), false);
332 if(file_exists(basename + ".xml"))
333 romxml[0] = loaded_slot(basename + ".xml", "", coretype->get_image_info(0), true);
335 romimg[romidx] = loaded_slot(file, "", coretype->get_image_info(romidx), false);
336 if(file_exists(file + ".xml"))
337 romxml[romidx] = loaded_slot(file + ".xml", "", coretype->get_image_info(romidx), true);
338 msu1_base = resolve_file_relative(file, "");
339 return;
341 std::vector<std::string> lines;
342 while(std::getline(spec, s))
343 lines.push_back(strip_CR(s));
344 std::string platname = "";
345 std::string platreg = "";
346 for(auto i : lines) {
347 regex_results tmp;
348 if(tmp = regex("type[ \t]+(.+)", i))
349 platname = tmp[1];
350 if(tmp = regex("region[ \t]+(.+)", i))
351 platreg = tmp[1];
354 //Detect type.
355 rtype = find_core_by_name(platname, tmpprefer);
356 if(!rtype)
357 (stringfmt() << "Not a valid system type '" << platname << "'").throwex();
359 //Detect region.
360 bool goodreg = false;
361 orig_region = &rtype->get_preferred_region();
362 for(auto i: rtype->get_regions())
363 if(i->get_iname() == platreg) {
364 orig_region = i;
365 goodreg = true;
367 if(!goodreg && platreg != "")
368 (stringfmt() << "Not a valid system region '" << platreg << "'").throwex();
369 region = orig_region;
371 //ROM files.
372 std::string cromimg[sizeof(romimg)/sizeof(romimg[0])];
373 std::string cromxml[sizeof(romimg)/sizeof(romimg[0])];
374 for(auto i : lines) {
375 regex_results tmp;
376 if(!(tmp = regex("(rom|xml)[ \t]+([^ \t]+)[ \t]+(.*)", i)))
377 continue;
378 size_t idxs = rtype->get_image_count();
379 size_t idx = idxs;
380 for(size_t i = 0; i < idxs; i++)
381 if(rtype->get_image_info(i).iname == tmp[2])
382 idx = i;
383 if(idx == idxs)
384 (stringfmt() << "Not a valid ROM name '" << tmp[2] << "'").throwex();
385 if(tmp[1] == "rom")
386 cromimg[idx] = tmp[3];
387 else
388 cromxml[idx] = tmp[3];
391 //Check ROMs.
392 unsigned mask1 = 0, mask2 = 0;
393 for(size_t i = 0; i < rtype->get_image_count(); i++) {
394 auto ii = rtype->get_image_info(i);
395 mask1 |= ii.mandatory;
396 if(cromimg[i] != "")
397 mask2 |= ii.mandatory;
398 if(cromimg[i] == "" && cromxml[i] != "") {
399 messages << "WARNING: Slot " << ii.iname << ": XML without ROM." << std::endl;
400 cromxml[i] = "";
403 if(mask1 != mask2)
404 throw std::runtime_error("Required ROM missing");
406 //Load ROMs.
407 for(size_t i = 0; i < rtype->get_image_count(); i++) {
408 romimg[i] = loaded_slot(cromimg[i], file, rtype->get_image_info(i), false);
409 romxml[i] = loaded_slot(cromxml[i], file, rtype->get_image_info(i), true);
412 //Patch ROMs.
413 for(auto i : lines) {
414 regex_results tmp;
415 if(!(tmp = regex("patch([+-][0-9]+)?[ \t]+([^ \t]+)[ \t]+(.*)", i)))
416 continue;
417 size_t idxs = rtype->get_image_count();
418 size_t idx = idxs;
419 for(size_t i = 0; i < idxs; i++)
420 if(rtype->get_image_info(i).iname == tmp[2])
421 idx = i;
422 if(idx == idxs)
423 (stringfmt() << "Not a valid ROM name '" << tmp[2] << "'").throwex();
424 int32_t offset = 0;
425 if(tmp[1] != "")
426 offset = parse_value<int32_t>(tmp[1]);
427 romimg[idx].patch(read_file_relative(tmp[3], file), offset);
430 //MSU-1 base.
431 if(cromimg[1] != "")
432 msu1_base = resolve_file_relative(cromimg[1], file);
433 else
434 msu1_base = resolve_file_relative(cromimg[0], file);
437 void loaded_rom::load(std::map<std::string, std::string>& settings, uint64_t rtc_sec, uint64_t rtc_subsec)
438 throw(std::bad_alloc, std::runtime_error)
440 core_core* old_core = current_rom_type->get_core();
441 current_rom_type = &core_null;
442 if(!orig_region && rtype != &core_null)
443 orig_region = &rtype->get_preferred_region();
444 if(!region)
445 region = orig_region;
446 if(rtype && !orig_region->compatible_with(*region))
447 throw std::runtime_error("Trying to force incompatible region");
448 if(rtype && !rtype->set_region(*region))
449 throw std::runtime_error("Trying to force unknown region");
451 core_romimage images[sizeof(romimg)/sizeof(romimg[0])];
452 for(size_t i = 0; i < sizeof(romimg)/sizeof(romimg[0]); i++) {
453 images[i].markup = (const char*)romxml[i];
454 images[i].data = (const unsigned char*)romimg[i];
455 images[i].size = (size_t)romimg[i];
457 if(!rtype->load(images, settings, rtc_sec, rtc_subsec))
458 throw std::runtime_error("Can't load cartridge ROM");
460 region = &rtype->get_region();
461 rtype->power();
462 auto nominal_fps = rtype->get_video_rate();
463 auto nominal_hz = rtype->get_audio_rate();
464 set_nominal_framerate(1.0 * nominal_fps.first / nominal_fps.second);
465 information_dispatch::do_sound_rate(nominal_hz.first, nominal_hz.second);
466 current_rom_type = rtype;
467 current_region = region;
468 current_romfile = load_filename;
469 //If core changes, unload the cartridge.
470 if(old_core != current_rom_type->get_core())
471 try { old_core->unload_cartridge(); } catch(...) {}
472 refresh_cart_mappings();
475 std::map<std::string, std::vector<char>> load_sram_commandline(const std::vector<std::string>& cmdline)
476 throw(std::bad_alloc, std::runtime_error)
478 std::map<std::string, std::vector<char>> ret;
479 regex_results opt;
480 for(auto i : cmdline) {
481 if(opt = regex("--continue=(.+)", i)) {
482 zip_reader r(opt[1]);
483 for(auto j : r) {
484 auto sramname = regex("sram\\.(.*)", j);
485 if(!sramname)
486 continue;
487 std::istream& x = r[j];
488 try {
489 std::vector<char> out;
490 boost::iostreams::back_insert_device<std::vector<char>> rd(out);
491 boost::iostreams::copy(x, rd);
492 delete &x;
493 ret[sramname[1]] = out;
494 } catch(...) {
495 delete &x;
496 throw;
499 continue;
500 } else if(opt = regex("--sram-([^=]+)=(.+)", i)) {
501 try {
502 ret[opt[1]] = read_file_relative(opt[2], "");
503 } catch(std::bad_alloc& e) {
504 throw;
505 } catch(std::runtime_error& e) {
506 throw std::runtime_error("Can't load SRAM '" + opt[1] + "': " + e.what());
510 return ret;
513 std::vector<char> loaded_rom::save_core_state(bool nochecksum) throw(std::bad_alloc)
515 std::vector<char> ret;
516 rtype->serialize(ret);
517 if(nochecksum)
518 return ret;
519 size_t offset = ret.size();
520 unsigned char tmp[32];
521 sha256::hash(tmp, ret);
522 ret.resize(offset + 32);
523 memcpy(&ret[offset], tmp, 32);
524 return ret;
527 void loaded_rom::load_core_state(const std::vector<char>& buf, bool nochecksum) throw(std::runtime_error)
529 if(nochecksum) {
530 rtype->unserialize(&buf[0], buf.size());
531 return;
534 if(buf.size() < 32)
535 throw std::runtime_error("Savestate corrupt");
536 unsigned char tmp[32];
537 sha256::hash(tmp, reinterpret_cast<const uint8_t*>(&buf[0]), buf.size() - 32);
538 if(memcmp(tmp, &buf[buf.size() - 32], 32))
539 throw std::runtime_error("Savestate corrupt");
540 rtype->unserialize(&buf[0], buf.size() - 32);;
543 std::map<std::string, core_type*> preferred_core;
544 std::string preferred_core_default;
545 std::string current_romfile;