Pack movie data in memory
[lsnes.git] / src / plat-wxwidgets / romselect.cpp
blob88f5d7a023bf3487f42096c2a31dd00ec49f96d5
1 //Gaah... wx/wx.h (contains something that breaks if included after snes/snes.hpp from bsnes v085.
2 #include <wx/wx.h>
4 #include "lsnes.hpp"
5 #include <snes/snes.hpp>
6 #include <ui-libsnes/libsnes.hpp>
8 #include "core/moviedata.hpp"
9 #include "core/framerate.hpp"
10 #include "core/zip.hpp"
12 #include "plat-wxwidgets/platform.hpp"
13 #include "plat-wxwidgets/window_romselect.hpp"
15 #define ROM_SELECTS_BASE (wxID_HIGHEST + 0)
16 #define ROM_SELECTS_LAST (wxID_HIGHEST + 127)
17 #define ASK_FILENAME_BUTTON (wxID_HIGHEST + 128)
18 #define ASK_SRAMS_BASE (wxID_HIGHEST + 129)
19 #define ASK_SRAMS_LAST (wxID_HIGHEST + 255)
21 #define CONTROLLERTYPES_P1 4
22 #define CONTROLLERTYPES 7
23 #define CNAME_NONE "None"
24 #define CNAME_GAMEPAD "Gamepad"
25 #define CNAME_MULTITAP "Multitap"
26 #define CNAME_MOUSE "Mouse"
27 #define CNAME_SUPERSCOPE "Superscope"
28 #define CNAME_JUSTIFIER "Justifier"
29 #define CNAME_JUSTIFIERS "2 Justifiers"
30 #define TNAME_SNES "SNES"
31 #define TNAME_BSX_NS "BS-X (non-slotted)"
32 #define TNAME_BSX_S "BS-X (slotted)"
33 #define TNAME_SUFAMITURBO "Sufami Turbo"
34 #define TNAME_SGB "SGB"
35 #define RNAME_AUTO "Autodetect"
36 #define RNAME_NTSC "NTSC"
37 #define RNAME_PAL "PAL"
38 #define WNAME_SNES_MAIN "ROM"
39 #define WNAME_SNES_MAIN_XML "ROM XML"
40 #define WNAME_BS_MAIN "BS-X BIOS"
41 #define WNAME_BS_MAIN_XML "BS-X BIOS XML"
42 #define WNAME_BS_SLOTA "BS FLASH"
43 #define WNAME_BS_SLOTA_XML "BS FLASH XML"
44 #define WNAME_ST_MAIN "ST BIOS"
45 #define WNAME_ST_MAIN_XML "ST BIOS XML"
46 #define WNAME_ST_SLOTA "SLOT A ROM"
47 #define WNAME_ST_SLOTA_XML "SLOT A XML"
48 #define WNAME_ST_SLOTB "SLOT B ROM"
49 #define WNAME_ST_SLOTB_XML "SLOT B XML"
50 #define WNAME_SGB_MAIN "SGB BIOS"
51 #define WNAME_SGB_MAIN_XML "SGB BIOS XML"
52 #define WNAME_SGB_SLOTA "DMG ROM"
53 #define WNAME_SGB_SLOTA_XML "BMG XML"
56 namespace
58 class my_interfaced : public SNES::Interface
60 string path(SNES::Cartridge::Slot slot, const string &hint)
62 return "./";
64 } simple_interface;
66 void enable_slot(wxStaticText* label, wxTextCtrl* filename, wxButton* ask, wxCheckBox* hcb,
67 const std::string& newlabel)
69 label->SetLabel(towxstring(newlabel));
70 filename->Enable();
71 ask->Enable();
72 if(hcb)
73 hcb->Enable();
76 void disable_slot(wxStaticText* label, wxTextCtrl* filename, wxButton* ask, wxCheckBox* hcb)
78 label->SetLabel(wxT(""));
79 filename->Disable();
80 ask->Disable();
81 hcb->Disable();
84 std::string sram_name(const nall::string& _id, SNES::Cartridge::Slot slotname)
86 std::string id(_id, _id.length());
87 if(slotname == SNES::Cartridge::Slot::SufamiTurboA)
88 return "slota." + id.substr(1);
89 if(slotname == SNES::Cartridge::Slot::SufamiTurboB)
90 return "slotb." + id.substr(1);
91 return id.substr(1);
94 porttype_t get_controller_type(const std::string& s)
96 if(s == CNAME_NONE)
97 return PT_NONE;
98 if(s == CNAME_GAMEPAD)
99 return PT_GAMEPAD;
100 if(s == CNAME_MULTITAP)
101 return PT_MULTITAP;
102 if(s == CNAME_MOUSE)
103 return PT_MOUSE;
104 if(s == CNAME_SUPERSCOPE)
105 return PT_SUPERSCOPE;
106 if(s == CNAME_JUSTIFIER)
107 return PT_JUSTIFIER;
108 if(s == CNAME_JUSTIFIERS)
109 return PT_JUSTIFIERS;
110 return PT_INVALID;
113 struct loaded_slot& get_rom_slot(struct loaded_rom& rom, unsigned index)
115 switch(index) {
116 case 0: return rom.rom;
117 case 1: return rom.rom_xml;
118 case 2: return rom.slota;
119 case 3: return rom.slota_xml;
120 case 4: return rom.slotb;
121 case 5: return rom.slotb_xml;
123 return rom.rom;
126 enum rom_region region_from_string(const std::string& str)
128 if(str == RNAME_NTSC)
129 return REGION_NTSC;
130 if(str == RNAME_PAL)
131 return REGION_PAL;
132 return REGION_AUTO;
135 unsigned populate_region_choices(wxString* array)
137 array[0] = wxT(RNAME_AUTO);
138 array[1] = wxT(RNAME_NTSC);
139 array[2] = wxT(RNAME_PAL);
140 return 3;
143 unsigned populate_system_choices(wxString* array)
145 array[0] = wxT(TNAME_SNES);
146 array[1] = wxT(TNAME_BSX_NS);
147 array[2] = wxT(TNAME_BSX_S);
148 array[3] = wxT(TNAME_SUFAMITURBO);
149 array[4] = wxT(TNAME_SGB);
150 return 5;
153 bool check_present_roms(enum rom_type rtype, unsigned flags)
155 switch(rtype)
157 case ROMTYPE_SNES:
158 return ((flags & 1) == 1);
159 case ROMTYPE_BSX:
160 case ROMTYPE_BSXSLOTTED:
161 case ROMTYPE_SGB:
162 return ((flags & 5) == 5);
163 case ROMTYPE_SUFAMITURBO:
164 return ((flags & 1) == 1) && ((flags & 20) != 0);
165 default:
166 return false;
170 wxString romname(enum rom_type rtype, unsigned index)
172 switch(rtype) {
173 case ROMTYPE_SNES:
174 switch(index) {
175 case 0: return wxT(WNAME_SNES_MAIN);
176 case 1: return wxT(WNAME_SNES_MAIN_XML);
178 break;
179 case ROMTYPE_BSX:
180 case ROMTYPE_BSXSLOTTED:
181 switch(index) {
182 case 0: return wxT(WNAME_BS_MAIN);
183 case 1: return wxT(WNAME_BS_MAIN_XML);
184 case 2: return wxT(WNAME_BS_SLOTA);
185 case 3: return wxT(WNAME_BS_SLOTA_XML);
187 break;
188 case ROMTYPE_SUFAMITURBO:
189 switch(index) {
190 case 0: return wxT(WNAME_ST_MAIN);
191 case 1: return wxT(WNAME_ST_MAIN_XML);
192 case 2: return wxT(WNAME_ST_SLOTA);
193 case 3: return wxT(WNAME_ST_SLOTA_XML);
194 case 4: return wxT(WNAME_ST_SLOTB);
195 case 5: return wxT(WNAME_ST_SLOTB_XML);
197 break;
198 case ROMTYPE_SGB:
199 switch(index) {
200 case 0: return wxT(WNAME_SGB_MAIN);
201 case 1: return wxT(WNAME_SGB_MAIN_XML);
202 case 2: return wxT(WNAME_SGB_SLOTA);
203 case 3: return wxT(WNAME_SGB_SLOTA_XML);
205 break;
206 case ROMTYPE_NONE:
207 if(index == 0) return wxT("dummy");
208 break;
210 return wxT("");
213 unsigned romname_to_index(enum rom_type rtype, const wxString& name)
215 for(unsigned i = 0; i < ROMSELECT_ROM_COUNT; i++)
216 if(romname(rtype, i) == name)
217 return i;
218 return ROMSELECT_ROM_COUNT;
221 unsigned fill_rom_names(enum rom_type rtype, wxString* array)
223 unsigned r = 0;
224 for(unsigned i = 0; i < 6; i++) {
225 wxString s = romname(rtype, i);
226 if(s.Length())
227 array[r++] = s;
229 return r;
232 enum rom_type romtype_from_string(const std::string& str)
234 if(str == TNAME_SNES)
235 return ROMTYPE_SNES;
236 if(str == TNAME_BSX_NS)
237 return ROMTYPE_BSX;
238 if(str == TNAME_BSX_S)
239 return ROMTYPE_BSXSLOTTED;
240 if(str == TNAME_SUFAMITURBO)
241 return ROMTYPE_SUFAMITURBO;
242 if(str == TNAME_SGB)
243 return ROMTYPE_SGB;
244 return ROMTYPE_NONE;
247 bool has_forced_region(const std::string& str)
249 enum rom_type rtype = romtype_from_string(str);
250 return (rtype != ROMTYPE_SNES && rtype != ROMTYPE_SGB);
255 wxwin_romselect::wxwin_romselect()
256 : wxFrame(NULL, wxID_ANY, wxT("Select ROM"), wxDefaultPosition, wxSize(-1, -1),
257 wxMINIMIZE_BOX | wxSYSTEM_MENU | wxCAPTION | wxCLIP_CHILDREN | wxCLOSE_BOX)
259 wxString rtchoices[32];
260 wxString rrchoices[32];
261 size_t systems = populate_system_choices(rtchoices);
262 size_t regions = populate_region_choices(rrchoices);
264 Centre();
266 //The toplevel sizer contains three sizers:
267 //- The top bar having ROM type / Region selects.
268 //- The middle area having ROM filename boxes.
269 //- The bottom bar having OK/Quit buttons.
270 wxFlexGridSizer* toplevel = new wxFlexGridSizer(3, 1, 0, 0);
271 SetSizer(toplevel);
273 //The ROM type / Region selects.
274 wxBoxSizer* selects = new wxBoxSizer(wxHORIZONTAL);
275 selects->Add(new wxStaticText(this, wxID_ANY, wxT("ROM type:")), 0, wxGROW);
276 selects->Add(romtype_combo = new wxComboBox(this, wxID_ANY, rtchoices[0], wxDefaultPosition, wxDefaultSize,
277 systems, rtchoices, wxCB_READONLY), 0, wxGROW);
278 selects->Add(new wxStaticText(this, wxID_ANY, wxT("Region:")), 0, wxGROW);
279 selects->Add(region_combo = new wxComboBox(this, wxID_ANY, rrchoices[0], wxDefaultPosition, wxDefaultSize,
280 regions, rrchoices, wxCB_READONLY), 0, wxGROW);
281 romtype_combo->Connect(wxEVT_COMMAND_COMBOBOX_SELECTED,
282 wxCommandEventHandler(wxwin_romselect::on_romtype_change), NULL, this);
283 region_combo->Connect(wxEVT_COMMAND_COMBOBOX_SELECTED,
284 wxCommandEventHandler(wxwin_romselect::on_romtype_change), NULL, this);
285 toplevel->Add(selects, 0, wxGROW);
287 //ROM filename selects
288 wxFlexGridSizer* romgrid = new wxFlexGridSizer(ROMSELECT_ROM_COUNT, 4, 0, 0);
289 for(unsigned i = 0; i < ROMSELECT_ROM_COUNT; i++) {
290 romgrid->Add(rom_label[i] = new wxStaticText(this, wxID_ANY, wxT("")), 0, wxGROW);
291 romgrid->Add(rom_name[i] = new wxTextCtrl(this, wxID_ANY, wxT(""), wxDefaultPosition, wxSize(500, -1)),
292 1, wxGROW);
293 romgrid->Add(rom_change[i] = new wxButton(this, ROM_SELECTS_BASE + i, wxT("...")), 0, wxGROW);
294 romgrid->Add(rom_headered[i] = new wxCheckBox(this, wxID_ANY, wxT("Headered")), 0, wxGROW);
295 rom_name[i]->Connect(wxEVT_COMMAND_TEXT_UPDATED,
296 wxCommandEventHandler(wxwin_romselect::on_filename_change), NULL, this);
297 rom_change[i]->Connect(wxEVT_COMMAND_BUTTON_CLICKED,
298 wxCommandEventHandler(wxwin_romselect::on_ask_rom_filename), NULL, this);
299 rom_headered[i]->Disable();
301 toplevel->Add(romgrid, 1, wxGROW);
303 //Button bar.
304 wxBoxSizer* buttons = new wxBoxSizer(wxHORIZONTAL);
305 buttons->AddStretchSpacer();
306 buttons->Add(open_rom = new wxButton(this, wxID_OPEN, wxT("Open ROM")), 0, wxALIGN_RIGHT);
307 buttons->Add(quit_button = new wxButton(this, wxID_EXIT, wxT("Quit")), 0, wxALIGN_RIGHT);
308 open_rom->Connect(wxEVT_COMMAND_BUTTON_CLICKED,
309 wxCommandEventHandler(wxwin_romselect::on_open_rom), NULL, this);
310 open_rom->Disable();
311 quit_button->Connect(wxEVT_COMMAND_BUTTON_CLICKED,
312 wxCommandEventHandler(wxwin_romselect::on_quit), NULL, this);
313 toplevel->Add(buttons, 1, wxGROW);
315 //Initialize form.
316 set_rtype("");
318 //Display it.
319 toplevel->SetSizeHints(this);
320 Fit();
323 wxwin_romselect::~wxwin_romselect()
327 void wxwin_romselect::set_rtype(std::string rtype)
329 bool no_rtype = (current_rtype == "");
330 if(rtype == "")
331 rtype = tostdstring(romtype_combo->GetValue());
332 if(rtype == current_rtype)
333 return;
334 if(has_forced_region(rtype)) {
335 region_combo->Disable();
336 remembered_region = tostdstring(region_combo->GetValue());
337 } else {
338 if(remembered_region != "")
339 region_combo->SetValue(towxstring(remembered_region));
340 remembered_region = "";
341 region_combo->Enable();
343 wxString tmp[ROMSELECT_ROM_COUNT];
344 unsigned c = fill_rom_names(romtype_from_string(rtype), tmp);
345 for(unsigned i = 0; i < c; i++)
346 enable_slot(rom_label[i], rom_name[i], rom_change[i], (i & 1) ? NULL : rom_headered[i],
347 tostdstring(tmp[i]));
348 for(unsigned i = c; i < ROMSELECT_ROM_COUNT; i++)
349 disable_slot(rom_label[i], rom_name[i], rom_change[i], rom_headered[i]);
350 current_rtype = rtype;
351 Fit();
354 void wxwin_romselect::on_ask_rom_filename(wxCommandEvent& e)
356 std::string fname;
357 wxFileDialog* d = new wxFileDialog(this, towxstring(std::string("Choose ") + tostdstring(
358 rom_label[e.GetId() - ROM_SELECTS_BASE]->GetLabel())), wxT("."));
359 if(d->ShowModal() == wxID_CANCEL) {
360 d->Destroy();
361 return;
363 fname = tostdstring(d->GetPath());
364 d->Destroy();
365 fname = pick_archive_member(this, fname);
366 if(fname == "")
367 return;
368 wxTextCtrl* textbox = rom_name[e.GetId() - ROM_SELECTS_BASE];
369 if(textbox)
370 textbox->SetValue(towxstring(fname));
371 on_filename_change(e);
374 void wxwin_romselect::on_filename_change(wxCommandEvent& e)
376 bool ok = true;
377 enum rom_type rtype = romtype_from_string(tostdstring(romtype_combo->GetValue()));
378 unsigned flags = 0;
379 for(unsigned i = 0; i < ROMSELECT_ROM_COUNT; i++)
380 flags |= ((rom_name[i]->GetValue().Length() != 0) ? (1 << i) : 0);
381 open_rom->Enable(check_present_roms(rtype, flags));
384 void wxwin_romselect::on_romtype_change(wxCommandEvent& e)
386 set_rtype(tostdstring(romtype_combo->GetValue()));
387 on_filename_change(e);
390 void wxwin_romselect::on_quit(wxCommandEvent& e)
392 Close(true);
395 void wxwin_romselect::on_open_rom(wxCommandEvent& e)
397 rom_files rfiles;
398 rfiles.base_file = "";
399 rfiles.rtype = romtype_from_string(tostdstring(romtype_combo->GetValue()));
400 rfiles.region = region_from_string(tostdstring(region_combo->GetValue()));
401 rfiles.rom = tostdstring(rom_name[0]->GetValue());
402 rfiles.rom_xml = tostdstring(rom_name[1]->GetValue());
403 rfiles.slota = tostdstring(rom_name[2]->GetValue());
404 rfiles.slota_xml = tostdstring(rom_name[3]->GetValue());
405 rfiles.slotb = tostdstring(rom_name[4]->GetValue());
406 rfiles.slotb_xml = tostdstring(rom_name[5]->GetValue());
407 rfiles.rom_headered = rom_headered[0]->GetValue();
408 rfiles.slota_headered = rom_headered[2]->GetValue();
409 rfiles.slotb_headered = rom_headered[4]->GetValue();
410 try {
411 our_rom = new loaded_rom(rfiles);
412 } catch(std::exception& e) {
413 wxMessageDialog* d = new wxMessageDialog(this, towxstring(e.what()),
414 wxT("Error loading ROM"), wxOK | wxICON_EXCLAMATION);
415 d->ShowModal();
416 return;
418 wxwin_patch* projwin = new wxwin_patch(*our_rom);
419 projwin->Show();
420 Destroy();
422 //---------------------------------------------------
423 wxwin_patch::wxwin_patch(loaded_rom& rom)
424 : wxFrame(NULL, wxID_ANY, wxT("Patch ROM"), wxDefaultPosition, wxSize(-1, -1),
425 wxMINIMIZE_BOX | wxSYSTEM_MENU | wxCAPTION | wxCLIP_CHILDREN | wxCLOSE_BOX)
427 our_rom = &rom;
428 wxString targets[ROMSELECT_ROM_COUNT];
429 size_t target_count = fill_rom_names(rom.rtype, targets);
431 //Toplevel has 5 blocks:
432 //- Checksums for ROMs.
433 //- Patch what select.
434 //- Filename select.
435 //- Patch offset.
436 //- Button bar.
437 Centre();
438 wxFlexGridSizer* toplevel = new wxFlexGridSizer(5, 1, 0, 0);
439 SetSizer(toplevel);
441 //Checksums block.
442 wxFlexGridSizer* hashes = new wxFlexGridSizer(target_count, 2, 0, 0);
443 for(unsigned i = 0; i < ROMSELECT_ROM_COUNT; i++)
444 checksums[i] = NULL;
445 for(unsigned i = 0; i < target_count; i++) {
446 std::string hash = get_rom_slot(*our_rom, i).sha256;
447 if(hash == "")
448 hash = "<Not present>";
449 hashes->Add(new wxStaticText(this, wxID_ANY, targets[i]), 0, wxGROW);
450 hashes->Add(checksums[i] = new wxStaticText(this, wxID_ANY, towxstring(hash)), 0, wxGROW);
452 toplevel->Add(hashes, 0, wxGROW);
454 //Target select.
455 wxFlexGridSizer* targetselect = new wxFlexGridSizer(1, 2, 0, 0);
456 targetselect->Add(new wxStaticText(this, wxID_ANY, wxT("Patch what:")), 0, wxGROW);
457 targetselect->Add(patch_what = new wxComboBox(this, wxID_ANY, targets[0], wxDefaultPosition, wxDefaultSize,
458 target_count, targets, wxCB_READONLY), 0, wxGROW);
459 toplevel->Add(targetselect, 0, wxGROW);
461 //Patchfile.
462 wxFlexGridSizer* patchsel = new wxFlexGridSizer(1, 3, 0, 0);
463 patchsel->Add(new wxStaticText(this, wxID_ANY, wxT("File:")), 0, wxGROW);
464 patchsel->Add(patchfile = new wxTextCtrl(this, wxID_ANY, wxT(""), wxDefaultPosition, wxSize(500, -1)),
465 1, wxGROW);
466 patchsel->Add(choosefile = new wxButton(this, wxID_ANY, wxT("...")), 0, wxGROW);
467 patchfile->Connect(wxEVT_COMMAND_TEXT_UPDATED,
468 wxCommandEventHandler(wxwin_patch::on_patchfile_change), NULL, this);
469 choosefile->Connect(wxEVT_COMMAND_BUTTON_CLICKED,
470 wxCommandEventHandler(wxwin_patch::on_ask_patchfile), NULL, this);
471 toplevel->Add(patchsel, 0, wxGROW);
473 //Patch offset.
474 wxFlexGridSizer* offsetselect = new wxFlexGridSizer(1, 2, 0, 0);
475 offsetselect->Add(new wxStaticText(this, wxID_ANY, wxT("Patch offset:")), 0, wxGROW);
476 offsetselect->Add(patch_offset = new wxTextCtrl(this, wxID_ANY, wxT("")), 0, wxGROW);
477 patch_offset->Connect(wxEVT_COMMAND_TEXT_UPDATED,
478 wxCommandEventHandler(wxwin_patch::on_patchfile_change), NULL, this);
479 toplevel->Add(offsetselect, 0, wxGROW);
481 //Button bar
482 wxBoxSizer* buttonbar = new wxBoxSizer(wxHORIZONTAL);
483 buttonbar->AddStretchSpacer();
484 wxButton* thats_enough = new wxButton(this, wxID_ANY, wxT("Enough"));
485 buttonbar->Add(thats_enough, 0, wxGROW);
486 thats_enough->Connect(wxEVT_COMMAND_BUTTON_CLICKED,
487 wxCommandEventHandler(wxwin_patch::on_done), NULL, this);
488 dopatch = new wxButton(this, wxID_ANY, wxT("Patch"));
489 buttonbar->Add(dopatch, 0, wxGROW);
490 dopatch->Connect(wxEVT_COMMAND_BUTTON_CLICKED,
491 wxCommandEventHandler(wxwin_patch::on_do_patch), NULL, this);
492 dopatch->Disable();
493 wxButton* quitbutton = new wxButton(this, wxID_EXIT, wxT("Quit"));
494 buttonbar->Add(quitbutton, 0, wxGROW);
495 quitbutton->Connect(wxEVT_COMMAND_BUTTON_CLICKED,
496 wxCommandEventHandler(wxwin_patch::on_quit), NULL, this);
497 toplevel->Add(buttonbar, 0, wxGROW);
499 patch_offset->SetValue(wxT("0"));
501 hashes->SetSizeHints(this);
502 targetselect->SetSizeHints(this);
503 buttonbar->SetSizeHints(this);
504 toplevel->SetSizeHints(this);
505 Fit();
508 void wxwin_patch::on_ask_patchfile(wxCommandEvent& e)
510 std::string fname;
511 wxFileDialog* d = new wxFileDialog(this, wxT("Choose patch file"), wxT("."));
512 if(d->ShowModal() == wxID_CANCEL) {
513 d->Destroy();
514 return;
516 fname = tostdstring(d->GetPath());
517 d->Destroy();
518 fname = pick_archive_member(this, fname);
519 if(fname == "")
520 return;
521 patchfile->SetValue(towxstring(fname));
522 on_patchfile_change(e);
525 wxwin_patch::~wxwin_patch()
529 void wxwin_patch::on_patchfile_change(wxCommandEvent& e)
531 bool ok = true;
532 ok = ok && (patchfile->GetValue().Length() != 0);
533 std::string offsetv = tostdstring(patch_offset->GetValue());
534 try {
535 int32_t offset = boost::lexical_cast<int32_t>(offsetv);
536 } catch(...) {
537 ok = false;
539 if(dopatch)
540 dopatch->Enable(ok);
543 void wxwin_patch::on_do_patch(wxCommandEvent& e)
545 try {
546 auto patch_contents = read_file_relative(tostdstring(patchfile->GetValue()), "");
547 size_t patch_index = romname_to_index(our_rom->rtype, patch_what->GetValue());
548 if(patch_index > ROMSELECT_ROM_COUNT)
549 throw std::runtime_error("Internal error: Patch WHAT?");
550 loaded_slot& s = get_rom_slot(*our_rom, patch_index);
551 std::string offsetv = tostdstring(patch_offset->GetValue());
552 int32_t offset = boost::lexical_cast<int32_t>(offsetv);
553 s.patch(patch_contents, offset);
554 checksums[patch_index]->SetLabel(towxstring(s.sha256));
555 } catch(std::exception& e) {
556 wxMessageDialog* d = new wxMessageDialog(this, towxstring(e.what()),
557 wxT("Error patching ROM"), wxOK | wxICON_EXCLAMATION);
558 d->ShowModal();
559 return;
561 patchfile->SetValue(wxT(""));
564 void wxwin_patch::on_quit(wxCommandEvent& e)
566 Close(true);
569 void wxwin_patch::on_done(wxCommandEvent& e)
571 try {
572 SNES::interface = &simple_interface;
573 our_rom->load();
574 } catch(std::exception& e) {
575 wxMessageDialog* d = new wxMessageDialog(this, towxstring(e.what()),
576 wxT("Error loading ROM"), wxOK | wxICON_EXCLAMATION);
577 d->ShowModal();
578 return;
580 messages << "Detected region: " << gtype::tostring(our_rom->rtype, our_rom->region) << std::endl;
581 if(our_rom->region == REGION_PAL)
582 set_nominal_framerate(322445.0/6448.0);
583 else if(our_rom->region == REGION_NTSC)
584 set_nominal_framerate(10738636.0/178683.0);
586 messages << "--- Internal memory mappings ---" << std::endl;
587 dump_region_map();
588 messages << "--- End of Startup --- " << std::endl;
589 wxwin_project* projwin = new wxwin_project(*our_rom);
590 projwin->Show();
591 Destroy();
593 //------------------------------------------------------------
595 wxwin_project::wxwin_project(loaded_rom& rom)
596 : wxFrame(NULL, wxID_ANY, wxT("Project settings"), wxDefaultPosition, wxSize(-1, -1),
597 wxMINIMIZE_BOX | wxSYSTEM_MENU | wxCAPTION | wxCLIP_CHILDREN | wxCLOSE_BOX)
599 our_rom = &rom;
600 wxString cchoices[CONTROLLERTYPES];
601 cchoices[0] = wxT(CNAME_NONE);
602 cchoices[1] = wxT(CNAME_GAMEPAD);
603 cchoices[2] = wxT(CNAME_MULTITAP);
604 cchoices[3] = wxT(CNAME_MOUSE);
605 cchoices[4] = wxT(CNAME_SUPERSCOPE);
606 cchoices[5] = wxT(CNAME_JUSTIFIER);
607 cchoices[6] = wxT(CNAME_JUSTIFIERS);
609 std::set<std::string> sram_set = get_sram_set();
611 Centre();
612 //6 Top-level blocks.
613 //- Radiobutton for load
614 //- Radiobutton for new.
615 //- Filename/Controllertypes/initRTC/Gamename/SRAMs.
616 //- Authors explanation.
617 //- Authors
618 //- Button bar.
619 wxFlexGridSizer* toplevel = new wxFlexGridSizer(6, 1, 0, 0);
620 SetSizer(toplevel);
622 //Radiobutton for load.
623 wxRadioButton* file = new wxRadioButton(this, wxID_ANY, wxT("Load movie/savestate"), wxDefaultPosition,
624 wxDefaultSize, wxRB_GROUP);
625 file->Connect(wxEVT_COMMAND_RADIOBUTTON_SELECTED,
626 wxCommandEventHandler(wxwin_project::on_file_select), NULL, this);
627 toplevel->Add(file, 0, wxGROW);
628 load_file = true;
630 //Radiobutton for new proect.
631 wxRadioButton* newp = new wxRadioButton(this, wxID_ANY, wxT("New project"));
632 newp->Connect(wxEVT_COMMAND_RADIOBUTTON_SELECTED,
633 wxCommandEventHandler(wxwin_project::on_new_select), NULL, this);
634 toplevel->Add(newp, 0, wxGROW);
636 //Filename/Controllertypes/Gamename/initRTC/SRAMs.
637 wxFlexGridSizer* mainblock = new wxFlexGridSizer(5 + sram_set.size(), 2, 0, 0);
638 mainblock->Add(new wxStaticText(this, wxID_ANY, wxT("File to load:")), 0, wxGROW);
639 wxFlexGridSizer* fileblock = new wxFlexGridSizer(1, 2, 0, 0);
640 fileblock->Add(savefile = new wxTextCtrl(this, wxID_ANY, wxT(""), wxDefaultPosition, wxSize(500, -1)),
641 1, wxGROW);
642 fileblock->Add(ask_savefile = new wxButton(this, ASK_FILENAME_BUTTON, wxT("...")), 0, wxGROW);
643 savefile->Connect(wxEVT_COMMAND_TEXT_UPDATED,
644 wxCommandEventHandler(wxwin_project::on_filename_change), NULL, this);
645 ask_savefile->Connect(wxEVT_COMMAND_BUTTON_CLICKED,
646 wxCommandEventHandler(wxwin_project::on_ask_filename), NULL, this);
647 mainblock->Add(fileblock, 0, wxGROW);
648 mainblock->Add(new wxStaticText(this, wxID_ANY, wxT("Controller 1 Type:")), 0, wxGROW);
649 mainblock->Add(controller1type = new wxComboBox(this, wxID_ANY, cchoices[1], wxDefaultPosition, wxDefaultSize,
650 CONTROLLERTYPES_P1, cchoices, wxCB_READONLY), 0, wxGROW);
651 mainblock->Add(new wxStaticText(this, wxID_ANY, wxT("Controller 2 Type:")), 0, wxGROW);
652 mainblock->Add(controller2type = new wxComboBox(this, wxID_ANY, cchoices[0], wxDefaultPosition, wxDefaultSize,
653 CONTROLLERTYPES, cchoices, wxCB_READONLY), 0, wxGROW);
654 mainblock->Add(new wxStaticText(this, wxID_ANY, wxT("Initial RTC value:")), 0, wxGROW);
655 wxFlexGridSizer* initrtc = new wxFlexGridSizer(1, 3, 0, 0);
656 initrtc->Add(rtc_sec = new wxTextCtrl(this, wxID_ANY, wxT("1000000000"), wxDefaultPosition, wxSize(150, -1)),
657 1, wxGROW);
658 initrtc->Add(new wxStaticText(this, wxID_ANY, wxT(":")), 0, wxGROW);
659 initrtc->Add(rtc_subsec = new wxTextCtrl(this, wxID_ANY, wxT("0"), wxDefaultPosition,
660 wxSize(150, -1)), 1, wxGROW);
661 mainblock->Add(initrtc, 0, wxGROW);
662 mainblock->Add(new wxStaticText(this, wxID_ANY, wxT("Game name:")), 0, wxGROW);
663 mainblock->Add(projectname = new wxTextCtrl(this, wxID_ANY, wxT(""), wxDefaultPosition, wxSize(400, -1)), 1,
664 wxGROW);
665 unsigned idx = 0;
666 for(auto i : sram_set) {
667 mainblock->Add(new wxStaticText(this, wxID_ANY, towxstring("SRAM " + i)), 0, wxGROW);
668 wxFlexGridSizer* fileblock2 = new wxFlexGridSizer(1, 2, 0, 0);
669 fileblock2->Add(sram_files[i] = new wxTextCtrl(this, wxID_ANY, wxT(""), wxDefaultPosition,
670 wxSize(500, -1)), 1, wxGROW);
671 fileblock2->Add(sram_choosers[i] = new wxButton(this, ASK_SRAMS_BASE + idx, wxT("...")), 0, wxGROW);
672 sram_files[i]->Connect(wxEVT_COMMAND_TEXT_UPDATED,
673 wxCommandEventHandler(wxwin_project::on_filename_change), NULL, this);
674 sram_choosers[i]->Connect(wxEVT_COMMAND_BUTTON_CLICKED,
675 wxCommandEventHandler(wxwin_project::on_ask_filename), NULL, this);
676 mainblock->Add(fileblock2, 0, wxGROW);
677 sram_names[idx] = i;
678 idx++;
680 toplevel->Add(mainblock, 0, wxGROW);
682 //Authors
683 toplevel->Add(new wxStaticText(this, wxID_ANY, wxT("Authors (one per line):")), 0, wxGROW);
684 toplevel->Add(authors = new wxTextCtrl(this, wxID_ANY, wxT(""), wxDefaultPosition, wxDefaultSize,
685 wxTE_MULTILINE), 0, wxGROW);
686 authors->Connect(wxEVT_COMMAND_TEXT_UPDATED, wxCommandEventHandler(wxwin_project::on_filename_change), NULL,
687 this);
689 //Button bar.
690 wxBoxSizer* buttonbar = new wxBoxSizer(wxHORIZONTAL);
691 buttonbar->AddStretchSpacer();
692 buttonbar->Add(load = new wxButton(this, wxID_ANY, wxT("Load")), 0, wxGROW);
693 buttonbar->Add(quit = new wxButton(this, wxID_EXIT, wxT("Quit")), 0, wxGROW);
694 load->Connect(wxEVT_COMMAND_BUTTON_CLICKED,
695 wxCommandEventHandler(wxwin_project::on_load), NULL, this);
696 quit->Connect(wxEVT_COMMAND_BUTTON_CLICKED,
697 wxCommandEventHandler(wxwin_project::on_quit), NULL, this);
698 toplevel->Add(buttonbar, 0, wxGROW);
701 wxCommandEvent e;
702 on_file_select(e);
704 mainblock->SetSizeHints(this);
705 toplevel->SetSizeHints(this);
706 Fit();
709 wxwin_project::~wxwin_project()
713 void wxwin_project::on_file_select(wxCommandEvent& e)
715 savefile->Enable();
716 ask_savefile->Enable();
717 controller1type->Disable();
718 controller2type->Disable();
719 rtc_sec->Disable();
720 rtc_subsec->Disable();
721 projectname->Disable();
722 authors->Disable();
723 load->SetLabel(wxT("Load"));
724 load_file = true;
725 for(auto i : sram_files)
726 i.second->Disable();
727 for(auto i : sram_choosers)
728 i.second->Disable();
729 on_filename_change(e);
732 void wxwin_project::on_new_select(wxCommandEvent& e)
734 savefile->Disable();
735 ask_savefile->Disable();
736 controller1type->Enable();
737 controller2type->Enable();
738 rtc_sec->Enable();
739 rtc_subsec->Enable();
740 projectname->Enable();
741 authors->Enable();
742 load->SetLabel(wxT("Start"));
743 on_filename_change(e);
744 load_file = false;
745 for(auto i : sram_files)
746 i.second->Enable();
747 for(auto i : sram_choosers)
748 i.second->Enable();
749 on_filename_change(e);
752 void wxwin_project::on_ask_filename(wxCommandEvent& e)
754 int id = e.GetId();
755 if(id == ASK_FILENAME_BUTTON) {
756 std::string fname;
757 wxFileDialog* d = new wxFileDialog(this, towxstring("Choose " + sram_names[id - ASK_SRAMS_BASE]),
758 wxT("."));
759 if(d->ShowModal() == wxID_CANCEL) {
760 d->Destroy();
761 return;
763 fname = tostdstring(d->GetPath());
764 d->Destroy();
765 if(fname == "")
766 return;
767 savefile->SetValue(towxstring(fname));
768 } else if(id >= ASK_SRAMS_BASE && id <= ASK_SRAMS_LAST) {
769 std::string fname;
770 wxFileDialog* d = new wxFileDialog(this, towxstring("Choose " + sram_names[id - ASK_SRAMS_BASE]),
771 wxT("."));
772 if(d->ShowModal() == wxID_CANCEL) {
773 d->Destroy();
774 return;
776 fname = tostdstring(d->GetPath());
777 d->Destroy();
778 if(fname == "")
779 return;
780 fname = pick_archive_member(this, fname);
781 if(fname == "")
782 return;
783 sram_files[sram_names[id - ASK_SRAMS_BASE]]->SetValue(towxstring(fname));
785 on_filename_change(e);
788 void wxwin_project::on_filename_change(wxCommandEvent& e)
790 if(load_file) {
791 load->Enable(savefile->GetValue().Length() != 0);
792 } else {
793 try {
794 boost::lexical_cast<int64_t>(tostdstring(rtc_sec->GetValue()));
795 if(boost::lexical_cast<int64_t>(tostdstring(rtc_subsec->GetValue())) < 0)
796 throw 42;
797 size_t lines = authors->GetNumberOfLines();
798 for(size_t i = 0; i < lines; i++) {
799 std::string l = tostdstring(authors->GetLineText(i));
800 if(l == "|")
801 throw 43;
803 load->Enable();
804 } catch(...) {
805 load->Disable();
810 void wxwin_project::on_quit(wxCommandEvent& e)
812 Close(true);
815 void wxwin_project::on_load(wxCommandEvent& e)
817 try {
818 if(load_file) {
819 boot_emulator(*our_rom, *new moviefile(tostdstring(savefile->GetValue())));
820 } else {
821 boot_emulator(*our_rom, *new moviefile(make_movie()));
823 Destroy();
824 } catch(std::exception& e) {
825 wxMessageDialog* d = new wxMessageDialog(this, towxstring(e.what()),
826 wxT("Error loading movie"), wxOK | wxICON_EXCLAMATION);
827 d->ShowModal();
828 return;
832 std::set<std::string> wxwin_project::get_sram_set()
834 std::set<std::string> r;
835 for(unsigned i = 0; i < SNES::cartridge.nvram.size(); i++) {
836 SNES::Cartridge::NonVolatileRAM& s = SNES::cartridge.nvram[i];
837 r.insert(sram_name(s.id, s.slot));
839 return r;
842 struct moviefile wxwin_project::make_movie()
844 moviefile f;
845 f.force_corrupt = false;
846 f.gametype = gtype::togametype(our_rom->rtype, our_rom->region);
847 f.port1 = get_controller_type(tostdstring(controller1type->GetValue()));
848 f.port2 = get_controller_type(tostdstring(controller2type->GetValue()));
849 f.coreversion = bsnes_core_version;
850 f.gamename = tostdstring(projectname->GetValue());
851 f.projectid = get_random_hexstring(40);
852 f.rerecords = "0";
853 f.rom_sha256 = our_rom->rom.sha256;
854 f.romxml_sha256 = our_rom->rom_xml.sha256;
855 f.slota_sha256 = our_rom->slota.sha256;
856 f.slotaxml_sha256 = our_rom->slota_xml.sha256;
857 f.slotb_sha256 = our_rom->slotb.sha256;
858 f.slotbxml_sha256 = our_rom->slotb_xml.sha256;
859 size_t lines = authors->GetNumberOfLines();
860 for(size_t i = 0; i < lines; i++) {
861 std::string l = tostdstring(authors->GetLineText(i));
862 if(l != "" && l != "|")
863 f.authors.push_back(split_author(l));
865 for(auto i : sram_files) {
866 std::string sf = tostdstring(i.second->GetValue());
867 if(sf != "")
868 f.movie_sram[i.first] = read_file_relative(sf, "");
870 f.is_savestate = false;
871 f.movie_rtc_second = f.rtc_second = boost::lexical_cast<int64_t>(tostdstring(rtc_sec->GetValue()));
872 f.movie_rtc_subsecond = f.rtc_subsecond = boost::lexical_cast<int64_t>(tostdstring(rtc_subsec->GetValue()));
873 if(f.movie_rtc_subsecond < 0)
874 throw std::runtime_error("RTC subsecond must be positive");
875 f.input.clear(f.port1, f.port2);
876 return f;