1 #include "platform/wxwidgets/platform.hpp"
2 #include "platform/wxwidgets/window_mainwindow.hpp"
3 #include "platform/wxwidgets/window-romload.hpp"
4 #include "core/command.hpp"
5 #include "core/instance.hpp"
6 #include "core/mainloop.hpp"
7 #include "core/messages.hpp"
8 #include "core/settings.hpp"
9 #include "core/window.hpp"
10 #include "interface/romtype.hpp"
11 #include "library/zip.hpp"
15 bool can_load_singlefile(core_type
* t
)
17 unsigned icnt
= t
->get_image_count();
18 unsigned pmand
= 0, tmand
= 0;
19 if(!icnt
) return false;
20 if(icnt
> 0) pmand
|= t
->get_image_info(0).mandatory
;
21 if(t
->get_biosname() != "" && icnt
> 1) pmand
|= t
->get_image_info(1).mandatory
;
22 for(unsigned i
= 0; i
< t
->get_image_count(); i
++)
23 tmand
|= t
->get_image_info(i
).mandatory
;
24 return pmand
== tmand
;
27 std::string
implode_set(const std::set
<std::string
>& s
)
37 int resolve_core(std::map
<unsigned, core_type
*> coreid
, const std::string
& filename
, int findex
)
40 if(coreid
.count(findex
))
41 return findex
; //Already resolved.
42 if(loaded_rom::is_gamepak(filename
))
43 return 0; //Gamepaks don't resolve.
46 regex_results r
= regex(".*\\.([^.]+)", filename
);
48 return 0; //WTF is this? Leave unresolved.
49 std::string extension
= r
[1];
51 std::map
<core_type
*, unsigned> candidates
;
52 for(auto i
: coreid
) {
53 if(!can_load_singlefile(i
.second
))
55 if(i
.second
->is_hidden())
57 if(i
.second
->isnull())
59 std::set
<std::string
> exts
;
60 unsigned base
= (i
.second
->get_biosname() != "" && i
.second
->get_image_count() > 1) ? 1 : 0;
61 for(auto j
: i
.second
->get_image_info(base
).extensions
)
63 //This is a candidate.
64 candidates
[i
.second
] = i
.first
;
68 if(candidates
.empty())
69 return 0; //Err. Leave unresolved.
70 if(candidates
.size() == 1)
71 return candidates
.begin()->second
; //Only one candidate.
73 //Okay, we have multiple candidates. Prompt among them.
74 std::vector
<std::string
> choices
;
75 std::vector
<unsigned> indexes
;
76 for(auto i
: candidates
) {
77 choices
.push_back(i
.first
->get_core_identifier() + " [" + i
.first
->get_hname() + "]");
78 indexes
.push_back(i
.second
);
81 coretext
= pick_among(NULL
, "Choose core", "Choose core to load the ROM", choices
, 0);
82 for(size_t i
= 0; i
< choices
.size(); i
++)
83 if(choices
[i
] == coretext
)
89 void do_load_rom_image_single(wxwin_mainwindow
* parent
, emulator_instance
& inst
)
92 std::map
<std::string
, core_type
*> cores
;
93 std::map
<unsigned, core_type
*> coreid
;
95 unsigned corecount
= 0;
96 std::set
<std::string
> all_filetypes
;
97 all_filetypes
.insert("lsgp");
98 for(auto i
: core_type::get_core_types()) {
99 if(!can_load_singlefile(i
))
101 unsigned base
= (i
->get_biosname() != "" && i
->get_image_count() > 1) ? 1 : 0;
102 for(auto j
: i
->get_image_info(base
).extensions
)
103 all_filetypes
.insert(j
);
104 cores
[i
->get_hname() + " [" + i
->get_core_identifier() + "]"] = i
;
106 filter
+= "Autodetect|" + implode_set(all_filetypes
);
107 for(auto i
: cores
) {
108 if(!can_load_singlefile(i
.second
))
110 if(i
.second
->is_hidden())
112 if(i
.second
->isnull())
114 std::set
<std::string
> exts
;
115 unsigned base
= (i
.second
->get_biosname() != "" && i
.second
->get_image_count() > 1) ? 1 : 0;
116 for(auto j
: i
.second
->get_image_info(base
).extensions
)
118 filter
+= "|" + i
.first
+ "|" + implode_set(exts
);
119 coreid
[++corecount
] = i
.second
;
121 filter
+= "|All files|*";
122 std::string directory
= inst
.setcache
->get("rompath");
123 wxFileDialog
* d
= new wxFileDialog(parent
, towxstring("Choose ROM to load"), towxstring(directory
),
124 wxT(""), towxstring(filter
), wxFD_OPEN
);
125 if(d
->ShowModal() == wxID_CANCEL
) {
130 recentfiles::multirom mr
;
131 std::string filename
= tostdstring(d
->GetPath());
132 int findex
= d
->GetFilterIndex();
134 findex
= resolve_core(coreid
, filename
, findex
);
135 } catch(canceled_exception
& e
) {
138 if(!coreid
.count(findex
)) {
140 mr
.packfile
= req
.packfile
= filename
;
142 mr
.core
= req
.core
= coreid
[findex
]->get_core_identifier();
143 mr
.system
= req
.system
= coreid
[findex
]->get_iname();
144 mr
.singlefile
= req
.singlefile
= filename
;
146 parent
->recent_roms
->add(mr
);
147 inst
.iqueue
->run_async([req
]() {
148 CORE().command
->invoke("unpause-emulator");
150 }, [](std::exception
& e
) {});
153 class multirom_dialog
: public wxDialog
156 void on_wclose(wxCloseEvent
& e
)
159 EndModal(wxID_CANCEL
);
161 multirom_dialog(wxWindow
* parent
, emulator_instance
& _inst
, std::string rtype
, core_type
& _t
)
162 : wxDialog(parent
, wxID_ANY
, towxstring("lsnes: Load " + rtype
+ " ROM")), inst(_inst
), t(_t
)
166 wxSizer
* vsizer
= new wxBoxSizer(wxVERTICAL
);
168 Connect(wxEVT_CLOSE_WINDOW
, wxCloseEventHandler(multirom_dialog::on_wclose
), NULL
, this);
170 wxSizer
* hsizer2
= new wxBoxSizer(wxHORIZONTAL
);
171 hsizer2
->Add(new wxStaticText(this, wxID_ANY
, wxT("Region: ")), 0, wxGROW
);
172 std::vector
<wxString
> regions_list
;
173 core_region
& prefr
= t
.get_preferred_region();
174 unsigned regindex
= 0;
175 for(auto i
: t
.get_regions()) {
176 if(i
== &prefr
) regindex
= regions_list
.size();
177 regions_list
.push_back(towxstring(i
->get_hname()));
178 regions_known
.push_back(i
);
180 regions
= new wxComboBox(this, wxID_ANY
, regions_list
[regindex
], wxDefaultPosition
,
181 wxDefaultSize
, regions_list
.size(), ®ions_list
[0], wxCB_READONLY
);
182 hsizer2
->Add(regions
, 0, wxGROW
);
183 vsizer
->Add(hsizer2
, 0, wxGROW
);
185 wxSizer
* rarray
= new wxFlexGridSizer(2 * t
.get_image_count(), 3, 0, 0);
186 for(unsigned i
= 0; i
< ROM_SLOT_COUNT
; i
++) {
188 fileselect
[i
] = NULL
;
190 for(unsigned i
= 0; i
< t
.get_image_count(); i
++) {
191 core_romimage_info iinfo
= t
.get_image_info(i
);
192 rarray
->Add(new wxStaticText(this, wxID_ANY
, towxstring(iinfo
.hname
)), 0, wxGROW
);
193 rarray
->Add(filenames
[i
] = new wxTextCtrl(this, wxID_HIGHEST
+ 100 + i
, wxT(""),
194 wxDefaultPosition
, wxSize(400, -1)), 1, wxGROW
);
195 rarray
->Add(fileselect
[i
] = new wxButton(this, wxID_HIGHEST
+ 200 + i
,
196 towxstring("...")), 0, wxGROW
);
197 rarray
->Add(new wxStaticText(this, wxID_ANY
, towxstring("")), 0, wxGROW
);
198 rarray
->Add(hashes
[i
] = new wxStaticText(this, wxID_ANY
, wxT("Not found")), 1,
200 rarray
->Add(new wxStaticText(this, wxID_ANY
, towxstring("")), 0, wxGROW
);
201 hash_ready
[i
] = true;
202 filenames
[i
]->Connect(wxEVT_COMMAND_TEXT_UPDATED
,
203 wxCommandEventHandler(multirom_dialog::do_command_event
), NULL
, this);
204 fileselect
[i
]->Connect(wxEVT_COMMAND_BUTTON_CLICKED
,
205 wxCommandEventHandler(multirom_dialog::do_command_event
), NULL
, this);
207 vsizer
->Add(rarray
, 1, wxGROW
);
209 wxSizer
* buttonbar
= new wxBoxSizer(wxHORIZONTAL
);
210 buttonbar
->Add(okb
= new wxButton(this, wxID_HIGHEST
+ 1, wxT("Load")), 0, wxGROW
);
211 buttonbar
->AddStretchSpacer();
212 buttonbar
->Add(cancelb
= new wxButton(this, wxID_HIGHEST
+ 2, wxT("Cancel")), 0, wxGROW
);
213 vsizer
->Add(buttonbar
, 0, wxGROW
);
214 okb
->Connect(wxEVT_COMMAND_BUTTON_CLICKED
,
215 wxCommandEventHandler(multirom_dialog::do_command_event
), NULL
, this);
217 cancelb
->Connect(wxEVT_COMMAND_BUTTON_CLICKED
,
218 wxCommandEventHandler(multirom_dialog::do_command_event
), NULL
, this);
219 timer
= new update_timer(this);
221 vsizer
->SetSizeHints(this);
232 void timer_update_hashes()
235 for(auto i
= 0; i
< ROM_SLOT_COUNT
&& filenames
[i
]; i
++) {
238 if(!hashfutures
[i
].ready())
241 hashes
[i
]->SetLabel(towxstring("Hash: " + hashfutures
[i
].read()));
242 } catch(std::runtime_error
& e
) {
243 hashes
[i
]->SetLabel(towxstring("Hash: Error"));
245 hash_ready
[i
] = true;
248 void do_command_event(wxCommandEvent
& e
)
252 if(id
== wxID_HIGHEST
+ 1) {
253 if(!check_requirements()) {
254 show_message_ok(this, "Needed ROM missing", "At least one required ROM "
255 "is missing!", wxICON_EXCLAMATION
);
259 } else if(id
== wxID_HIGHEST
+ 2) {
260 EndModal(wxID_CANCEL
);
261 } else if(id
>= wxID_HIGHEST
+ 100 && id
<= wxID_HIGHEST
+ 199) {
262 filename_updated(id
- (wxID_HIGHEST
+ 100));
263 } else if(id
>= wxID_HIGHEST
+ 200 && id
<= wxID_HIGHEST
+ 299) {
264 do_fileselect(id
- (wxID_HIGHEST
+ 200));
267 std::string
getfilename(unsigned i
)
270 if(i
>= ROM_SLOT_COUNT
|| !filenames
[i
])
272 return tostdstring(filenames
[i
]->GetValue());
274 std::string
getregion()
277 int i
= regions
->GetSelection();
279 return t
.get_preferred_region().get_iname();
280 return regions_known
[i
]->get_iname();
283 bool check_requirements()
286 unsigned pm
= 0, tm
= 0;
287 for(unsigned i
= 0; i
< t
.get_image_count(); i
++) {
288 core_romimage_info iinfo
= t
.get_image_info(i
);
289 tm
|= iinfo
.mandatory
;
290 if(filenames
[i
]->GetValue().Length() != 0)
291 pm
|= iinfo
.mandatory
;
295 void do_fileselect(unsigned i
)
298 if(i
>= ROM_SLOT_COUNT
|| !fileselect
[i
])
301 std::set
<std::string
> exts
;
302 for(auto j
: t
.get_image_info(i
).extensions
)
304 filter
= "Known file types|" + implode_set(exts
) + "|All files|*";
305 std::string directory
;
306 if(t
.get_biosname() != "" && i
== 0)
307 directory
= "firmwarepath";
309 directory
= "rompath";
310 directory
= inst
.setcache
->get(directory
);
311 core_romimage_info iinfo
= t
.get_image_info(i
);
312 wxFileDialog
* d
= new wxFileDialog(this, towxstring("Load " + iinfo
.hname
),
313 towxstring(directory
), wxT(""), towxstring(filter
), wxFD_OPEN
);
314 if(d
->ShowModal() == wxID_CANCEL
) {
318 filenames
[i
]->SetValue(d
->GetPath());
321 void filename_updated(unsigned i
)
324 if(i
>= t
.get_image_count() || !filenames
[i
])
326 uint64_t header
= t
.get_image_info(i
).headersize
;
328 std::string filename
= tostdstring(filenames
[i
]->GetValue());
329 if(!zip::file_exists(filename
)) {
330 hashfutures
[i
] = fileimage::hashval();
331 hash_ready
[i
] = true;
332 hashes
[i
]->SetLabel(towxstring("Not found"));
335 //TODO: Handle files inside ZIP files.
336 hashfutures
[i
] = lsnes_image_hasher(filename
, fileimage::std_headersize_fn(header
));
337 if((hash_ready
[i
] = hashfutures
[i
].ready()))
339 hashes
[i
]->SetLabel(towxstring("Hash: " + hashfutures
[i
].read()));
340 } catch(std::runtime_error
& e
) {
341 hashes
[i
]->SetLabel(towxstring("Hash: Error"));
344 hashes
[i
]->SetLabel(towxstring("Hash: Calculating..."));
345 okb
->Enable(check_requirements());
347 struct update_timer
: public wxTimer
350 update_timer(multirom_dialog
* p
)
356 w
->timer_update_hashes();
361 emulator_instance
& inst
;
363 wxTextCtrl
* filenames
[ROM_SLOT_COUNT
];
364 wxButton
* fileselect
[ROM_SLOT_COUNT
];
365 wxStaticText
* hashes
[ROM_SLOT_COUNT
];
366 bool hash_ready
[ROM_SLOT_COUNT
];
367 fileimage::hashval hashfutures
[ROM_SLOT_COUNT
];
372 std::vector
<core_region
*> regions_known
;
375 void do_load_rom_image_multiple(wxwin_mainwindow
* parent
, emulator_instance
& inst
, core_type
& t
)
378 multirom_dialog
* d
= new multirom_dialog(parent
, inst
, t
.get_hname(), t
);
379 if(d
->ShowModal() == wxID_CANCEL
) {
383 std::string files
[ROM_SLOT_COUNT
];
384 for(unsigned i
= 0; i
< ROM_SLOT_COUNT
; i
++)
385 files
[i
] = d
->getfilename(i
);
386 std::string region
= d
->getregion();
389 recentfiles::multirom mr
;
390 mr
.core
= req
.core
= t
.get_core_identifier();
391 mr
.system
= req
.system
= t
.get_iname();
392 mr
.region
= req
.region
= region
;
393 for(unsigned i
= 0; i
< ROM_SLOT_COUNT
; i
++) {
395 mr
.files
.resize(i
+ 1);
396 mr
.files
[i
] = files
[i
];
398 req
.files
[i
] = files
[i
];
400 parent
->recent_roms
->add(mr
);
401 inst
.iqueue
->run([req
]() {
402 CORE().command
->invoke("unpause-emulator");
409 void wxwin_mainwindow::request_rom(rom_request
& req
)
412 std::vector
<std::string
> choices
;
413 for(auto i
: req
.cores
)
414 choices
.push_back(i
->get_core_identifier());
415 std::string coretext
;
417 if(choices
.size() > 1 && !req
.core_guessed
)
418 coretext
= pick_among(this, "Choose core", "Choose core to load the ROM", choices
,
421 coretext
= choices
[req
.selected
];
422 } catch(canceled_exception
& e
) {
425 for(size_t i
= 0; i
< req
.cores
.size(); i
++)
426 if(coretext
== req
.cores
[i
]->get_core_identifier())
428 core_type
& type
= *req
.cores
[req
.selected
];
430 bool has_bios
= (type
.get_biosname() != "");
431 for(unsigned i
= 0; i
< ROM_SLOT_COUNT
; i
++) {
435 continue; //Leave these alone.
436 if(i
>= type
.get_image_count()) {
437 messages
<< "wxwin_mainwindow::request_rom: Present image index (" << i
438 << ") out of range!" << std::endl
;
439 continue; //Shouldn't happen.
441 core_romimage_info iinfo
= type
.get_image_info(i
);
442 std::string directory
;
443 if(i
== 0 && has_bios
)
444 directory
= "firmwarepath";
446 directory
= "rompath";
447 directory
= inst
.setcache
->get(directory
);
448 std::string _title
= "Select " + iinfo
.hname
;
449 std::string filespec
= "Known ROMs|";
450 std::string exts
= "";
451 std::string defaultname
= "";
452 for(auto j
: iinfo
.extensions
) {
453 exts
= exts
+ ";*." + j
;
454 if(zip::file_exists(directory
+ "/" + req
.filename
[i
] + "." + j
))
455 defaultname
= req
.filename
[i
] + "." + j
;
457 if(exts
!= "") exts
= exts
.substr(1);
458 filespec
= "Known ROMs (" + exts
+ ")|" + exts
+ "|All files|*";
461 uint64_t header
= type
.get_image_info(i
).headersize
;
463 d
= new wxFileDialog(this, towxstring(_title
), towxstring(directory
), wxT(""),
464 towxstring(filespec
), wxFD_OPEN
);
465 if(defaultname
!= "") d
->SetFilename(towxstring(defaultname
));
466 if(d
->ShowModal() == wxID_CANCEL
) {
470 req
.filename
[i
] = tostdstring(d
->GetPath());
473 if(!zip::file_exists(req
.filename
[i
])) {
474 show_message_ok(this, "File not found", "Can't find '" + req
.filename
[i
] + "'",
479 auto future
= lsnes_image_hasher(req
.filename
[i
], fileimage::std_headersize_fn(header
));
480 //Dirty method to run the event loop until hashing finishes.
481 while(!future
.ready()) {
485 std::string hash
= future
.read();
486 if(hash
!= req
.hash
[i
]) {
488 wxMessageDialog
* d3
= new wxMessageDialog(this, towxstring("The ROM checksum does "
489 "not match movie\n\nProceed anyway?"), towxstring("Checksum error"),
490 wxYES_NO
| wxNO_DEFAULT
| wxICON_EXCLAMATION
);
491 int r
= d3
->ShowModal();
493 if(r
== wxID_NO
) goto again
;
496 wxMessageDialog
* d3
= new wxMessageDialog(this, towxstring("Can't read checksum for "
497 "ROM\n\nProceed anyway?"), towxstring("Checksum error"), wxYES_NO
|
498 wxYES_DEFAULT
| wxICON_EXCLAMATION
);
499 int r
= d3
->ShowModal();
501 if(r
== wxID_NO
) goto again
;
504 req
.canceled
= false;
507 void wxwin_mainwindow::do_load_rom_image(core_type
* t
)
510 return do_load_rom_image_single(this, inst
);
512 return do_load_rom_image_multiple(this, inst
, *t
);