Cleanup port defintions
[lsnes.git] / src / core / rom.cpp
blob6403a49f8f04a41a73bf7571b6ec12d932f2e8ae
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 "library/pixfmt-rgb16.hpp"
15 #include "library/controller-data.hpp"
16 #include "library/patch.hpp"
17 #include "library/sha256.hpp"
18 #include "library/string.hpp"
19 #include "library/zip.hpp"
21 #include <stdexcept>
23 #include <sstream>
24 #include <iomanip>
25 #include <cstdint>
26 #include <set>
27 #include <boost/iostreams/categories.hpp>
28 #include <boost/iostreams/copy.hpp>
29 #include <boost/iostreams/stream.hpp>
30 #include <boost/iostreams/stream_buffer.hpp>
31 #include <boost/iostreams/filter/symmetric.hpp>
32 #include <boost/iostreams/filter/zlib.hpp>
33 #include <boost/iostreams/filtering_stream.hpp>
34 #include <boost/iostreams/device/back_inserter.hpp>
35 #include <boost/filesystem.hpp>
37 #ifdef BOOST_FILESYSTEM3
38 namespace boost_fs = boost::filesystem3;
39 #else
40 namespace boost_fs = boost::filesystem;
41 #endif
43 namespace
45 const char* null_chars = "F";
46 uint16_t null_cover_fbmem[512 * 448];
48 //Framebuffer.
49 struct framebuffer_info null_fbinfo = {
50 &_pixel_format_bgr16, //Format.
51 (char*)null_cover_fbmem, //Memory.
52 512, 448, 1024, //Physical size.
53 512, 448, 1024, //Logical size.
54 0, 0 //Offset.
57 port_type* port_types[] = {NULL};
58 port_index_triple sync_triple = {true, 0, 0, 0 };
60 core_setting_group null_settings;
62 unsigned null_compatible[] = {0, UINT_MAX};
63 struct core_region_params _null_region = {
64 "null", "(null)", 0, 0, false, {1, 60}, null_compatible
66 core_region null_region(_null_region);
67 core_region* null_regions[] = {&null_region, NULL};
68 core_romimage_info* null_images[] = {NULL};
69 core_core_params _core_null = {
70 .core_identifier = []() -> std::string { return "null core"; },
71 .set_region = [](core_region& reg) -> bool { return true; },
72 .video_rate = []() -> std::pair<unsigned, unsigned> { return std::make_pair(60, 1); },
73 .audio_rate = []() -> std::pair<unsigned, unsigned> { return std::make_pair(48000, 1); },
74 .snes_rate = NULL,
75 .save_sram = []() -> std::map<std::string, std::vector<char>> {
76 std::map<std::string, std::vector<char>> x;
77 return x;
79 .load_sram = [](std::map<std::string, std::vector<char>>& sram) -> void {},
80 .serialize = [](std::vector<char>& out) -> void { out.clear(); },
81 .unserialize = [](const char* in, size_t insize) -> void {},
82 .get_region = []() -> core_region& { return null_region; },
83 .power = []() -> void {},
84 .unload_cartridge = []() -> void {},
85 .get_scale_factors = [](uint32_t width, uint32_t height) -> std::pair<uint32_t, uint32_t> {
86 return std::make_pair(1, 1);
88 .install_handler = []() -> void {},
89 .uninstall_handler = []() -> void {},
90 .emulate = []() -> void {},
91 .runtosave = []() -> void {},
92 .get_pflag = []() -> bool { return false; },
93 .set_pflag = [](bool pflag) -> void {},
94 .request_reset = [](long delay, bool hard) -> void {},
95 .port_types = port_types,
96 .draw_cover = []() -> framebuffer_raw& {
97 static framebuffer_raw x(null_fbinfo);
98 for(size_t i = 0; i < sizeof(null_cover_fbmem)/sizeof(null_cover_fbmem[0]); i++)
99 null_cover_fbmem[i] = 0x0000;
100 std::string message = "NO ROM LOADED";
101 cover_render_string(null_cover_fbmem, 204, 220, message, 0xFFFF, 0x0000, 512, 448, 1024, 2);
102 return x;
104 .get_core_shortname = []() -> std::string { return "null"; },
105 .pre_emulate_frame = [](controller_frame& cf) -> void {}
107 core_core core_null(_core_null);
109 core_type_params _type_null = {
110 "null", "(null)", 9999, 0,
111 [](core_romimage* img, std::map<std::string, std::string>& settings,
112 uint64_t secs, uint64_t subsecs) -> int {
113 return 0;
115 [](std::map<std::string, std::string>& settings) -> controller_set {
116 controller_set x;
117 x.ports.push_back(&get_default_system_port_type());
118 x.portindex.indices.push_back(sync_triple);
119 return x;
121 "", NULL, null_regions, null_images, &null_settings, &core_null,
122 []() -> std::pair<uint64_t, uint64_t> { return std::make_pair(0ULL, 0ULL); },
123 []() -> std::list<core_vma_info> {
124 std::list<core_vma_info> x;
125 return x;
127 []() -> std::set<std::string> {
128 std::set<std::string> x;
129 return x;
132 core_type type_null(_type_null);
133 core_sysregion sysregion_null("null", type_null, null_region);
135 core_type* current_rom_type = &type_null;
136 core_region* current_region = &null_region;
138 core_type* find_core_by_extension(const std::string& ext)
140 std::string key = "ext:" + ext;
141 std::list<core_type*> possible = core_type::get_core_types();
142 core_type* fallback = NULL;
143 core_type* preferred = preferred_core.count(key) ? preferred_core[key] : NULL;
144 for(auto i : possible)
145 if(i->is_known_extension(ext)) {
146 fallback = i;
147 if(!preferred && i->get_core_shortname() == preferred_core_default)
148 return i;
149 if(i == preferred)
150 return i;
152 return fallback;
155 core_type* find_core_by_name(const std::string& name)
157 std::string key = "type:" + name;
158 std::list<core_type*> possible = core_type::get_core_types();
159 core_type* fallback = NULL;
160 core_type* preferred = preferred_core.count(key) ? preferred_core[key] : NULL;
161 for(auto i : possible)
162 if(i->get_iname() == name) {
163 fallback = i;
164 if(!preferred && i->get_core_shortname() == preferred_core_default)
165 return i;
166 if(i == preferred)
167 return i;
169 return fallback;
172 bool file_exists(const std::string& name)
174 try {
175 delete &open_file_relative(name, "");
176 return true;
177 } catch(...) {
178 return false;
183 loaded_slot::loaded_slot() throw(std::bad_alloc)
185 valid = false;
186 xml = false;
187 sha_256 = "";
188 filename_flag = false;
191 loaded_slot::loaded_slot(const std::string& filename, const std::string& base,
192 const struct core_romimage_info& imginfo, bool xml_flag) throw(std::bad_alloc, std::runtime_error)
194 unsigned headered = 0;
195 xml = xml_flag;
196 if(filename == "") {
197 valid = false;
198 sha_256 = "";
199 filename_flag = (!xml && imginfo.pass_mode);
200 return;
202 //XMLs are always loaded, no matter what.
203 if(!xml && imginfo.pass_mode) {
204 std::string _filename = filename;
205 //Translate the passed filename to absolute one.
206 _filename = resolve_file_relative(_filename, base);
207 _filename = boost_fs::absolute(boost_fs::path(_filename)).string();
208 filename_flag = true;
209 data.resize(_filename.length());
210 std::copy(_filename.begin(), _filename.end(), data.begin());
211 //Compute the SHA-256.
212 std::istream& s = open_file_relative(filename, "");
213 sha256 hash;
214 char buffer[8192];
215 size_t block;
216 while((block = s.readsome(buffer, 8192)))
217 hash.write(buffer, block);
218 sha_256 = hash.read();
219 delete &s;
220 valid = true;
221 return;
223 filename_flag = false;
224 valid = true;
225 data = read_file_relative(filename, base);
226 if(!xml && imginfo.headersize)
227 headered = ((data.size() % (2 * imginfo.headersize)) == imginfo.headersize) ? imginfo.headersize : 0;
228 if(headered && !xml) {
229 if(data.size() >= headered) {
230 memmove(&data[0], &data[headered], data.size() - headered);
231 data.resize(data.size() - headered);
232 } else {
233 data.resize(0);
236 sha_256 = sha256::hash(data);
237 if(xml) {
238 size_t osize = data.size();
239 data.resize(osize + 1);
240 data[osize] = 0;
244 void loaded_slot::patch(const std::vector<char>& patch, int32_t offset) throw(std::bad_alloc, std::runtime_error)
246 if(filename_flag)
247 throw std::runtime_error("CD images can't be patched on the fly");
248 try {
249 std::vector<char> data2 = data;
250 if(xml && valid)
251 data2.resize(data2.size() - 1);
252 data2 = do_patch_file(data2, patch, offset);
253 //Mark the slot as valid and update hash.
254 valid = true;
255 std::string new_sha256 = sha256::hash(data2);
256 if(xml) {
257 size_t osize = data2.size();
258 data2.resize(osize + 1);
259 data2[osize] = 0;
261 data = data2;
262 sha_256 = new_sha256;
263 } catch(...) {
264 throw;
268 std::pair<core_type*, core_region*> get_current_rom_info() throw()
270 return std::make_pair(current_rom_type, current_region);
273 loaded_rom::loaded_rom() throw()
275 rtype = &type_null;
276 region = orig_region = &null_region;
279 loaded_rom::loaded_rom(const std::string& file) throw(std::bad_alloc, std::runtime_error)
281 std::istream& spec = open_file_relative(file, "");
282 std::string s;
283 std::getline(spec, s);
284 istrip_CR(s);
285 load_filename = file;
286 if(!spec || s != "[GAMEPACK FILE]") {
287 //This is a Raw ROM image.
288 regex_results tmp;
289 std::string ext = regex(".*\\.([^.]*)?", file, "Unknown ROM file type")[1];
290 core_type* coretype = find_core_by_extension(ext);
291 if(!coretype)
292 throw std::runtime_error("Unknown ROM file type");
293 rtype = coretype;
294 region = orig_region = &rtype->get_preferred_region();
295 unsigned romidx = 0;
296 std::string bios;
297 if((bios = coretype->get_biosname()) != "") {
298 //This thing has a BIOS.
299 romidx = 1;
300 std::string basename = lsnes_vset["firmwarepath"].str() + "/" + bios;
301 romimg[0] = loaded_slot(basename, "", coretype->get_image_info(0), false);
302 if(file_exists(basename + ".xml"))
303 romxml[0] = loaded_slot(basename + ".xml", "", coretype->get_image_info(0), true);
305 romimg[romidx] = loaded_slot(file, "", coretype->get_image_info(romidx), false);
306 if(file_exists(file + ".xml"))
307 romxml[romidx] = loaded_slot(file + ".xml", "", coretype->get_image_info(romidx), true);
308 msu1_base = resolve_file_relative(file, "");
309 return;
311 std::vector<std::string> lines;
312 while(std::getline(spec, s))
313 lines.push_back(strip_CR(s));
314 std::string platname = "";
315 std::string platreg = "";
316 for(auto i : lines) {
317 regex_results tmp;
318 if(tmp = regex("type[ \t]+(.+)", i))
319 platname = tmp[1];
320 if(tmp = regex("region[ \t]+(.+)", i))
321 platreg = tmp[1];
324 //Detect type.
325 rtype = find_core_by_name(platname);
326 if(!rtype)
327 (stringfmt() << "Not a valid system type '" << platname << "'").throwex();
329 //Detect region.
330 bool goodreg = false;
331 orig_region = &rtype->get_preferred_region();
332 for(auto i: rtype->get_regions())
333 if(i->get_iname() == platreg) {
334 orig_region = i;
335 goodreg = true;
337 if(!goodreg && platreg != "")
338 (stringfmt() << "Not a valid system region '" << platreg << "'").throwex();
339 region = orig_region;
341 //ROM files.
342 std::string cromimg[sizeof(romimg)/sizeof(romimg[0])];
343 std::string cromxml[sizeof(romimg)/sizeof(romimg[0])];
344 for(auto i : lines) {
345 regex_results tmp;
346 if(!(tmp = regex("(rom|xml)[ \t]+([^ \t]+)[ \t]+(.*)", i)))
347 continue;
348 size_t idxs = rtype->get_image_count();
349 size_t idx = idxs;
350 for(size_t i = 0; i < idxs; i++)
351 if(rtype->get_image_info(i).iname == tmp[2])
352 idx = i;
353 if(idx == idxs)
354 (stringfmt() << "Not a valid ROM name '" << tmp[2] << "'").throwex();
355 if(tmp[1] == "rom")
356 cromimg[idx] = tmp[3];
357 else
358 cromxml[idx] = tmp[3];
361 //Check ROMs.
362 unsigned mask1 = 0, mask2 = 0;
363 for(size_t i = 0; i < rtype->get_image_count(); i++) {
364 auto ii = rtype->get_image_info(i);
365 mask1 |= ii.mandatory;
366 if(cromimg[i] != "")
367 mask2 |= ii.mandatory;
368 if(cromimg[i] == "" && cromxml[i] != "") {
369 messages << "WARNING: Slot " << ii.iname << ": XML without ROM." << std::endl;
370 cromxml[i] = "";
373 if(mask1 != mask2)
374 throw std::runtime_error("Required ROM missing");
376 //Load ROMs.
377 for(size_t i = 0; i < rtype->get_image_count(); i++) {
378 romimg[i] = loaded_slot(cromimg[i], file, rtype->get_image_info(i), false);
379 romxml[i] = loaded_slot(cromxml[i], file, rtype->get_image_info(i), true);
382 //Patch ROMs.
383 for(auto i : lines) {
384 regex_results tmp;
385 if(!(tmp = regex("patch([+-][0-9]+)?[ \t]+([^ \t]+)[ \t]+(.*)", i)))
386 continue;
387 size_t idxs = rtype->get_image_count();
388 size_t idx = idxs;
389 for(size_t i = 0; i < idxs; i++)
390 if(rtype->get_image_info(i).iname == tmp[2])
391 idx = i;
392 if(idx == idxs)
393 (stringfmt() << "Not a valid ROM name '" << tmp[2] << "'").throwex();
394 int32_t offset = 0;
395 if(tmp[1] != "")
396 offset = parse_value<int32_t>(tmp[1]);
397 romimg[idx].patch(read_file_relative(tmp[3], file), offset);
400 //MSU-1 base.
401 if(cromimg[1] != "")
402 msu1_base = resolve_file_relative(cromimg[1], file);
403 else
404 msu1_base = resolve_file_relative(cromimg[0], file);
407 void loaded_rom::load(std::map<std::string, std::string>& settings, uint64_t rtc_sec, uint64_t rtc_subsec)
408 throw(std::bad_alloc, std::runtime_error)
410 current_rom_type = &type_null;
411 if(!orig_region && rtype != &type_null)
412 orig_region = &rtype->get_preferred_region();
413 if(!region)
414 region = orig_region;
415 if(rtype && !orig_region->compatible_with(*region))
416 throw std::runtime_error("Trying to force incompatible region");
417 if(rtype && !rtype->set_region(*region))
418 throw std::runtime_error("Trying to force unknown region");
420 core_romimage images[sizeof(romimg)/sizeof(romimg[0])];
421 for(size_t i = 0; i < sizeof(romimg)/sizeof(romimg[0]); i++) {
422 images[i].markup = (const char*)romxml[i];
423 images[i].data = (const unsigned char*)romimg[i];
424 images[i].size = (size_t)romimg[i];
426 if(!rtype->load(images, settings, rtc_sec, rtc_subsec))
427 throw std::runtime_error("Can't load cartridge ROM");
429 region = &rtype->get_region();
430 rtype->power();
431 auto nominal_fps = rtype->get_video_rate();
432 auto nominal_hz = rtype->get_audio_rate();
433 set_nominal_framerate(1.0 * nominal_fps.first / nominal_fps.second);
434 information_dispatch::do_sound_rate(nominal_hz.first, nominal_hz.second);
435 current_rom_type = rtype;
436 current_region = region;
437 refresh_cart_mappings();
440 std::map<std::string, std::vector<char>> load_sram_commandline(const std::vector<std::string>& cmdline)
441 throw(std::bad_alloc, std::runtime_error)
443 std::map<std::string, std::vector<char>> ret;
444 regex_results opt;
445 for(auto i : cmdline) {
446 if(opt = regex("--continue=(.+)", i)) {
447 zip_reader r(opt[1]);
448 for(auto j : r) {
449 auto sramname = regex("sram\\.(.*)", j);
450 if(!sramname)
451 continue;
452 std::istream& x = r[j];
453 try {
454 std::vector<char> out;
455 boost::iostreams::back_insert_device<std::vector<char>> rd(out);
456 boost::iostreams::copy(x, rd);
457 delete &x;
458 ret[sramname[1]] = out;
459 } catch(...) {
460 delete &x;
461 throw;
464 continue;
465 } else if(opt = regex("--sram-([^=]+)=(.+)", i)) {
466 try {
467 ret[opt[1]] = read_file_relative(opt[2], "");
468 } catch(std::bad_alloc& e) {
469 throw;
470 } catch(std::runtime_error& e) {
471 throw std::runtime_error("Can't load SRAM '" + opt[1] + "': " + e.what());
475 return ret;
478 std::vector<char> loaded_rom::save_core_state(bool nochecksum) throw(std::bad_alloc)
480 std::vector<char> ret;
481 rtype->serialize(ret);
482 if(nochecksum)
483 return ret;
484 size_t offset = ret.size();
485 unsigned char tmp[32];
486 sha256::hash(tmp, ret);
487 ret.resize(offset + 32);
488 memcpy(&ret[offset], tmp, 32);
489 return ret;
492 void loaded_rom::load_core_state(const std::vector<char>& buf, bool nochecksum) throw(std::runtime_error)
494 if(nochecksum) {
495 rtype->unserialize(&buf[0], buf.size());
496 return;
499 if(buf.size() < 32)
500 throw std::runtime_error("Savestate corrupt");
501 unsigned char tmp[32];
502 sha256::hash(tmp, reinterpret_cast<const uint8_t*>(&buf[0]), buf.size() - 32);
503 if(memcmp(tmp, &buf[buf.size() - 32], 32))
504 throw std::runtime_error("Savestate corrupt");
505 rtype->unserialize(&buf[0], buf.size() - 32);;
508 std::map<std::string, core_type*> preferred_core;
509 std::string preferred_core_default;