Fix SA1 open bus
[lsnes.git] / src / core / romloader.cpp
blob2b670c39be16154c3ae89dd4a229b0baf9ac1bd7
1 #include "core/dispatch.hpp"
2 #include "core/moviedata.hpp"
3 #include "core/rom.hpp"
4 #include "core/romloader.hpp"
5 #include "core/romguess.hpp"
6 #include "core/settings.hpp"
7 #include "core/project.hpp"
8 #include "core/window.hpp"
9 #include "library/zip.hpp"
11 bool load_null_rom()
13 if(project_get()) {
14 std::cerr << "Can't switch ROM with project active." << std::endl;
15 return false;
17 loaded_rom newrom;
18 our_rom = newrom;
19 if(movb)
20 for(size_t i = 0; i < ROM_SLOT_COUNT; i++) {
21 movb.get_mfile().romimg_sha256[i] = "";
22 movb.get_mfile().romxml_sha256[i] = "";
23 movb.get_mfile().namehint[i] = "";
25 notify_core_change();
26 return true;
29 namespace
31 void load_new_rom_inner(const romload_request& req)
33 if(req.packfile != "") {
34 messages << "Loading ROM " << req.packfile << std::endl;
35 loaded_rom newrom(req.packfile);
36 our_rom = newrom;
37 return;
38 } else if(req.singlefile != "") {
39 messages << "Loading ROM " << req.singlefile << std::endl;
40 loaded_rom newrom(req.singlefile, req.core, req.system, req.region);
41 our_rom = newrom;
42 return;
43 } else {
44 messages << "Loading multi-file ROM." << std::endl;
45 loaded_rom newrom(req.files, req.core, req.system, req.region);
46 our_rom = newrom;
47 return;
51 std::string call_rom(unsigned i, bool bios)
53 if(i == 0 && bios)
54 return "BIOS";
55 if(i == 26 && !bios)
56 return "ROM @";
57 if(bios) i--;
58 char j[2] = {0, 0};
59 j[0] = 'A' + i;
60 return std::string("ROM ") + j;
63 void print_missing(core_type& t, unsigned present)
65 bool has_bios = (t.get_biosname() != "");
66 unsigned total = 0;
67 for(unsigned i = 0; i < t.get_image_count(); i++)
68 total |= t.get_image_info(i).mandatory;
69 unsigned bit = 1;
70 std::string need = "";
71 bool first = false;
72 while(bit) {
73 first = true;
74 if((total & bit) && !(present & bit)) {
75 if(need != "") need += ", ";
76 for(unsigned i = 0; i < t.get_image_count(); i++) {
77 if(t.get_image_info(i).mandatory & bit) {
78 if(!first) need += "/";
79 need += call_rom(i, has_bios);
80 first = false;
84 bit <<= 1;
86 (stringfmt() << "Slots " << need << " are required.").throwex();
90 bool _load_new_rom(const romload_request& req)
92 if(project_get()) {
93 std::cerr << "Can't switch ROM with project active." << std::endl;
94 return false;
96 try {
97 load_new_rom_inner(req);
98 if(movb)
99 for(size_t i = 0; i < ROM_SLOT_COUNT; i++) {
100 movb.get_mfile().romimg_sha256[i] = our_rom.romimg[i].sha_256.read();
101 movb.get_mfile().romxml_sha256[i] = our_rom.romxml[i].sha_256.read();
102 movb.get_mfile().namehint[i] = our_rom.romimg[i].namehint;
104 } catch(std::exception& e) {
105 platform::error_message(std::string("Can't load ROM: ") + e.what());
106 messages << "Can't reload ROM: " << e.what() << std::endl;
107 return false;
109 messages << "Using core: " << our_rom.rtype->get_core_identifier() << std::endl;
110 notify_core_change();
111 return true;
115 bool reload_active_rom()
117 romload_request req;
118 if(our_rom.rtype->isnull()) {
119 platform::error_message("Can't reload ROM: No existing ROM");
120 messages << "No ROM loaded" << std::endl;
121 return false;
123 if(our_rom.load_filename != "") {
124 req.packfile = our_rom.load_filename;
125 return _load_new_rom(req);
127 //This is composite ROM.
128 req.core = our_rom.rtype->get_core_identifier();
129 req.system = our_rom.rtype->get_iname();
130 req.region = our_rom.orig_region->get_iname();
131 for(unsigned i = 0; i < ROM_SLOT_COUNT; i++)
132 req.files[i] = our_rom.romimg[i].filename;
133 return _load_new_rom(req);
136 regex_results get_argument(const std::vector<std::string>& cmdline, const std::string& regexp)
138 for(auto i : cmdline) {
139 regex_results r;
140 if(r = regex(regexp, i))
141 return r;
143 return regex_results();
146 std::string get_requested_core(const std::vector<std::string>& cmdline)
148 regex_results r;
149 if(r = get_argument(cmdline, "--core=(.+)"))
150 return r[1];
151 return "";
154 loaded_rom construct_rom_multifile(core_type* ctype, const moviefile::brief_info& info,
155 const std::vector<std::string>& cmdline, bool have_movie)
157 std::string roms[ROM_SLOT_COUNT];
158 std::string realcore = ctype->get_core_identifier();
159 std::string realtype = ctype->get_iname();
160 std::string bios = ctype->get_biosname();
161 uint32_t pmand = 0, tmand = 0;
162 for(unsigned i = 0; i < ROM_SLOT_COUNT; i++) {
163 std::string optregex;
164 bool isbios = false;
165 std::string psetting;
166 std::string romid;
167 if(bios != "" && i == 0) {
168 optregex = "--bios=(.*)";
169 isbios = true;
170 psetting = "firmwarepath";
171 romid = "BIOS";
172 } else {
173 char j[2] = {0, 0};
174 j[0] = i - ((bios != "") ? 1 : 0) + 'a';
175 if(j[0] == 'a' + 26)
176 j[0] = '@';
177 optregex = std::string("--rom-") + j + "=(.*)";
178 psetting = "rompath";
179 j[0] = i - ((bios != "") ? 1 : 0) + 'A';
180 if(j[0] == 'A' + 26)
181 j[0] = '@';
182 romid = std::string("ROM ") + j;
184 regex_results r = get_argument(cmdline, optregex);
185 if(i >= ctype->get_image_count()) {
186 if(r)
187 throw std::runtime_error("This ROM type has no " + romid);
188 else
189 continue;
191 if(info.hash[i] == "" && have_movie && r)
192 throw std::runtime_error("This movie has no " + romid);
194 auto img = ctype->get_image_info(i);
195 tmand |= img.mandatory;
196 if(r) {
197 //Explicitly specified, use that.
198 roms[i] = r[1];
199 } else if(info.hint[i] != "") {
200 //Try to use hint.
201 std::set<std::string> exts = img.extensions;
202 for(auto j : exts) {
203 std::string candidate = lsnes_vset[psetting].str() + "/" + info.hint[i] +
204 "." + j;
205 if(zip::file_exists(candidate)) {
206 roms[i] = candidate;
207 break;
211 if(isbios && roms[i] == "" && i == 0) {
212 //Fallback default.
213 roms[0] = lsnes_vset["firmwarepath"].str() + "/" + bios;
215 if(roms[i] == "" && info.hash[i] != "")
216 roms[i] = try_to_guess_rom(info.hint[i], info.hash[i], info.hashxml[i], *ctype, i);
217 if(roms[i] == "" && info.hash[i] != "")
218 throw std::runtime_error("Can't find " + romid + " (specify explicitly)");
219 if(roms[i] != "")
220 pmand |= img.mandatory;
221 if(roms[i] != "" && !zip::file_exists(roms[i]))
222 throw std::runtime_error(romid + " points to nonexistent file (" + roms[i] + ")");
224 if(pmand != tmand)
225 print_missing(*ctype, pmand);
226 return loaded_rom(roms, realcore, realtype, "");
229 loaded_rom construct_rom_nofile(const std::vector<std::string>& cmdline)
231 std::string requested_core = get_requested_core(cmdline);
232 //Handle bundle / single-file ROMs.
233 for(auto i : cmdline) {
234 regex_results r;
235 if(r = regex("--rom=(.*)", i)) {
236 //Okay, load as ROM bundle and check validity.
237 loaded_rom cr(r[1], requested_core);
238 return cr;
242 for(auto i : core_core::all_cores())
243 if(i->get_core_shortname() == requested_core || requested_core == "")
244 goto valid;
245 throw std::runtime_error("Specified unsupported core");
246 valid: ;
248 //Multi-file ROMs.
249 regex_results _type = get_argument(cmdline, "--rom-type=(.*)");
250 if(!_type)
251 return loaded_rom(); //NULL rom.
252 core_type* ctype = NULL;
253 for(auto i : core_type::get_core_types()) {
254 if(i->get_iname() != _type[1])
255 continue;
256 if(i->get_core_shortname() != requested_core && requested_core != "")
257 continue;
258 ctype = i;
260 if(!ctype)
261 throw std::runtime_error("Specified impossible core/type combination");
263 moviefile::brief_info info;
264 return construct_rom_multifile(ctype, info, cmdline, false);
268 loaded_rom construct_rom(const std::string& movie_filename, const std::vector<std::string>& cmdline)
270 if(movie_filename == "")
271 return construct_rom_nofile(cmdline);
272 moviefile::brief_info info(movie_filename);
273 std::string requested_core = get_requested_core(cmdline);
274 auto sysregs = core_sysregion::find_matching(info.sysregion);
275 if(sysregs.empty())
276 throw std::runtime_error("No core supports '" + info.sysregion + "'");
278 //Default to correct core.
279 if(requested_core == "") {
280 for(auto i : core_core::all_cores())
281 if(i->get_core_identifier() == info.corename)
282 requested_core = i->get_core_shortname();
285 //Handle bundle / single-file ROMs.
286 for(auto i : cmdline) {
287 regex_results r;
288 if(r = regex("--rom=(.*)", i)) {
289 //Okay, load as ROM bundle and check validity.
290 loaded_rom cr(r[1], requested_core);
291 for(auto j : sysregs) {
292 if(&j->get_type() != cr.rtype)
293 continue;
294 for(auto k : cr.rtype->get_regions())
295 if(k == &j->get_region())
296 goto valid;
298 throw std::runtime_error("Specified ROM is of wrong type ('" +
299 cr.rtype->get_hname() + "') for movie ('" + info.sysregion + ")");
300 valid:
301 return cr;
305 //Multi-ROM case.
306 core_type* ctype = NULL;
307 for(auto i : sysregs) {
308 ctype = &i->get_type();
309 if(ctype->get_core_shortname() == requested_core)
310 break;
312 if(requested_core != "" && ctype->get_core_shortname() != requested_core)
313 throw std::runtime_error("Specified incomplatible or unsupported core");
314 if(requested_core == "")
315 messages << "Will use '" << ctype->get_core_identifier() << "'" << std::endl;
316 return construct_rom_multifile(ctype, info, cmdline, true);