Make instance vars to be pointers
[lsnes.git] / src / core / romloader.cpp
blob95826c9c7809f26572238ab490f512f897233ccf
1 #include "core/dispatch.hpp"
2 #include "core/instance.hpp"
3 #include "core/moviedata.hpp"
4 #include "core/rom.hpp"
5 #include "core/romloader.hpp"
6 #include "core/romguess.hpp"
7 #include "core/settings.hpp"
8 #include "core/project.hpp"
9 #include "core/window.hpp"
10 #include "library/zip.hpp"
12 bool load_null_rom()
14 if(CORE().project->get()) {
15 std::cerr << "Can't switch ROM with project active." << std::endl;
16 return false;
18 loaded_rom newrom;
19 our_rom = newrom;
20 if(*CORE().mlogic)
21 for(size_t i = 0; i < ROM_SLOT_COUNT; i++) {
22 CORE().mlogic->get_mfile().romimg_sha256[i] = "";
23 CORE().mlogic->get_mfile().romxml_sha256[i] = "";
24 CORE().mlogic->get_mfile().namehint[i] = "";
26 notify_core_change();
27 return true;
30 namespace
32 void load_new_rom_inner(const romload_request& req)
34 if(req.packfile != "") {
35 messages << "Loading ROM " << req.packfile << std::endl;
36 loaded_rom newrom(req.packfile);
37 our_rom = newrom;
38 return;
39 } else if(req.singlefile != "") {
40 messages << "Loading ROM " << req.singlefile << std::endl;
41 loaded_rom newrom(req.singlefile, req.core, req.system, req.region);
42 our_rom = newrom;
43 return;
44 } else {
45 messages << "Loading multi-file ROM." << std::endl;
46 loaded_rom newrom(req.files, req.core, req.system, req.region);
47 our_rom = newrom;
48 return;
52 std::string call_rom(unsigned i, bool bios)
54 if(i == 0 && bios)
55 return "BIOS";
56 if(i == 26 && !bios)
57 return "ROM @";
58 if(bios) i--;
59 char j[2] = {0, 0};
60 j[0] = 'A' + i;
61 return std::string("ROM ") + j;
64 void print_missing(core_type& t, unsigned present)
66 bool has_bios = (t.get_biosname() != "");
67 unsigned total = 0;
68 for(unsigned i = 0; i < t.get_image_count(); i++)
69 total |= t.get_image_info(i).mandatory;
70 unsigned bit = 1;
71 std::string need = "";
72 bool first = false;
73 while(bit) {
74 first = true;
75 if((total & bit) && !(present & bit)) {
76 if(need != "") need += ", ";
77 for(unsigned i = 0; i < t.get_image_count(); i++) {
78 if(t.get_image_info(i).mandatory & bit) {
79 if(!first) need += "/";
80 need += call_rom(i, has_bios);
81 first = false;
85 bit <<= 1;
87 (stringfmt() << "Slots " << need << " are required.").throwex();
91 bool _load_new_rom(const romload_request& req)
93 if(CORE().project->get()) {
94 std::cerr << "Can't switch ROM with project active." << std::endl;
95 return false;
97 try {
98 load_new_rom_inner(req);
99 if(*CORE().mlogic)
100 for(size_t i = 0; i < ROM_SLOT_COUNT; i++) {
101 CORE().mlogic->get_mfile().romimg_sha256[i] = our_rom.romimg[i].sha_256.read();
102 CORE().mlogic->get_mfile().romxml_sha256[i] = our_rom.romxml[i].sha_256.read();
103 CORE().mlogic->get_mfile().namehint[i] = our_rom.romimg[i].namehint;
105 } catch(std::exception& e) {
106 platform::error_message(std::string("Can't load ROM: ") + e.what());
107 messages << "Can't reload ROM: " << e.what() << std::endl;
108 return false;
110 messages << "Using core: " << our_rom.rtype->get_core_identifier() << std::endl;
111 notify_core_change();
112 return true;
116 bool reload_active_rom()
118 romload_request req;
119 if(our_rom.rtype->isnull()) {
120 platform::error_message("Can't reload ROM: No existing ROM");
121 messages << "No ROM loaded" << std::endl;
122 return false;
124 if(our_rom.load_filename != "") {
125 req.packfile = our_rom.load_filename;
126 return _load_new_rom(req);
128 //This is composite ROM.
129 req.core = our_rom.rtype->get_core_identifier();
130 req.system = our_rom.rtype->get_iname();
131 req.region = our_rom.orig_region->get_iname();
132 for(unsigned i = 0; i < ROM_SLOT_COUNT; i++)
133 req.files[i] = our_rom.romimg[i].filename;
134 return _load_new_rom(req);
137 regex_results get_argument(const std::vector<std::string>& cmdline, const std::string& regexp)
139 for(auto i : cmdline) {
140 regex_results r;
141 if(r = regex(regexp, i))
142 return r;
144 return regex_results();
147 std::string get_requested_core(const std::vector<std::string>& cmdline)
149 regex_results r;
150 if(r = get_argument(cmdline, "--core=(.+)"))
151 return r[1];
152 return "";
155 loaded_rom construct_rom_multifile(core_type* ctype, const moviefile::brief_info& info,
156 const std::vector<std::string>& cmdline, bool have_movie)
158 std::string roms[ROM_SLOT_COUNT];
159 std::string realcore = ctype->get_core_identifier();
160 std::string realtype = ctype->get_iname();
161 std::string bios = ctype->get_biosname();
162 uint32_t pmand = 0, tmand = 0;
163 for(unsigned i = 0; i < ROM_SLOT_COUNT; i++) {
164 std::string optregex;
165 bool isbios = false;
166 std::string psetting;
167 std::string romid;
168 if(bios != "" && i == 0) {
169 optregex = "--bios=(.*)";
170 isbios = true;
171 psetting = "firmwarepath";
172 romid = "BIOS";
173 } else {
174 char j[2] = {0, 0};
175 j[0] = i - ((bios != "") ? 1 : 0) + 'a';
176 if(j[0] == 'a' + 26)
177 j[0] = '@';
178 optregex = std::string("--rom-") + j + "=(.*)";
179 psetting = "rompath";
180 j[0] = i - ((bios != "") ? 1 : 0) + 'A';
181 if(j[0] == 'A' + 26)
182 j[0] = '@';
183 romid = std::string("ROM ") + j;
185 regex_results r = get_argument(cmdline, optregex);
186 if(i >= ctype->get_image_count()) {
187 if(r)
188 throw std::runtime_error("This ROM type has no " + romid);
189 else
190 continue;
192 if(info.hash[i] == "" && have_movie && r)
193 throw std::runtime_error("This movie has no " + romid);
195 auto img = ctype->get_image_info(i);
196 tmand |= img.mandatory;
197 if(r) {
198 //Explicitly specified, use that.
199 roms[i] = r[1];
200 } else if(info.hint[i] != "") {
201 //Try to use hint.
202 std::set<std::string> exts = img.extensions;
203 for(auto j : exts) {
204 std::string candidate = CORE().setcache->get(psetting) + "/" + info.hint[i] +
205 "." + j;
206 if(zip::file_exists(candidate)) {
207 roms[i] = candidate;
208 break;
212 if(isbios && roms[i] == "" && i == 0) {
213 //Fallback default.
214 roms[0] = CORE().setcache->get("firmwarepath") + "/" + bios;
216 if(roms[i] == "" && info.hash[i] != "")
217 roms[i] = try_to_guess_rom(info.hint[i], info.hash[i], info.hashxml[i], *ctype, i);
218 if(roms[i] == "" && info.hash[i] != "")
219 throw std::runtime_error("Can't find " + romid + " (specify explicitly)");
220 if(roms[i] != "")
221 pmand |= img.mandatory;
222 if(roms[i] != "" && !zip::file_exists(roms[i]))
223 throw std::runtime_error(romid + " points to nonexistent file (" + roms[i] + ")");
225 if(pmand != tmand)
226 print_missing(*ctype, pmand);
227 return loaded_rom(roms, realcore, realtype, "");
230 loaded_rom construct_rom_nofile(const std::vector<std::string>& cmdline)
232 std::string requested_core = get_requested_core(cmdline);
233 //Handle bundle / single-file ROMs.
234 for(auto i : cmdline) {
235 regex_results r;
236 if(r = regex("--rom=(.*)", i)) {
237 //Okay, load as ROM bundle and check validity.
238 loaded_rom cr(r[1], requested_core);
239 return cr;
243 for(auto i : core_core::all_cores())
244 if(i->get_core_shortname() == requested_core || requested_core == "")
245 goto valid;
246 throw std::runtime_error("Specified unsupported core");
247 valid: ;
249 //Multi-file ROMs.
250 regex_results _type = get_argument(cmdline, "--rom-type=(.*)");
251 if(!_type)
252 return loaded_rom(); //NULL rom.
253 core_type* ctype = NULL;
254 for(auto i : core_type::get_core_types()) {
255 if(i->get_iname() != _type[1])
256 continue;
257 if(i->get_core_shortname() != requested_core && requested_core != "")
258 continue;
259 ctype = i;
261 if(!ctype)
262 throw std::runtime_error("Specified impossible core/type combination");
264 moviefile::brief_info info;
265 return construct_rom_multifile(ctype, info, cmdline, false);
269 loaded_rom construct_rom(const std::string& movie_filename, const std::vector<std::string>& cmdline)
271 if(movie_filename == "")
272 return construct_rom_nofile(cmdline);
273 moviefile::brief_info info(movie_filename);
274 std::string requested_core = get_requested_core(cmdline);
275 auto sysregs = core_sysregion::find_matching(info.sysregion);
276 if(sysregs.empty())
277 throw std::runtime_error("No core supports '" + info.sysregion + "'");
279 //Default to correct core.
280 if(requested_core == "") {
281 for(auto i : core_core::all_cores())
282 if(i->get_core_identifier() == info.corename)
283 requested_core = i->get_core_shortname();
286 //Handle bundle / single-file ROMs.
287 for(auto i : cmdline) {
288 regex_results r;
289 if(r = regex("--rom=(.*)", i)) {
290 //Okay, load as ROM bundle and check validity.
291 loaded_rom cr(r[1], requested_core);
292 for(auto j : sysregs) {
293 if(&j->get_type() != cr.rtype)
294 continue;
295 for(auto k : cr.rtype->get_regions())
296 if(k == &j->get_region())
297 goto valid;
299 throw std::runtime_error("Specified ROM is of wrong type ('" +
300 cr.rtype->get_hname() + "') for movie ('" + info.sysregion + ")");
301 valid:
302 return cr;
306 //Multi-ROM case.
307 core_type* ctype = NULL;
308 for(auto i : sysregs) {
309 ctype = &i->get_type();
310 if(ctype->get_core_shortname() == requested_core)
311 break;
313 if(requested_core != "" && ctype->get_core_shortname() != requested_core)
314 throw std::runtime_error("Specified incomplatible or unsupported core");
315 if(requested_core == "")
316 messages << "Will use '" << ctype->get_core_identifier() << "'" << std::endl;
317 return construct_rom_multifile(ctype, info, cmdline, true);