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/settings.hpp"
8 #include "core/window.hpp"
9 #include "interface/romtype.hpp"
10 #include "library/zip.hpp"
14 std::string
rom_path()
16 return lsnes_instance
.setcache
.get("rompath");
19 bool can_load_singlefile(core_type
* t
)
21 unsigned icnt
= t
->get_image_count();
22 unsigned pmand
= 0, tmand
= 0;
23 if(!icnt
) return false;
24 if(icnt
> 0) pmand
|= t
->get_image_info(0).mandatory
;
25 if(t
->get_biosname() != "" && icnt
> 1) pmand
|= t
->get_image_info(1).mandatory
;
26 for(unsigned i
= 0; i
< t
->get_image_count(); i
++)
27 tmand
|= t
->get_image_info(i
).mandatory
;
28 return pmand
== tmand
;
31 std::string
implode_set(const std::set
<std::string
>& s
)
41 int resolve_core(std::map
<unsigned, core_type
*> coreid
, const std::string
& filename
, int findex
)
43 if(coreid
.count(findex
))
44 return findex
; //Already resolved.
45 if(loaded_rom::is_gamepak(filename
))
46 return 0; //Gamepaks don't resolve.
49 regex_results r
= regex(".*\\.([^.]+)", filename
);
51 return 0; //WTF is this? Leave unresolved.
52 std::string extension
= r
[1];
54 std::map
<core_type
*, unsigned> candidates
;
55 for(auto i
: coreid
) {
56 if(!can_load_singlefile(i
.second
))
58 if(i
.second
->is_hidden())
60 if(i
.second
->isnull())
62 std::set
<std::string
> exts
;
63 unsigned base
= (i
.second
->get_biosname() != "" && i
.second
->get_image_count() > 1) ? 1 : 0;
64 for(auto j
: i
.second
->get_image_info(base
).extensions
)
66 //This is a candidate.
67 candidates
[i
.second
] = i
.first
;
71 if(candidates
.empty())
72 return 0; //Err. Leave unresolved.
73 if(candidates
.size() == 1)
74 return candidates
.begin()->second
; //Only one candidate.
76 //Okay, we have multiple candidates. Prompt among them.
77 std::vector
<std::string
> choices
;
78 std::vector
<unsigned> indexes
;
79 for(auto i
: candidates
) {
80 choices
.push_back(i
.first
->get_core_identifier() + " [" + i
.first
->get_hname() + "]");
81 indexes
.push_back(i
.second
);
84 coretext
= pick_among(NULL
, "Choose core", "Choose core to load the ROM", choices
, 0);
85 for(size_t i
= 0; i
< choices
.size(); i
++)
86 if(choices
[i
] == coretext
)
92 void do_load_rom_image_single(wxwin_mainwindow
* parent
)
94 std::map
<std::string
, core_type
*> cores
;
95 std::map
<unsigned, core_type
*> coreid
;
97 unsigned corecount
= 0;
98 std::set
<std::string
> all_filetypes
;
99 all_filetypes
.insert("lsgp");
100 for(auto i
: core_type::get_core_types()) {
101 if(!can_load_singlefile(i
))
103 unsigned base
= (i
->get_biosname() != "" && i
->get_image_count() > 1) ? 1 : 0;
104 for(auto j
: i
->get_image_info(base
).extensions
)
105 all_filetypes
.insert(j
);
106 cores
[i
->get_hname() + " [" + i
->get_core_identifier() + "]"] = i
;
108 filter
+= "Autodetect|" + implode_set(all_filetypes
);
109 for(auto i
: cores
) {
110 if(!can_load_singlefile(i
.second
))
112 if(i
.second
->is_hidden())
114 if(i
.second
->isnull())
116 std::set
<std::string
> exts
;
117 unsigned base
= (i
.second
->get_biosname() != "" && i
.second
->get_image_count() > 1) ? 1 : 0;
118 for(auto j
: i
.second
->get_image_info(base
).extensions
)
120 filter
+= "|" + i
.first
+ "|" + implode_set(exts
);
121 coreid
[++corecount
] = i
.second
;
123 filter
+= "|All files|*";
124 std::string directory
= lsnes_instance
.setcache
.get("rompath");
125 wxFileDialog
* d
= new wxFileDialog(parent
, towxstring("Choose ROM to load"), towxstring(directory
),
126 wxT(""), towxstring(filter
), wxFD_OPEN
);
127 if(d
->ShowModal() == wxID_CANCEL
) {
132 recentfiles::multirom mr
;
133 std::string filename
= tostdstring(d
->GetPath());
134 int findex
= d
->GetFilterIndex();
136 findex
= resolve_core(coreid
, filename
, findex
);
137 } catch(canceled_exception
& e
) {
140 if(!coreid
.count(findex
)) {
142 mr
.packfile
= req
.packfile
= filename
;
144 mr
.core
= req
.core
= coreid
[findex
]->get_core_identifier();
145 mr
.system
= req
.system
= coreid
[findex
]->get_iname();
146 mr
.singlefile
= req
.singlefile
= filename
;
148 parent
->recent_roms
->add(mr
);
149 lsnes_instance
.iqueue
.run_async([req
]() {
150 lsnes_instance
.command
.invoke("unpause-emulator");
152 }, [](std::exception
& e
) {});
155 class multirom_dialog
: public wxDialog
158 void on_wclose(wxCloseEvent
& e
)
160 EndModal(wxID_CANCEL
);
162 multirom_dialog(wxWindow
* parent
, std::string rtype
, core_type
& _t
)
163 : wxDialog(parent
, wxID_ANY
, towxstring("lsnes: Load " + rtype
+ " ROM")), 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);
231 void timer_update_hashes()
233 for(auto i
= 0; i
< ROM_SLOT_COUNT
&& filenames
[i
]; i
++) {
236 if(!hashfutures
[i
].ready())
239 hashes
[i
]->SetLabel(towxstring("Hash: " + hashfutures
[i
].read()));
240 } catch(std::runtime_error
& e
) {
241 hashes
[i
]->SetLabel(towxstring("Hash: Error"));
243 hash_ready
[i
] = true;
246 void do_command_event(wxCommandEvent
& e
)
249 if(id
== wxID_HIGHEST
+ 1) {
250 if(!check_requirements()) {
251 show_message_ok(this, "Needed ROM missing", "At least one required ROM "
252 "is missing!", wxICON_EXCLAMATION
);
256 } else if(id
== wxID_HIGHEST
+ 2) {
257 EndModal(wxID_CANCEL
);
258 } else if(id
>= wxID_HIGHEST
+ 100 && id
<= wxID_HIGHEST
+ 199) {
259 filename_updated(id
- (wxID_HIGHEST
+ 100));
260 } else if(id
>= wxID_HIGHEST
+ 200 && id
<= wxID_HIGHEST
+ 299) {
261 do_fileselect(id
- (wxID_HIGHEST
+ 200));
264 std::string
getfilename(unsigned i
)
266 if(i
>= ROM_SLOT_COUNT
|| !filenames
[i
])
268 return tostdstring(filenames
[i
]->GetValue());
270 std::string
getregion()
272 int i
= regions
->GetSelection();
274 return t
.get_preferred_region().get_iname();
275 return regions_known
[i
]->get_iname();
278 bool check_requirements()
280 unsigned pm
= 0, tm
= 0;
281 for(unsigned i
= 0; i
< t
.get_image_count(); i
++) {
282 core_romimage_info iinfo
= t
.get_image_info(i
);
283 tm
|= iinfo
.mandatory
;
284 if(filenames
[i
]->GetValue().Length() != 0)
285 pm
|= iinfo
.mandatory
;
289 void do_fileselect(unsigned i
)
291 if(i
>= ROM_SLOT_COUNT
|| !fileselect
[i
])
294 std::set
<std::string
> exts
;
295 for(auto j
: t
.get_image_info(i
).extensions
)
297 filter
= "Known file types|" + implode_set(exts
) + "|All files|*";
298 std::string directory
;
299 if(t
.get_biosname() != "" && i
== 0)
300 directory
= "firmwarepath";
302 directory
= "rompath";
303 directory
= lsnes_instance
.setcache
.get(directory
);
304 core_romimage_info iinfo
= t
.get_image_info(i
);
305 wxFileDialog
* d
= new wxFileDialog(this, towxstring("Load " + iinfo
.hname
),
306 towxstring(directory
), wxT(""), towxstring(filter
), wxFD_OPEN
);
307 if(d
->ShowModal() == wxID_CANCEL
) {
311 filenames
[i
]->SetValue(d
->GetPath());
314 void filename_updated(unsigned i
)
316 if(i
>= t
.get_image_count() || !filenames
[i
])
318 uint64_t header
= t
.get_image_info(i
).headersize
;
320 std::string filename
= tostdstring(filenames
[i
]->GetValue());
321 if(!zip::file_exists(filename
)) {
322 hashfutures
[i
] = fileimage::hashval();
323 hash_ready
[i
] = true;
324 hashes
[i
]->SetLabel(towxstring("Not found"));
327 //TODO: Handle files inside ZIP files.
328 hashfutures
[i
] = lsnes_image_hasher(filename
, fileimage::std_headersize_fn(header
));
329 if((hash_ready
[i
] = hashfutures
[i
].ready()))
331 hashes
[i
]->SetLabel(towxstring("Hash: " + hashfutures
[i
].read()));
332 } catch(std::runtime_error
& e
) {
333 hashes
[i
]->SetLabel(towxstring("Hash: Error"));
336 hashes
[i
]->SetLabel(towxstring("Hash: Calculating..."));
337 okb
->Enable(check_requirements());
339 struct update_timer
: public wxTimer
342 update_timer(multirom_dialog
* p
)
348 w
->timer_update_hashes();
354 wxTextCtrl
* filenames
[ROM_SLOT_COUNT
];
355 wxButton
* fileselect
[ROM_SLOT_COUNT
];
356 wxStaticText
* hashes
[ROM_SLOT_COUNT
];
357 bool hash_ready
[ROM_SLOT_COUNT
];
358 fileimage::hashval hashfutures
[ROM_SLOT_COUNT
];
363 std::vector
<core_region
*> regions_known
;
366 void do_load_rom_image_multiple(wxwin_mainwindow
* parent
, core_type
& t
)
368 multirom_dialog
* d
= new multirom_dialog(parent
, t
.get_hname(), t
);
369 if(d
->ShowModal() == wxID_CANCEL
) {
373 std::string files
[ROM_SLOT_COUNT
];
374 for(unsigned i
= 0; i
< ROM_SLOT_COUNT
; i
++)
375 files
[i
] = d
->getfilename(i
);
376 std::string region
= d
->getregion();
379 recentfiles::multirom mr
;
380 mr
.core
= req
.core
= t
.get_core_identifier();
381 mr
.system
= req
.system
= t
.get_iname();
382 mr
.region
= req
.region
= region
;
383 for(unsigned i
= 0; i
< ROM_SLOT_COUNT
; i
++) {
385 mr
.files
.resize(i
+ 1);
386 mr
.files
[i
] = files
[i
];
388 req
.files
[i
] = files
[i
];
390 parent
->recent_roms
->add(mr
);
391 lsnes_instance
.iqueue
.run([req
]() {
392 lsnes_instance
.command
.invoke("unpause-emulator");
399 void wxwin_mainwindow::request_rom(rom_request
& req
)
401 std::vector
<std::string
> choices
;
402 for(auto i
: req
.cores
)
403 choices
.push_back(i
->get_core_identifier());
404 std::string coretext
;
406 if(choices
.size() > 1 && !req
.core_guessed
)
407 coretext
= pick_among(this, "Choose core", "Choose core to load the ROM", choices
,
410 coretext
= choices
[req
.selected
];
411 } catch(canceled_exception
& e
) {
414 for(size_t i
= 0; i
< req
.cores
.size(); i
++)
415 if(coretext
== req
.cores
[i
]->get_core_identifier())
417 core_type
& type
= *req
.cores
[req
.selected
];
419 bool has_bios
= (type
.get_biosname() != "");
420 for(unsigned i
= 0; i
< ROM_SLOT_COUNT
; i
++) {
424 continue; //Leave these alone.
425 if(i
>= type
.get_image_count()) {
426 messages
<< "wxwin_mainwindow::request_rom: Present image index (" << i
427 << ") out of range!" << std::endl
;
428 continue; //Shouldn't happen.
430 core_romimage_info iinfo
= type
.get_image_info(i
);
431 std::string directory
;
432 if(i
== 0 && has_bios
)
433 directory
= "firmwarepath";
435 directory
= "rompath";
436 directory
= lsnes_instance
.setcache
.get(directory
);
437 std::string _title
= "Select " + iinfo
.hname
;
438 std::string filespec
= "Known ROMs|";
439 std::string exts
= "";
440 std::string defaultname
= "";
441 for(auto j
: iinfo
.extensions
) {
442 exts
= exts
+ ";*." + j
;
443 if(zip::file_exists(directory
+ "/" + req
.filename
[i
] + "." + j
))
444 defaultname
= req
.filename
[i
] + "." + j
;
446 if(exts
!= "") exts
= exts
.substr(1);
447 filespec
= "Known ROMs (" + exts
+ ")|" + exts
+ "|All files|*";
450 uint64_t header
= type
.get_image_info(i
).headersize
;
452 d
= new wxFileDialog(this, towxstring(_title
), towxstring(directory
), wxT(""),
453 towxstring(filespec
), wxFD_OPEN
);
454 if(defaultname
!= "") d
->SetFilename(towxstring(defaultname
));
455 if(d
->ShowModal() == wxID_CANCEL
) {
459 req
.filename
[i
] = tostdstring(d
->GetPath());
462 if(!zip::file_exists(req
.filename
[i
])) {
463 show_message_ok(this, "File not found", "Can't find '" + req
.filename
[i
] + "'",
468 auto future
= lsnes_image_hasher(req
.filename
[i
], fileimage::std_headersize_fn(header
));
469 //Dirty method to run the event loop until hashing finishes.
470 while(!future
.ready()) {
474 std::string hash
= future
.read();
475 if(hash
!= req
.hash
[i
]) {
477 wxMessageDialog
* d3
= new wxMessageDialog(this, towxstring("The ROM checksum does "
478 "not match movie\n\nProceed anyway?"), towxstring("Checksum error"),
479 wxYES_NO
| wxNO_DEFAULT
| wxICON_EXCLAMATION
);
480 int r
= d3
->ShowModal();
482 if(r
== wxID_NO
) goto again
;
485 wxMessageDialog
* d3
= new wxMessageDialog(this, towxstring("Can't read checksum for "
486 "ROM\n\nProceed anyway?"), towxstring("Checksum error"), wxYES_NO
|
487 wxYES_DEFAULT
| wxICON_EXCLAMATION
);
488 int r
= d3
->ShowModal();
490 if(r
== wxID_NO
) goto again
;
493 req
.canceled
= false;
496 void wxwin_mainwindow::do_load_rom_image(core_type
* t
)
499 return do_load_rom_image_single(this);
501 return do_load_rom_image_multiple(this, *t
);