lsnes rr2-β24
[lsnes.git] / src / core / romloader.cpp
blobfd76ea1ea62c7f780c7338725a7c4993cc46ca47
1 #include "core/dispatch.hpp"
2 #include "core/instance.hpp"
3 #include "core/messages.hpp"
4 #include "core/moviedata.hpp"
5 #include "core/project.hpp"
6 #include "core/rom.hpp"
7 #include "core/settings.hpp"
8 #include "core/window.hpp"
9 #include "library/zip.hpp"
11 bool load_null_rom()
13 auto& core = CORE();
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 *core.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 core.dispatch->core_change();
27 return true;
30 namespace
32 void load_new_rom_inner(const romload_request& req)
34 auto& core = CORE();
35 if(req.packfile != "") {
36 messages << "Loading ROM " << req.packfile << std::endl;
37 loaded_rom newrom(new rom_image(req.packfile));
38 *core.rom = newrom;
39 return;
40 } else if(req.singlefile != "") {
41 messages << "Loading ROM " << req.singlefile << std::endl;
42 loaded_rom newrom(new rom_image(req.singlefile, req.core, req.system, req.region));
43 *core.rom = newrom;
44 return;
45 } else {
46 messages << "Loading multi-file ROM." << std::endl;
47 loaded_rom newrom(new rom_image(req.files, req.core, req.system, req.region));
48 *core.rom = newrom;
49 return;
53 std::string call_rom(unsigned i, bool bios)
55 if(i == 0 && bios)
56 return "BIOS";
57 if(i == 26 && !bios)
58 return "ROM @";
59 if(bios) i--;
60 char j[2] = {0, 0};
61 j[0] = 'A' + i;
62 return std::string("ROM ") + j;
65 void print_missing(core_type& t, unsigned present)
67 bool has_bios = (t.get_biosname() != "");
68 unsigned total = 0;
69 for(unsigned i = 0; i < t.get_image_count(); i++)
70 total |= t.get_image_info(i).mandatory;
71 unsigned bit = 1;
72 std::string need = "";
73 bool first = false;
74 while(bit) {
75 first = true;
76 if((total & bit) && !(present & bit)) {
77 if(need != "") need += ", ";
78 for(unsigned i = 0; i < t.get_image_count(); i++) {
79 if(t.get_image_info(i).mandatory & bit) {
80 if(!first) need += "/";
81 need += call_rom(i, has_bios);
82 first = false;
86 bit <<= 1;
88 (stringfmt() << "Slots " << need << " are required.").throwex();
92 bool _load_new_rom(const romload_request& req)
94 auto& core = CORE();
95 if(core.project->get()) {
96 std::string msg = "Can't switch ROM with project active.";
97 platform::error_message(msg);
98 messages << msg << std::endl;
99 return false;
101 try {
102 load_new_rom_inner(req);
103 if(*core.mlogic)
104 for(size_t i = 0; i < ROM_SLOT_COUNT; i++) {
105 auto& img = core.rom->get_rom(i);
106 auto& xml = core.rom->get_markup(i);
107 core.mlogic->get_mfile().romimg_sha256[i] = img.sha_256.read();
108 core.mlogic->get_mfile().romxml_sha256[i] = xml.sha_256.read();
109 core.mlogic->get_mfile().namehint[i] = img.namehint;
111 } catch(std::exception& e) {
112 platform::error_message(std::string("Can't load ROM: ") + e.what());
113 messages << "Can't reload ROM: " << e.what() << std::endl;
114 return false;
116 messages << "Using core: " << core.rom->get_core_identifier() << std::endl;
117 core.dispatch->core_change();
118 return true;
122 bool reload_active_rom()
124 auto& core = CORE();
125 romload_request req;
126 if(core.rom->isnull()) {
127 platform::error_message("Can't reload ROM: No existing ROM");
128 messages << "No ROM loaded" << std::endl;
129 return false;
131 //Single-file ROM?
132 std::string loadfile = core.rom->get_pack_filename();
133 if(loadfile != "") {
134 req.packfile = loadfile;
135 return _load_new_rom(req);
137 //This is composite ROM.
138 req.core = core.rom->get_core_identifier();
139 req.system = core.rom->get_iname();
140 req.region = core.rom->orig_region_get_iname();
141 for(unsigned i = 0; i < ROM_SLOT_COUNT; i++)
142 req.files[i] = core.rom->get_rom(i).filename;
143 return _load_new_rom(req);
146 regex_results get_argument(const std::vector<std::string>& cmdline, const std::string& regexp)
148 for(auto i : cmdline) {
149 regex_results r;
150 if(r = regex(regexp, i))
151 return r;
153 return regex_results();
156 std::string get_requested_core(const std::vector<std::string>& cmdline)
158 regex_results r;
159 if(r = get_argument(cmdline, "--core=(.+)"))
160 return r[1];
161 return "";
164 rom_image_handle construct_rom_multifile(core_type* ctype, const moviefile::brief_info& info,
165 const std::vector<std::string>& cmdline, bool have_movie)
167 auto& core = CORE();
168 std::string roms[ROM_SLOT_COUNT];
169 std::string realcore = ctype->get_core_identifier();
170 std::string realtype = ctype->get_iname();
171 std::string bios = ctype->get_biosname();
172 uint32_t pmand = 0, tmand = 0;
173 for(unsigned i = 0; i < ROM_SLOT_COUNT; i++) {
174 std::string optregex;
175 bool isbios = false;
176 auto psetting = &SET_firmwarepath;
177 std::string romid;
178 if(bios != "" && i == 0) {
179 optregex = "--bios=(.*)";
180 isbios = true;
181 psetting = &SET_firmwarepath;
182 romid = "BIOS";
183 } else {
184 char j[2] = {0, 0};
185 j[0] = i - ((bios != "") ? 1 : 0) + 'a';
186 if(j[0] == 'a' + 26)
187 j[0] = '@';
188 optregex = std::string("--rom-") + j + "=(.*)";
189 psetting = &SET_rompath;
190 j[0] = i - ((bios != "") ? 1 : 0) + 'A';
191 if(j[0] == 'A' + 26)
192 j[0] = '@';
193 romid = std::string("ROM ") + j;
195 regex_results r = get_argument(cmdline, optregex);
196 if(i >= ctype->get_image_count()) {
197 if(r)
198 throw std::runtime_error("This ROM type has no " + romid);
199 else
200 continue;
202 if(info.hash[i] == "" && have_movie && r)
203 throw std::runtime_error("This movie has no " + romid);
205 auto img = ctype->get_image_info(i);
206 tmand |= img.mandatory;
207 if(r) {
208 //Explicitly specified, use that.
209 roms[i] = r[1];
210 } else if(info.hint[i] != "") {
211 //Try to use hint.
212 std::set<std::string> exts = img.extensions;
213 for(auto j : exts) {
214 std::string candidate = (*psetting)(*core.settings) + "/" + info.hint[i] +
215 "." + j;
216 if(zip::file_exists(candidate)) {
217 roms[i] = candidate;
218 break;
222 if(isbios && roms[i] == "" && i == 0) {
223 //Fallback default.
224 roms[0] = SET_firmwarepath(*core.settings) + "/" + bios;
226 if(roms[i] == "" && info.hash[i] != "")
227 roms[i] = try_to_guess_rom(info.hint[i], info.hash[i], info.hashxml[i], *ctype, i);
228 if(roms[i] == "" && info.hash[i] != "")
229 throw std::runtime_error("Can't find " + romid + " (specify explicitly)");
230 if(roms[i] != "")
231 pmand |= img.mandatory;
232 if(roms[i] != "" && !zip::file_exists(roms[i]))
233 throw std::runtime_error(romid + " points to nonexistent file (" + roms[i] + ")");
235 if(pmand != tmand)
236 print_missing(*ctype, pmand);
237 return new rom_image(roms, realcore, realtype, "");
240 rom_image_handle construct_rom_nofile(const std::vector<std::string>& cmdline)
242 std::string requested_core = get_requested_core(cmdline);
243 //Handle bundle / single-file ROMs.
244 for(auto i : cmdline) {
245 regex_results r;
246 if(r = regex("--rom=(.*)", i)) {
247 //Okay, load as ROM bundle and check validity.
248 return new rom_image(r[1], requested_core);
252 for(auto i : core_core::all_cores())
253 if(i->get_core_shortname() == requested_core || requested_core == "")
254 goto valid;
255 throw std::runtime_error("Specified unsupported core");
256 valid: ;
258 //Multi-file ROMs.
259 regex_results _type = get_argument(cmdline, "--rom-type=(.*)");
260 if(!_type)
261 return rom_image_handle(); //NULL rom.
262 core_type* ctype = NULL;
263 for(auto i : core_type::get_core_types()) {
264 if(i->get_iname() != _type[1])
265 continue;
266 if(i->get_core_shortname() != requested_core && requested_core != "")
267 continue;
268 ctype = i;
270 if(!ctype)
271 throw std::runtime_error("Specified impossible core/type combination");
273 moviefile::brief_info info;
274 return construct_rom_multifile(ctype, info, cmdline, false);
278 rom_image_handle construct_rom(const std::string& movie_filename, const std::vector<std::string>& cmdline)
280 if(movie_filename == "")
281 return construct_rom_nofile(cmdline);
282 moviefile::brief_info info(movie_filename);
283 std::string requested_core = get_requested_core(cmdline);
284 auto sysregs = core_sysregion::find_matching(info.sysregion);
285 if(sysregs.empty())
286 throw std::runtime_error("No core supports '" + info.sysregion + "'");
288 //Default to correct core.
289 if(requested_core == "") {
290 for(auto i : core_core::all_cores())
291 if(i->get_core_identifier() == info.corename)
292 requested_core = i->get_core_shortname();
295 //Handle bundle / single-file ROMs.
296 for(auto i : cmdline) {
297 regex_results r;
298 if(r = regex("--rom=(.*)", i)) {
299 //Okay, load as ROM bundle and check validity.
300 auto cr = new rom_image(r[1], requested_core);
301 for(auto j : sysregs) {
302 if(!cr->is_of_type(j->get_type()))
303 continue;
304 for(auto k : cr->get_regions())
305 if(k == &j->get_region())
306 goto valid;
308 throw std::runtime_error("Specified ROM is of wrong type ('" +
309 cr->get_hname() + "') for movie ('" + info.sysregion + ")");
310 valid:
311 return cr;
315 //Multi-ROM case.
316 core_type* ctype = NULL;
317 for(auto i : sysregs) {
318 ctype = &i->get_type();
319 if(ctype->get_core_shortname() == requested_core)
320 break;
322 if(requested_core != "" && ctype->get_core_shortname() != requested_core)
323 throw std::runtime_error("Specified incomplatible or unsupported core");
324 if(requested_core == "")
325 messages << "Will use '" << ctype->get_core_identifier() << "'" << std::endl;
326 return construct_rom_multifile(ctype, info, cmdline, true);