Make the SNES rate specials methods
[lsnes.git] / src / core / rom.cpp
blob26b32e5d36a31cd0d3a9dbc14b8b083a55d50187
1 #include "lsnes.hpp"
2 #include "core/emucore.hpp"
4 #include "core/command.hpp"
5 #include "core/dispatch.hpp"
6 #include "core/framerate.hpp"
7 #include "core/mainloop.hpp"
8 #include "core/memorymanip.hpp"
9 #include "core/misc.hpp"
10 #include "core/rom.hpp"
11 #include "core/settings.hpp"
12 #include "core/window.hpp"
13 #include "library/portfn.hpp"
14 #include "library/patch.hpp"
15 #include "library/sha256.hpp"
16 #include "library/string.hpp"
17 #include "library/zip.hpp"
19 #include <stdexcept>
21 #include <sstream>
22 #include <iomanip>
23 #include <cstdint>
24 #include <set>
25 #include <boost/iostreams/categories.hpp>
26 #include <boost/iostreams/copy.hpp>
27 #include <boost/iostreams/stream.hpp>
28 #include <boost/iostreams/stream_buffer.hpp>
29 #include <boost/iostreams/filter/symmetric.hpp>
30 #include <boost/iostreams/filter/zlib.hpp>
31 #include <boost/iostreams/filtering_stream.hpp>
32 #include <boost/iostreams/device/back_inserter.hpp>
33 #include <boost/filesystem.hpp>
35 #ifdef BOOST_FILESYSTEM3
36 namespace boost_fs = boost::filesystem3;
37 #else
38 namespace boost_fs = boost::filesystem;
39 #endif
41 namespace
43 const char* null_chars = "F";
45 port_controller_button* null_buttons[] = {};
46 port_controller simple_controller = {"(system)", "system", 0, null_buttons};
47 port_controller* simple_controllers[] = {&simple_controller};
48 port_controller_set simple_port = {1, simple_controllers};
50 struct porttype_null : public port_type
52 porttype_null() : port_type("null", "null", 999997, 1)
54 write = generic_port_write<1, 0, 1>;
55 read = generic_port_read<1, 0, 1>;
56 display = generic_port_display<1, 0, 1, &null_chars>;
57 serialize = generic_port_serialize<1, 0, 1, &null_chars>;
58 deserialize = generic_port_deserialize<1, 0, 1>;
59 legal = generic_port_legal<1>;
60 controller_info = &simple_port;
61 used_indices = generic_used_indices<1, 1>;
63 } pnull;
65 port_index_triple sync_triple = {true, 0, 0, 0, false };
67 controller_set null_controllerconfig(std::map<std::string, std::string>& settings)
69 controller_set x;
70 x.ports.push_back(&pnull);
71 x.portindex.indices.push_back(sync_triple);
72 return x;
75 std::pair<unsigned, unsigned> videorate_null() { return std::make_pair(60, 1); }
76 std::pair<unsigned, unsigned> audiorate_null() { return std::make_pair(48000, 1); }
78 bool set_region_null(core_region& reg)
80 return true;
83 int load_rom_null(core_romimage* img, std::map<std::string, std::string>& settings,
84 uint64_t secs, uint64_t subsecs)
86 return 0;
89 core_setting_group null_settings;
91 unsigned null_compatible[] = {0, UINT_MAX};
92 struct core_region_params _null_region = {
93 "null", "(null)", 0, 0, false, {1, 60, 16666666, 40}, null_compatible
95 core_region null_region(_null_region);
96 core_region* null_regions[] = {&null_region, NULL};
97 core_romimage_info* null_images[] = {NULL};
98 core_type_params _type_null = {
99 "null", "(null)", 9999, 0, load_rom_null, null_controllerconfig,
100 "", NULL, null_regions, null_images, &null_settings, set_region_null,
101 videorate_null, audiorate_null, NULL
103 core_type type_null(_type_null);
104 core_sysregion sysregion_null("null", type_null, null_region);
106 core_type* current_rom_type = &type_null;
107 core_region* current_region = &null_region;
110 loaded_slot::loaded_slot() throw(std::bad_alloc)
112 valid = false;
113 xml = false;
114 sha_256 = "";
115 filename_flag = false;
118 loaded_slot::loaded_slot(const std::string& filename, const std::string& base,
119 const struct core_romimage_info& imginfo, bool xml_flag) throw(std::bad_alloc, std::runtime_error)
121 unsigned headered = 0;
122 xml = xml_flag;
123 if(filename == "") {
124 valid = false;
125 sha_256 = "";
126 filename_flag = (!xml && imginfo.pass_mode);
127 return;
129 //XMLs are always loaded, no matter what.
130 if(!xml && imginfo.pass_mode) {
131 std::string _filename = filename;
132 //Translate the passed filename to absolute one.
133 _filename = resolve_file_relative(_filename, base);
134 _filename = boost_fs::absolute(boost_fs::path(_filename)).string();
135 filename_flag = true;
136 data.resize(_filename.length());
137 std::copy(_filename.begin(), _filename.end(), data.begin());
138 //Compute the SHA-256.
139 std::istream& s = open_file_relative(filename, "");
140 sha256 hash;
141 char buffer[8192];
142 size_t block;
143 while(block = s.readsome(buffer, 8192))
144 hash.write(buffer, block);
145 sha_256 = hash.read();
146 delete &s;
147 valid = true;
148 return;
150 filename_flag = false;
151 valid = true;
152 data = read_file_relative(filename, base);
153 if(!xml && imginfo.headersize)
154 headered = ((data.size() % (2 * imginfo.headersize)) == imginfo.headersize) ? imginfo.headersize : 0;
155 if(headered && !xml) {
156 if(data.size() >= headered) {
157 memmove(&data[0], &data[headered], data.size() - headered);
158 data.resize(data.size() - headered);
159 } else {
160 data.resize(0);
163 sha_256 = sha256::hash(data);
164 if(xml) {
165 size_t osize = data.size();
166 data.resize(osize + 1);
167 data[osize] = 0;
171 void loaded_slot::patch(const std::vector<char>& patch, int32_t offset) throw(std::bad_alloc, std::runtime_error)
173 if(filename_flag)
174 throw std::runtime_error("CD images can't be patched on the fly");
175 try {
176 std::vector<char> data2 = data;
177 size_t poffset = 0;
178 if(xml && valid)
179 data2.resize(data2.size() - 1);
180 data2 = do_patch_file(data2, patch, offset);
181 //Mark the slot as valid and update hash.
182 valid = true;
183 std::string new_sha256 = sha256::hash(data2);
184 if(xml) {
185 size_t osize = data2.size();
186 data2.resize(osize + 1);
187 data2[osize] = 0;
189 data = data2;
190 sha_256 = new_sha256;
191 } catch(...) {
192 throw;
196 std::pair<core_type*, core_region*> get_current_rom_info() throw()
198 return std::make_pair(current_rom_type, current_region);
201 loaded_rom::loaded_rom() throw()
203 rtype = &type_null;
204 region = orig_region = &null_region;
207 loaded_rom::loaded_rom(const std::string& file) throw(std::bad_alloc, std::runtime_error)
209 std::list<core_type*> possible = core_type::get_core_types();
210 std::istream& spec = open_file_relative(file, "");
211 std::string s;
212 std::getline(spec, s);
213 istrip_CR(s);
214 load_filename = file;
215 if(!spec || s != "[GAMEPACK FILE]") {
216 //This is a Raw ROM image.
217 regex_results tmp;
218 std::string ext = regex(".*\\.([^.]*)?", file, "Unknown ROM file type")[1];
219 core_type* coretype = NULL;
220 for(auto i : possible) {
221 if(i->is_known_extension(ext))
222 coretype = i;
224 if(!coretype)
225 throw std::runtime_error("Unknown ROM file type");
226 rtype = coretype;
227 region = orig_region = &rtype->get_preferred_region();
228 unsigned romidx = 0;
229 std::string bios;
230 if((bios = coretype->get_biosname()) != "") {
231 //This thing has a BIOS.
232 romidx = 1;
233 romimg[0] = loaded_slot(lsnes_set.get("firmwarepath") + "/" + bios, "",
234 coretype->get_image_info(0), false);
236 romimg[romidx] = loaded_slot(file, "", coretype->get_image_info(romidx), false);
237 msu1_base = resolve_file_relative(file, "");
238 return;
240 std::vector<std::string> lines;
241 while(std::getline(spec, s))
242 lines.push_back(strip_CR(s));
243 std::string platname = "";
244 std::string platreg = "";
245 for(auto i : lines) {
246 regex_results tmp;
247 if(tmp = regex("type[ \t]+(.+)", i))
248 platname = tmp[1];
249 if(tmp = regex("region[ \t]+(.+)", i))
250 platreg = tmp[1];
253 //Detect type.
254 rtype = &type_null;
255 for(auto i : possible)
256 if(i->get_iname() == platname)
257 rtype = i;
258 if(rtype == &type_null)
259 (stringfmt() << "Not a valid system type '" << platname << "'").throwex();
261 //Detect region.
262 bool goodreg = false;
263 orig_region = &rtype->get_preferred_region();
264 for(auto i: rtype->get_regions())
265 if(i->get_iname() == platreg) {
266 orig_region = i;
267 goodreg = true;
269 if(!goodreg && platreg != "")
270 (stringfmt() << "Not a valid system region '" << platreg << "'").throwex();
271 region = orig_region;
273 //ROM files.
274 std::string cromimg[sizeof(romimg)/sizeof(romimg[0])];
275 std::string cromxml[sizeof(romimg)/sizeof(romimg[0])];
276 for(auto i : lines) {
277 regex_results tmp;
278 if(!(tmp = regex("(rom|xml)[ \t]+([^ \t]+)[ \t]+(.*)", i)))
279 continue;
280 size_t idxs = rtype->get_image_count();
281 size_t idx = idxs;
282 for(size_t i = 0; i < idxs; i++)
283 if(rtype->get_image_info(i).iname == tmp[2])
284 idx = i;
285 if(idx == idxs)
286 (stringfmt() << "Not a valid ROM name '" << tmp[2] << "'").throwex();
287 if(tmp[1] == "rom")
288 cromimg[idx] = tmp[3];
289 else
290 cromxml[idx] = tmp[3];
293 //Check ROMs.
294 unsigned mask1 = 0, mask2 = 0;
295 for(size_t i = 0; i < rtype->get_image_count(); i++) {
296 auto ii = rtype->get_image_info(i);
297 mask1 |= ii.mandatory;
298 if(cromimg[i] != "")
299 mask2 |= ii.mandatory;
300 if(cromimg[i] == "" && cromxml[i] != "") {
301 messages << "WARNING: Slot " << ii.iname << ": XML without ROM." << std::endl;
302 cromxml[i] = "";
305 if(mask1 != mask2)
306 throw std::runtime_error("Required ROM missing");
308 //Load ROMs.
309 for(size_t i = 0; i < rtype->get_image_count(); i++) {
310 romimg[i] = loaded_slot(cromimg[i], file, rtype->get_image_info(i), false);
311 romxml[i] = loaded_slot(cromxml[i], file, rtype->get_image_info(i), true);
314 //Patch ROMs.
315 for(auto i : lines) {
316 regex_results tmp;
317 if(!(tmp = regex("patch([+-][0-9]+)?[ \t]+([^ \t]+)[ \t]+(.*)", i)))
318 continue;
319 size_t idxs = rtype->get_image_count();
320 size_t idx = idxs;
321 for(size_t i = 0; i < idxs; i++)
322 if(rtype->get_image_info(i).iname == tmp[2])
323 idx = i;
324 if(idx == idxs)
325 (stringfmt() << "Not a valid ROM name '" << tmp[2] << "'").throwex();
326 int32_t offset = 0;
327 if(tmp[1] != "")
328 offset = parse_value<int32_t>(tmp[1]);
329 romimg[idx].patch(read_file_relative(tmp[3], file), offset);
332 //MSU-1 base.
333 if(cromimg[1] != "")
334 msu1_base = resolve_file_relative(cromimg[1], file);
335 else
336 msu1_base = resolve_file_relative(cromimg[0], file);
339 void loaded_rom::load(std::map<std::string, std::string>& settings, uint64_t rtc_sec, uint64_t rtc_subsec)
340 throw(std::bad_alloc, std::runtime_error)
342 current_rom_type = &type_null;
343 if(!orig_region && rtype != &type_null)
344 orig_region = &rtype->get_preferred_region();
345 if(!region)
346 region = orig_region;
347 if(rtype && !orig_region->compatible_with(*region))
348 throw std::runtime_error("Trying to force incompatible region");
349 if(rtype && !rtype->set_region(*region))
350 throw std::runtime_error("Trying to force unknown region");
352 core_romimage images[sizeof(romimg)/sizeof(romimg[0])];
353 for(size_t i = 0; i < sizeof(romimg)/sizeof(romimg[0]); i++) {
354 images[i].markup = (const char*)romxml[i];
355 images[i].data = (const unsigned char*)romimg[i];
356 images[i].size = (size_t)romimg[i];
358 if(rtype) {
359 if(!rtype->load(images, settings, rtc_sec, rtc_subsec))
360 throw std::runtime_error("Can't load cartridge ROM");
361 } else
362 core_unload_cartridge();
364 if(rtype == &type_null) {
365 region = &null_region;
366 } else {
367 region = &core_get_region();
368 core_power();
370 auto nominal_fps = rtype->get_video_rate();
371 auto nominal_hz = rtype->get_audio_rate();
372 set_nominal_framerate(1.0 * nominal_fps.first / nominal_fps.second);
373 information_dispatch::do_sound_rate(nominal_hz.first, nominal_hz.second);
374 current_rom_type = rtype;
375 current_region = region;
376 refresh_cart_mappings();
379 std::map<std::string, std::vector<char>> load_sram_commandline(const std::vector<std::string>& cmdline)
380 throw(std::bad_alloc, std::runtime_error)
382 std::map<std::string, std::vector<char>> ret;
383 regex_results opt;
384 for(auto i : cmdline) {
385 if(opt = regex("--continue=(.+)", i)) {
386 zip_reader r(opt[1]);
387 for(auto j : r) {
388 auto sramname = regex("sram\\.(.*)", j);
389 if(!sramname)
390 continue;
391 std::istream& x = r[j];
392 try {
393 std::vector<char> out;
394 boost::iostreams::back_insert_device<std::vector<char>> rd(out);
395 boost::iostreams::copy(x, rd);
396 delete &x;
397 ret[sramname[1]] = out;
398 } catch(...) {
399 delete &x;
400 throw;
403 continue;
404 } else if(opt = regex("--sram-([^=]+)=(.+)", i)) {
405 try {
406 ret[opt[1]] = read_file_relative(opt[2], "");
407 } catch(std::bad_alloc& e) {
408 throw;
409 } catch(std::runtime_error& e) {
410 throw std::runtime_error("Can't load SRAM '" + opt[1] + "': " + e.what());
414 return ret;
417 std::vector<char> save_core_state(bool nochecksum) throw(std::bad_alloc)
419 std::vector<char> ret;
420 core_serialize(ret);
421 if(nochecksum)
422 return ret;
423 size_t offset = ret.size();
424 unsigned char tmp[32];
425 sha256::hash(tmp, ret);
426 ret.resize(offset + 32);
427 memcpy(&ret[offset], tmp, 32);
428 return ret;
431 void load_core_state(const std::vector<char>& buf, bool nochecksum) throw(std::runtime_error)
433 if(nochecksum) {
434 core_unserialize(&buf[0], buf.size());
435 return;
438 if(buf.size() < 32)
439 throw std::runtime_error("Savestate corrupt");
440 unsigned char tmp[32];
441 sha256::hash(tmp, reinterpret_cast<const uint8_t*>(&buf[0]), buf.size() - 32);
442 if(memcmp(tmp, &buf[buf.size() - 32], 32))
443 throw std::runtime_error("Savestate corrupt");
444 core_unserialize(&buf[0], buf.size() - 32);;