Allow immediate saving at point of save
[lsnes.git] / src / core / rom.cpp
blob2540a54c5851a4d79112a8a4018b89ec982af3e7
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/patch.hpp"
14 #include "library/sha256.hpp"
15 #include "library/string.hpp"
16 #include "library/zip.hpp"
18 #include <stdexcept>
19 #include <sstream>
20 #include <iomanip>
21 #include <cstdint>
22 #include <set>
23 #include <boost/iostreams/categories.hpp>
24 #include <boost/iostreams/copy.hpp>
25 #include <boost/iostreams/stream.hpp>
26 #include <boost/iostreams/stream_buffer.hpp>
27 #include <boost/iostreams/filter/symmetric.hpp>
28 #include <boost/iostreams/filter/zlib.hpp>
29 #include <boost/iostreams/filtering_stream.hpp>
30 #include <boost/iostreams/device/back_inserter.hpp>
32 namespace
34 core_type* current_rom_type = NULL;
35 core_region* current_region = NULL;
37 bool file_exists(const std::string& name)
39 try {
40 delete &open_file_relative(name, "");
41 return true;
42 } catch(...) {
43 return false;
48 loaded_slot::loaded_slot() throw(std::bad_alloc)
50 valid = false;
51 xml = false;
52 sha256 = "";
55 loaded_slot::loaded_slot(const std::string& filename, const std::string& base,
56 const struct core_romimage_info& imginfo, bool xml_flag) throw(std::bad_alloc, std::runtime_error)
58 unsigned headered = 0;
59 xml = xml_flag;
60 if(filename == "") {
61 valid = false;
62 sha256 = "";
63 return;
65 valid = true;
66 data = read_file_relative(filename, base);
67 if(!xml)
68 headered = imginfo.headersize(data.size());
69 if(headered && !xml) {
70 if(data.size() >= headered) {
71 memmove(&data[0], &data[headered], data.size() - headered);
72 data.resize(data.size() - headered);
73 } else {
74 data.resize(0);
77 sha256 = sha256::hash(data);
78 if(xml) {
79 size_t osize = data.size();
80 data.resize(osize + 1);
81 data[osize] = 0;
85 void loaded_slot::patch(const std::vector<char>& patch, int32_t offset) throw(std::bad_alloc, std::runtime_error)
87 try {
88 std::vector<char> data2 = data;
89 size_t poffset = 0;
90 if(xml && valid)
91 data2.resize(data2.size() - 1);
92 data2 = do_patch_file(data2, patch, offset);
93 //Mark the slot as valid and update hash.
94 valid = true;
95 std::string new_sha256 = sha256::hash(data2);
96 if(xml) {
97 size_t osize = data2.size();
98 data2.resize(osize + 1);
99 data2[osize] = 0;
101 data = data2;
102 sha256 = new_sha256;
103 } catch(...) {
104 throw;
110 std::pair<core_type*, core_region*> get_current_rom_info() throw()
112 return std::make_pair(current_rom_type, current_region);
115 loaded_rom::loaded_rom() throw()
117 rtype = NULL;
118 region = orig_region = NULL;
121 loaded_rom::loaded_rom(const std::string& file) throw(std::bad_alloc, std::runtime_error)
123 std::list<core_type*> possible = core_type::get_core_types();
124 std::istream& spec = open_file_relative(file, "");
125 std::string s;
126 std::getline(spec, s);
127 istrip_CR(s);
128 load_filename = file;
129 if(!spec || s != "[GAMEPACK FILE]") {
130 //This is a Raw ROM image.
131 regex_results tmp;
132 std::string ext = regex(".*\\.([^.]*)?", file, "Unknown ROM file type")[1];
133 core_type* coretype = NULL;
134 for(auto i : possible) {
135 if(i->is_known_extension(ext))
136 coretype = i;
138 if(!coretype)
139 throw std::runtime_error("Unknown ROM file type");
140 rtype = coretype;
141 region = orig_region = &rtype->get_preferred_region();
142 unsigned romidx = 0;
143 std::string bios;
144 if((bios = coretype->get_biosname()) != "") {
145 //This thing has a BIOS.
146 romidx = 1;
147 std::string basename = setting::get("firmwarepath") + "/" + bios;
148 romimg[0] = loaded_slot(basename, "", coretype->get_image_info(0), false);
149 if(file_exists(basename + ".xml"))
150 romxml[0] = loaded_slot(basename + ".xml", "", coretype->get_image_info(0), true);
152 romimg[romidx] = loaded_slot(file, "", coretype->get_image_info(romidx), false);
153 if(file_exists(file + ".xml"))
154 romxml[romidx] = loaded_slot(file + ".xml", "", coretype->get_image_info(romidx), true);
155 msu1_base = resolve_file_relative(file, "");
156 return;
158 std::vector<std::string> lines;
159 while(std::getline(spec, s))
160 lines.push_back(strip_CR(s));
161 std::string platname = "";
162 std::string platreg = "";
163 for(auto i : lines) {
164 regex_results tmp;
165 if(tmp = regex("type[ \t]+(.+)", i))
166 platname = tmp[1];
167 if(tmp = regex("region[ \t]+(.+)", i))
168 platreg = tmp[1];
171 //Detect type.
172 rtype = NULL;
173 for(auto i : possible)
174 if(i->get_iname() == platname)
175 rtype = i;
176 if(!rtype)
177 (stringfmt() << "Not a valid system type '" << platname << "'").throwex();
179 //Detect region.
180 bool goodreg = false;
181 orig_region = &rtype->get_preferred_region();
182 for(auto i: rtype->get_regions())
183 if(i->get_iname() == platreg) {
184 orig_region = i;
185 goodreg = true;
187 if(!goodreg && platreg != "")
188 (stringfmt() << "Not a valid system region '" << platreg << "'").throwex();
189 region = orig_region;
191 //ROM files.
192 std::string cromimg[sizeof(romimg)/sizeof(romimg[0])];
193 std::string cromxml[sizeof(romimg)/sizeof(romimg[0])];
194 for(auto i : lines) {
195 regex_results tmp;
196 if(!(tmp = regex("(rom|xml)[ \t]+([^ \t]+)[ \t]+(.*)", i)))
197 continue;
198 size_t idxs = rtype->get_image_count();
199 size_t idx = idxs;
200 for(size_t i = 0; i < idxs; i++)
201 if(rtype->get_image_info(i).iname == tmp[2])
202 idx = i;
203 if(idx == idxs)
204 (stringfmt() << "Not a valid ROM name '" << tmp[2] << "'").throwex();
205 if(tmp[1] == "rom")
206 cromimg[idx] = tmp[3];
207 else
208 cromxml[idx] = tmp[3];
211 //Check ROMs.
212 unsigned mask1 = 0, mask2 = 0;
213 for(size_t i = 0; i < rtype->get_image_count(); i++) {
214 auto ii = rtype->get_image_info(i);
215 mask1 |= ii.mandatory;
216 if(cromimg[i] != "")
217 mask2 |= ii.mandatory;
218 if(cromimg[i] == "" && cromxml[i] != "") {
219 messages << "WARNING: Slot " << ii.iname << ": XML without ROM." << std::endl;
220 cromxml[i] = "";
223 if(mask1 != mask2)
224 throw std::runtime_error("Required ROM missing");
226 //Load ROMs.
227 for(size_t i = 0; i < rtype->get_image_count(); i++) {
228 romimg[i] = loaded_slot(cromimg[i], file, rtype->get_image_info(i), false);
229 romxml[i] = loaded_slot(cromxml[i], file, rtype->get_image_info(i), true);
232 //Patch ROMs.
233 for(auto i : lines) {
234 regex_results tmp;
235 if(!(tmp = regex("patch([+-][0-9]+)?[ \t]+([^ \t]+)[ \t]+(.*)", i)))
236 continue;
237 size_t idxs = rtype->get_image_count();
238 size_t idx = idxs;
239 for(size_t i = 0; i < idxs; i++)
240 if(rtype->get_image_info(i).iname == tmp[2])
241 idx = i;
242 if(idx == idxs)
243 (stringfmt() << "Not a valid ROM name '" << tmp[2] << "'").throwex();
244 int32_t offset = 0;
245 if(tmp[1] != "")
246 offset = parse_value<int32_t>(tmp[1]);
247 romimg[idx].patch(read_file_relative(tmp[3], file), offset);
250 //MSU-1 base.
251 if(cromimg[1] != "")
252 msu1_base = resolve_file_relative(cromimg[1], file);
253 else
254 msu1_base = resolve_file_relative(cromimg[0], file);
257 void loaded_rom::load(uint64_t rtc_sec, uint64_t rtc_subsec) throw(std::bad_alloc, std::runtime_error)
259 current_rom_type = NULL;
260 if(!orig_region && rtype)
261 orig_region = &rtype->get_preferred_region();
262 if(!region)
263 region = orig_region;
264 if(rtype && !orig_region->compatible_with(*region))
265 throw std::runtime_error("Trying to force incompatible region");
266 if(rtype && !core_set_region(*region))
267 throw std::runtime_error("Trying to force unknown region");
269 core_romimage images[sizeof(romimg)/sizeof(romimg[0])];
270 for(size_t i = 0; i < sizeof(romimg)/sizeof(romimg[0]); i++) {
271 images[i].markup = (const char*)romxml[i];
272 images[i].data = (const unsigned char*)romimg[i];
273 images[i].size = (size_t)romimg[i];
275 if(rtype) {
276 if(!rtype->load(images, rtc_sec, rtc_subsec))
277 throw std::runtime_error("Can't load cartridge ROM");
278 } else
279 core_unload_cartridge();
281 region = &core_get_region();
282 core_power();
283 auto nominal_fps = get_video_rate();
284 auto nominal_hz = get_audio_rate();
285 set_nominal_framerate(1.0 * nominal_fps.first / nominal_fps.second);
286 information_dispatch::do_sound_rate(nominal_hz.first, nominal_hz.second);
287 current_rom_type = rtype;
288 current_region = region;
289 refresh_cart_mappings();
292 std::map<std::string, std::vector<char>> load_sram_commandline(const std::vector<std::string>& cmdline)
293 throw(std::bad_alloc, std::runtime_error)
295 std::map<std::string, std::vector<char>> ret;
296 regex_results opt;
297 for(auto i : cmdline) {
298 if(opt = regex("--continue=(.+)", i)) {
299 zip_reader r(opt[1]);
300 for(auto j : r) {
301 auto sramname = regex("sram\\.(.*)", j);
302 if(!sramname)
303 continue;
304 std::istream& x = r[j];
305 try {
306 std::vector<char> out;
307 boost::iostreams::back_insert_device<std::vector<char>> rd(out);
308 boost::iostreams::copy(x, rd);
309 delete &x;
310 ret[sramname[1]] = out;
311 } catch(...) {
312 delete &x;
313 throw;
316 continue;
317 } else if(opt = regex("--sram-([^=]+)=(.+)", i)) {
318 try {
319 ret[opt[1]] = read_file_relative(opt[2], "");
320 } catch(std::bad_alloc& e) {
321 throw;
322 } catch(std::runtime_error& e) {
323 throw std::runtime_error("Can't load SRAM '" + opt[1] + "': " + e.what());
327 return ret;
330 std::vector<char> save_core_state(bool nochecksum) throw(std::bad_alloc)
332 std::vector<char> ret;
333 core_serialize(ret);
334 if(nochecksum)
335 return ret;
336 size_t offset = ret.size();
337 unsigned char tmp[32];
338 sha256::hash(tmp, ret);
339 ret.resize(offset + 32);
340 memcpy(&ret[offset], tmp, 32);
341 return ret;
344 void load_core_state(const std::vector<char>& buf, bool nochecksum) throw(std::runtime_error)
346 if(nochecksum) {
347 core_unserialize(&buf[0], buf.size());
348 return;
351 if(buf.size() < 32)
352 throw std::runtime_error("Savestate corrupt");
353 unsigned char tmp[32];
354 sha256::hash(tmp, reinterpret_cast<const uint8_t*>(&buf[0]), buf.size() - 32);
355 if(memcmp(tmp, &buf[buf.size() - 32], 32))
356 throw std::runtime_error("Savestate corrupt");
357 core_unserialize(&buf[0], buf.size() - 32);;