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/mainloop.hpp"
6 #include "core/settings.hpp"
7 #include "core/window.hpp"
8 #include "interface/romtype.hpp"
9 #include "library/zip.hpp"
13 std::string
rom_path()
15 return lsnes_vset
["rompath"].str();
18 bool can_load_singlefile(core_type
* t
)
20 unsigned icnt
= t
->get_image_count();
21 unsigned pmand
= 0, tmand
= 0;
22 if(!icnt
) return false;
23 if(icnt
> 0) pmand
|= t
->get_image_info(0).mandatory
;
24 if(t
->get_biosname() != "" && icnt
> 1) pmand
|= t
->get_image_info(1).mandatory
;
25 for(unsigned i
= 0; i
< t
->get_image_count(); i
++)
26 tmand
|= t
->get_image_info(i
).mandatory
;
27 return pmand
== tmand
;
30 std::string
implode_set(const std::set
<std::string
>& s
)
40 void do_load_rom_image_single(wxwin_mainwindow
* parent
)
42 std::map
<std::string
, core_type
*> cores
;
43 std::map
<unsigned, core_type
*> coreid
;
45 unsigned corecount
= 0;
46 std::set
<std::string
> all_filetypes
;
47 all_filetypes
.insert("lsgp");
48 for(auto i
: core_type::get_core_types()) {
49 if(!can_load_singlefile(i
))
51 unsigned base
= (i
->get_biosname() != "" && i
->get_image_count() > 1) ? 1 : 0;
52 for(auto j
: i
->get_image_info(base
).extensions
)
53 all_filetypes
.insert(j
);
54 cores
[i
->get_hname() + " [" + i
->get_core_identifier() + "]"] = i
;
56 filter
+= "Autodetect|" + implode_set(all_filetypes
);
58 if(!can_load_singlefile(i
.second
))
60 if(i
.second
->is_hidden())
62 if(i
.second
->isnull())
64 std::set
<std::string
> exts
;
65 unsigned base
= (i
.second
->get_biosname() != "" && i
.second
->get_image_count() > 1) ? 1 : 0;
66 for(auto j
: i
.second
->get_image_info(base
).extensions
)
68 filter
+= "|" + i
.first
+ "|" + implode_set(exts
);
69 coreid
[++corecount
] = i
.second
;
71 filter
+= "|All files|*";
72 std::string directory
= lsnes_vset
["rompath"].str();
73 wxFileDialog
* d
= new wxFileDialog(parent
, towxstring("Choose ROM to load"), towxstring(directory
),
74 wxT(""), towxstring(filter
), wxFD_OPEN
);
75 if(d
->ShowModal() == wxID_CANCEL
) {
80 recentfile_multirom mr
;
81 std::string filename
= tostdstring(d
->GetPath());
82 int findex
= d
->GetFilterIndex();
83 if(!coreid
.count(findex
)) {
85 mr
.packfile
= req
.packfile
= filename
;
87 mr
.core
= req
.core
= coreid
[findex
]->get_core_identifier();
88 mr
.system
= req
.system
= coreid
[findex
]->get_iname();
89 mr
.singlefile
= req
.singlefile
= filename
;
91 parent
->recent_roms
->add(mr
);
93 lsnes_cmd
.invoke("unpause-emulator");
98 class multirom_dialog
: public wxDialog
101 void on_wclose(wxCloseEvent
& e
)
103 EndModal(wxID_CANCEL
);
105 multirom_dialog(wxWindow
* parent
, std::string rtype
, core_type
& _t
)
106 : wxDialog(parent
, wxID_ANY
, towxstring("lsnes: Load " + rtype
+ " ROM")), t(_t
)
109 wxSizer
* vsizer
= new wxBoxSizer(wxVERTICAL
);
111 Connect(wxEVT_CLOSE_WINDOW
, wxCloseEventHandler(multirom_dialog::on_wclose
), NULL
, this);
113 wxSizer
* hsizer2
= new wxBoxSizer(wxHORIZONTAL
);
114 hsizer2
->Add(new wxStaticText(this, wxID_ANY
, wxT("Region: ")), 0, wxGROW
);
115 std::vector
<wxString
> regions_list
;
116 core_region
& prefr
= t
.get_preferred_region();
117 unsigned regindex
= 0;
118 for(auto i
: t
.get_regions()) {
119 if(i
== &prefr
) regindex
= regions_list
.size();
120 regions_list
.push_back(towxstring(i
->get_hname()));
121 regions_known
.push_back(i
);
123 regions
= new wxComboBox(this, wxID_ANY
, regions_list
[regindex
], wxDefaultPosition
,
124 wxDefaultSize
, regions_list
.size(), ®ions_list
[0], wxCB_READONLY
);
125 hsizer2
->Add(regions
, 0, wxGROW
);
126 vsizer
->Add(hsizer2
, 0, wxGROW
);
128 wxSizer
* rarray
= new wxFlexGridSizer(2 * t
.get_image_count(), 3, 0, 0);
129 for(unsigned i
= 0; i
< ROM_SLOT_COUNT
; i
++) {
131 fileselect
[i
] = NULL
;
133 for(unsigned i
= 0; i
< t
.get_image_count(); i
++) {
134 core_romimage_info iinfo
= t
.get_image_info(i
);
135 rarray
->Add(new wxStaticText(this, wxID_ANY
, towxstring(iinfo
.hname
)), 0, wxGROW
);
136 rarray
->Add(filenames
[i
] = new wxTextCtrl(this, wxID_HIGHEST
+ 100 + i
, wxT(""),
137 wxDefaultPosition
, wxSize(400, -1)), 1, wxGROW
);
138 rarray
->Add(fileselect
[i
] = new wxButton(this, wxID_HIGHEST
+ 200 + i
,
139 towxstring("...")), 0, wxGROW
);
140 rarray
->Add(new wxStaticText(this, wxID_ANY
, towxstring("")), 0, wxGROW
);
141 rarray
->Add(hashes
[i
] = new wxStaticText(this, wxID_ANY
, wxT("Not found")), 1,
143 rarray
->Add(new wxStaticText(this, wxID_ANY
, towxstring("")), 0, wxGROW
);
144 hash_ready
[i
] = true;
145 filenames
[i
]->Connect(wxEVT_COMMAND_TEXT_UPDATED
,
146 wxCommandEventHandler(multirom_dialog::do_command_event
), NULL
, this);
147 fileselect
[i
]->Connect(wxEVT_COMMAND_BUTTON_CLICKED
,
148 wxCommandEventHandler(multirom_dialog::do_command_event
), NULL
, this);
150 vsizer
->Add(rarray
, 1, wxGROW
);
152 wxSizer
* buttonbar
= new wxBoxSizer(wxHORIZONTAL
);
153 buttonbar
->Add(okb
= new wxButton(this, wxID_HIGHEST
+ 1, wxT("Load")), 0, wxGROW
);
154 buttonbar
->AddStretchSpacer();
155 buttonbar
->Add(cancelb
= new wxButton(this, wxID_HIGHEST
+ 2, wxT("Cancel")), 0, wxGROW
);
156 vsizer
->Add(buttonbar
, 0, wxGROW
);
157 okb
->Connect(wxEVT_COMMAND_BUTTON_CLICKED
,
158 wxCommandEventHandler(multirom_dialog::do_command_event
), NULL
, this);
160 cancelb
->Connect(wxEVT_COMMAND_BUTTON_CLICKED
,
161 wxCommandEventHandler(multirom_dialog::do_command_event
), NULL
, this);
162 timer
= new update_timer(this);
164 vsizer
->SetSizeHints(this);
174 void timer_update_hashes()
176 for(auto i
= 0; i
< ROM_SLOT_COUNT
&& filenames
[i
]; i
++) {
179 if(!hashfutures
[i
].ready())
182 hashes
[i
]->SetLabel(towxstring("Hash: " + hashfutures
[i
].read()));
183 } catch(std::runtime_error
& e
) {
184 hashes
[i
]->SetLabel(towxstring("Hash: Error"));
186 hash_ready
[i
] = true;
189 void do_command_event(wxCommandEvent
& e
)
192 if(id
== wxID_HIGHEST
+ 1) {
193 if(!check_requirements()) {
194 show_message_ok(this, "Needed ROM missing", "At least one required ROM "
195 "is missing!", wxICON_EXCLAMATION
);
199 } else if(id
== wxID_HIGHEST
+ 2) {
200 EndModal(wxID_CANCEL
);
201 } else if(id
>= wxID_HIGHEST
+ 100 && id
<= wxID_HIGHEST
+ 199) {
202 filename_updated(id
- (wxID_HIGHEST
+ 100));
203 } else if(id
>= wxID_HIGHEST
+ 200 && id
<= wxID_HIGHEST
+ 299) {
204 do_fileselect(id
- (wxID_HIGHEST
+ 200));
207 std::string
getfilename(unsigned i
)
209 if(i
>= ROM_SLOT_COUNT
|| !filenames
[i
])
211 return tostdstring(filenames
[i
]->GetValue());
213 std::string
getregion()
215 int i
= regions
->GetSelection();
217 return t
.get_preferred_region().get_iname();
218 return regions_known
[i
]->get_iname();
221 bool check_requirements()
223 unsigned pm
= 0, tm
= 0;
224 for(unsigned i
= 0; i
< t
.get_image_count(); i
++) {
225 core_romimage_info iinfo
= t
.get_image_info(i
);
226 tm
|= iinfo
.mandatory
;
227 if(filenames
[i
]->GetValue().Length() != 0)
228 pm
|= iinfo
.mandatory
;
232 void do_fileselect(unsigned i
)
234 if(i
>= ROM_SLOT_COUNT
|| !fileselect
[i
])
237 std::set
<std::string
> exts
;
238 for(auto j
: t
.get_image_info(i
).extensions
)
240 filter
= "Known file types|" + implode_set(exts
) + "|All files|*";
241 std::string directory
;
242 if(t
.get_biosname() != "" && i
== 0)
243 directory
= "firmwarepath";
245 directory
= "rompath";
246 directory
= lsnes_vset
[directory
].str();
247 core_romimage_info iinfo
= t
.get_image_info(i
);
248 wxFileDialog
* d
= new wxFileDialog(this, towxstring("Load " + iinfo
.hname
),
249 towxstring(directory
), wxT(""), towxstring(filter
), wxFD_OPEN
);
250 if(d
->ShowModal() == wxID_CANCEL
) {
254 filenames
[i
]->SetValue(d
->GetPath());
257 void filename_updated(unsigned i
)
259 if(i
>= t
.get_image_count() || !filenames
[i
])
261 uint64_t header
= t
.get_image_info(i
).headersize
;
263 std::string filename
= tostdstring(filenames
[i
]->GetValue());
264 if(!file_exists_zip(filename
)) {
265 hashfutures
[i
] = sha256_future();
266 hash_ready
[i
] = true;
267 hashes
[i
]->SetLabel(towxstring("Not found"));
270 //TODO: Handle files inside ZIP files.
271 hashfutures
[i
] = lsnes_image_hasher(filename
, std_headersize_fn(header
));
272 if(hash_ready
[i
] = hashfutures
[i
].ready())
274 hashes
[i
]->SetLabel(towxstring("Hash: " + hashfutures
[i
].read()));
275 } catch(std::runtime_error
& e
) {
276 hashes
[i
]->SetLabel(towxstring("Hash: Error"));
279 hashes
[i
]->SetLabel(towxstring("Hash: Calculating..."));
280 okb
->Enable(check_requirements());
282 struct update_timer
: public wxTimer
285 update_timer(multirom_dialog
* p
)
291 w
->timer_update_hashes();
297 wxTextCtrl
* filenames
[ROM_SLOT_COUNT
];
298 wxButton
* fileselect
[ROM_SLOT_COUNT
];
299 wxStaticText
* hashes
[ROM_SLOT_COUNT
];
300 bool hash_ready
[ROM_SLOT_COUNT
];
301 sha256_future hashfutures
[ROM_SLOT_COUNT
];
306 std::vector
<core_region
*> regions_known
;
309 void do_load_rom_image_multiple(wxwin_mainwindow
* parent
, core_type
& t
)
311 multirom_dialog
* d
= new multirom_dialog(parent
, t
.get_hname(), t
);
312 if(d
->ShowModal() == wxID_CANCEL
) {
316 std::string files
[ROM_SLOT_COUNT
];
317 for(unsigned i
= 0; i
< ROM_SLOT_COUNT
; i
++)
318 files
[i
] = d
->getfilename(i
);
319 std::string region
= d
->getregion();
322 recentfile_multirom mr
;
323 mr
.core
= req
.core
= t
.get_core_identifier();
324 mr
.system
= req
.system
= t
.get_iname();
325 mr
.region
= req
.region
= region
;
326 for(unsigned i
= 0; i
< ROM_SLOT_COUNT
; i
++) {
328 mr
.files
.resize(i
+ 1);
329 mr
.files
[i
] = files
[i
];
331 req
.files
[i
] = files
[i
];
333 parent
->recent_roms
->add(mr
);
335 lsnes_cmd
.invoke("unpause-emulator");
342 void wxwin_mainwindow::request_rom(rom_request
& req
)
344 std::vector
<std::string
> choices
;
345 for(auto i
: req
.cores
)
346 choices
.push_back(i
->get_core_identifier());
347 std::string coretext
;
349 if(choices
.size() > 1 && !req
.core_guessed
)
350 coretext
= pick_among(this, "Choose core", "Choose core to load the ROM", choices
,
353 coretext
= choices
[req
.selected
];
354 } catch(canceled_exception
& e
) {
357 for(size_t i
= 0; i
< req
.cores
.size(); i
++)
358 if(coretext
== req
.cores
[i
]->get_core_identifier())
360 core_type
& type
= *req
.cores
[req
.selected
];
362 bool has_bios
= (type
.get_biosname() != "");
363 for(unsigned i
= 0; i
< ROM_SLOT_COUNT
; i
++) {
367 continue; //Leave these alone.
368 if(i
>= type
.get_image_count()) {
369 messages
<< "wxwin_mainwindow::request_rom: Present image index (" << i
370 << ") out of range!" << std::endl
;
371 continue; //Shouldn't happen.
373 core_romimage_info iinfo
= type
.get_image_info(i
);
374 std::string directory
;
375 if(i
== 0 && has_bios
)
376 directory
= "firmwarepath";
378 directory
= "rompath";
379 directory
= lsnes_vset
[directory
].str();
380 std::string _title
= "Select " + iinfo
.hname
;
381 std::string filespec
= "Known ROMs|";
382 std::string exts
= "";
383 std::string defaultname
= "";
384 for(auto j
: iinfo
.extensions
) {
385 exts
= exts
+ ";*." + j
;
386 if(file_exists_zip(directory
+ "/" + req
.filename
[i
] + "." + j
))
387 defaultname
= req
.filename
[i
] + "." + j
;
389 if(exts
!= "") exts
= exts
.substr(1);
390 filespec
= "Known ROMs (" + exts
+ ")|" + exts
+ "|All files|*";
393 uint64_t header
= type
.get_image_info(i
).headersize
;
395 d
= new wxFileDialog(this, towxstring(_title
), towxstring(directory
), wxT(""),
396 towxstring(filespec
), wxFD_OPEN
);
397 if(defaultname
!= "") d
->SetFilename(towxstring(defaultname
));
398 if(d
->ShowModal() == wxID_CANCEL
) {
402 req
.filename
[i
] = tostdstring(d
->GetPath());
405 if(!file_exists_zip(req
.filename
[i
])) {
406 show_message_ok(this, "File not found", "Can't find '" + req
.filename
[i
] + "'",
411 auto future
= lsnes_image_hasher(req
.filename
[i
], std_headersize_fn(header
));
412 //Dirty method to run the event loop until hashing finishes.
413 while(!future
.ready()) {
417 std::string hash
= future
.read();
418 if(hash
!= req
.hash
[i
]) {
420 wxMessageDialog
* d3
= new wxMessageDialog(this, towxstring("The ROM checksum does "
421 "not match movie\n\nProceed anyway?"), towxstring("Checksum error"),
422 wxYES_NO
| wxNO_DEFAULT
| wxICON_EXCLAMATION
);
423 int r
= d3
->ShowModal();
425 if(r
== wxID_NO
) goto again
;
428 wxMessageDialog
* d3
= new wxMessageDialog(this, towxstring("Can't read checksum for "
429 "ROM\n\nProceed anyway?"), towxstring("Checksum error"), wxYES_NO
|
430 wxYES_DEFAULT
| wxICON_EXCLAMATION
);
431 int r
= d3
->ShowModal();
433 if(r
== wxID_NO
) goto again
;
436 req
.canceled
= false;
439 void wxwin_mainwindow::do_load_rom_image(core_type
* t
)
442 return do_load_rom_image_single(this);
444 return do_load_rom_image_multiple(this, *t
);