3 #include <wx/control.h>
4 #include <wx/combobox.h>
5 #include <wx/radiobut.h>
6 #include "platform/wxwidgets/platform.hpp"
7 #include "platform/wxwidgets/loadsave.hpp"
8 #include "core/messages.hpp"
9 #include "core/misc.hpp"
10 #include "core/window.hpp"
11 #include "library/directory.hpp"
12 #include "library/filelist.hpp"
13 #include "library/loadlib.hpp"
14 #include "library/string.hpp"
15 #include "library/zip.hpp"
19 #if defined(_WIN32) || defined(_WIN64)
24 //TODO: Handle plugin blacklist/killlist
27 std::set
<std::string
> failed_plugins
;
28 std::string killlist_file
= "/killlist";
29 std::string blacklist_file
= "/blacklist";
31 std::string
string_add_list(std::string a
, std::string b
)
39 std::string
get_name(std::string path
)
41 #if defined(_WIN32) || defined(_WIN64)
42 const char* sep
= "\\/";
44 const char* sep
= "/";
46 size_t p
= path
.find_last_of(sep
);
48 if(p
== std::string::npos
)
51 name
= path
.substr(p
+ 1);
55 std::string
strip_extension(std::string tmp
, std::string ext
)
57 regex_results r
= regex("(.*)\\." + ext
, tmp
);
63 class wxeditor_plugins
: public wxDialog
66 wxeditor_plugins(wxWindow
* parent
);
67 void on_selection_change(wxCommandEvent
& e
);
68 void on_add(wxCommandEvent
& e
);
69 void on_rename(wxCommandEvent
& e
);
70 void on_enable(wxCommandEvent
& e
);
71 void on_delete(wxCommandEvent
& e
);
72 void on_start(wxCommandEvent
& e
);
73 void on_close(wxCommandEvent
& e
);
75 void reload_plugins();
76 filelist
& get_blacklist();
77 filelist
& get_killlist();
80 wxButton
* renamebutton
;
81 wxButton
* enablebutton
;
82 wxButton
* deletebutton
;
83 wxButton
* startbutton
;
84 wxButton
* closebutton
;
85 std::vector
<std::pair
<std::string
, bool>> pluginstbl
;
86 std::string extension
;
90 wxeditor_plugins::wxeditor_plugins(wxWindow
* parent
)
91 : wxDialog(parent
, wxID_ANY
, wxT("lsnes: Plugin manager"), wxDefaultPosition
, wxSize(-1, -1))
95 wxFlexGridSizer
* top_s
= new wxFlexGridSizer(2, 1, 0, 0);
97 pathpfx
= get_config_path() + "/autoload";
98 extension
= loadlib::library::extension();
100 top_s
->Add(plugins
= new wxListBox(this, wxID_ANY
, wxDefaultPosition
, wxSize(400, 300)), 1, wxGROW
);
101 plugins
->Connect(wxEVT_COMMAND_LISTBOX_SELECTED
,
102 wxCommandEventHandler(wxeditor_plugins::on_selection_change
), NULL
, this);
104 wxBoxSizer
* pbutton_s
= new wxBoxSizer(wxHORIZONTAL
);
105 pbutton_s
->Add(addbutton
= new wxButton(this, wxID_ANY
, wxT("Add")), 0, wxGROW
);
106 pbutton_s
->Add(renamebutton
= new wxButton(this, wxID_ANY
, wxT("Rename")), 0, wxGROW
);
107 pbutton_s
->Add(enablebutton
= new wxButton(this, wxID_ANY
, wxT("Enable")), 0, wxGROW
);
108 pbutton_s
->Add(deletebutton
= new wxButton(this, wxID_ANY
, wxT("Delete")), 0, wxGROW
);
109 pbutton_s
->AddStretchSpacer();
111 pbutton_s
->Add(startbutton
= new wxButton(this, wxID_ANY
, wxT("Start")), 0, wxGROW
);
115 pbutton_s
->Add(closebutton
= new wxButton(this, wxID_EXIT
, wxT("Quit")), 0, wxGROW
);
117 pbutton_s
->Add(closebutton
= new wxButton(this, wxID_ANY
, wxT("Close")), 0, wxGROW
);
118 addbutton
->Connect(wxEVT_COMMAND_BUTTON_CLICKED
, wxCommandEventHandler(wxeditor_plugins::on_add
), NULL
,
120 renamebutton
->Connect(wxEVT_COMMAND_BUTTON_CLICKED
, wxCommandEventHandler(wxeditor_plugins::on_rename
), NULL
,
122 enablebutton
->Connect(wxEVT_COMMAND_BUTTON_CLICKED
, wxCommandEventHandler(wxeditor_plugins::on_enable
), NULL
,
124 deletebutton
->Connect(wxEVT_COMMAND_BUTTON_CLICKED
, wxCommandEventHandler(wxeditor_plugins::on_delete
), NULL
,
127 startbutton
->Connect(wxEVT_COMMAND_BUTTON_CLICKED
, wxCommandEventHandler(wxeditor_plugins::on_start
),
129 closebutton
->Connect(wxEVT_COMMAND_BUTTON_CLICKED
, wxCommandEventHandler(wxeditor_plugins::on_close
), NULL
,
131 top_s
->Add(pbutton_s
, 0, wxGROW
);
134 on_selection_change(e
);
138 void wxeditor_plugins::reload_plugins()
141 int sel
= plugins
->GetSelection();
143 if(sel
== wxNOT_FOUND
|| sel
>= (ssize_t
)pluginstbl
.size())
146 name
= pluginstbl
[sel
].first
;
148 if(!directory::ensure_exists(pathpfx
)) {
149 throw std::runtime_error("Can't create plugins directory");
151 auto dir
= directory::enumerate(pathpfx
, ".*\\." + extension
);
155 std::string name
= get_name(i
);
156 regex_results r
= regex("(.*)\\." + extension
, name
);
158 //Blacklisted plugins should be listed as disabled. Killlisted plugins shouldn't appear at all.
159 bool is_disabled
= false;
160 auto blacklist
= get_blacklist().enumerate();
161 auto killlist
= get_killlist().enumerate();
162 if(killlist
.count(name
))
164 is_disabled
= blacklist
.count(name
);
165 pluginstbl
.push_back(std::make_pair(r
[1], !is_disabled
));
166 std::string r1
= r
[1];
167 std::string attributes
;
168 if(is_disabled
) attributes
= string_add_list(attributes
, "disabled");
169 if(failed_plugins
.count(r
[1] + "." + extension
)) attributes
= string_add_list(attributes
, "failed");
170 if(attributes
.length()) attributes
= " (" + attributes
+ ")";
171 plugins
->Append(towxstring(r1
+ attributes
));
174 for(size_t i
= 0; i
< pluginstbl
.size(); i
++) {
175 if(pluginstbl
[i
].first
== name
)
176 plugins
->SetSelection(i
);
179 on_selection_change(e
);
182 void wxeditor_plugins::on_selection_change(wxCommandEvent
& e
)
185 int sel
= plugins
->GetSelection();
186 if(sel
== wxNOT_FOUND
|| sel
>= (ssize_t
)pluginstbl
.size()) {
187 renamebutton
->Enable(false);
188 enablebutton
->Enable(false);
189 deletebutton
->Enable(false);
191 enablebutton
->SetLabel(towxstring(pluginstbl
[sel
].second
? "Disable" : "Enable"));
192 renamebutton
->Enable(true);
193 enablebutton
->Enable(true);
194 deletebutton
->Enable(true);
198 void wxeditor_plugins::on_add(wxCommandEvent
& e
)
202 std::string file
= choose_file_load(this, "Choose plugin to add", ".",
203 single_type(loadlib::library::extension(), loadlib::library::name()));
204 std::string name
= strip_extension(get_name(file
), extension
);
205 std::string nname
= pathpfx
+ "/" + name
+ "." + extension
;
206 bool overwrite_ok
= false;
209 while(!overwrite_ok
&& directory::exists(nname
)) {
211 wxMessageDialog
* d3
= new wxMessageDialog(this,
212 towxstring("Plugin '" + name
+ "' already exists.\n\nOverwrite?"),
213 towxstring("Plugin already exists"),
214 wxYES_NO
| wxCANCEL
| wxNO_DEFAULT
| wxICON_QUESTION
);
215 int r
= d3
->ShowModal();
220 if(r
== wxID_CANCEL
) {
225 nname
= pathpfx
+ "/" + name
+ "(" + (stringfmt() << counter
++).str() + ")." + extension
;
227 std::string nnamet
= nname
+ ".tmp";
228 std::ifstream
in(file
, std::ios::binary
);
229 std::ofstream
out(nnamet
, std::ios::binary
);
231 show_message_ok(this, "Error", "Can't write file '" + nnamet
+ "'", wxICON_EXCLAMATION
);
236 remove(nnamet
.c_str());
237 show_message_ok(this, "Error", "Can't read file '" + file
+ "'", wxICON_EXCLAMATION
);
244 r
= in
.readsome(buf
, sizeof(buf
));
250 remove(nnamet
.c_str());
251 show_message_ok(this, "Error", "Can't write file '" + nnamet
+ "'", wxICON_EXCLAMATION
);
256 #if defined(_WIN32) || defined(_WIN64)
259 if(stat(file
.c_str(), &s
) < 0)
261 if(s
.st_mode
& 0400) s
.st_mode
|= 0100;
262 if(s
.st_mode
& 040) s
.st_mode
|= 010;
263 if(s
.st_mode
& 04) s
.st_mode
|= 01;
264 chmod(nnamet
.c_str(), s
.st_mode
& 0777);
266 if(directory::rename_overwrite(nnamet
.c_str(), nname
.c_str())) {
267 remove(nnamet
.c_str());
268 show_message_ok(this, "Error", "Can't rename-over file '" + nname
+ "'",
273 //The new plugin isn't failed.
274 failed_plugins
.erase(get_name(nname
));
275 //Nor is it on killlist/blacklist
276 try { get_blacklist().remove(get_name(nname
)); } catch(...) {}
277 try { get_killlist().remove(get_name(nname
)); } catch(...) {}
279 } catch(canceled_exception
& e
) {
283 void wxeditor_plugins::on_rename(wxCommandEvent
& e
)
286 int sel
= plugins
->GetSelection();
287 if(sel
== wxNOT_FOUND
|| sel
>= (ssize_t
)pluginstbl
.size())
289 std::string name
= pluginstbl
[sel
].first
;
292 name2
= pick_text(this, "Rename plugin to", "Enter new name for plugin", name
, false);
293 } catch(canceled_exception
& e
) {
296 std::string oname
= pathpfx
+ "/" + name
+ "." + extension
;
297 std::string nname
= pathpfx
+ "/" + name2
+ "." + extension
;
299 directory::rename_overwrite(oname
.c_str(), nname
.c_str());
301 pluginstbl
[sel
].first
= name2
;
302 if(failed_plugins
.count(name
+ "." + extension
)) {
303 failed_plugins
.insert(name2
+ "." + extension
);
304 failed_plugins
.erase(name
+ "." + extension
);
306 failed_plugins
.erase(name2
+ "." + extension
);
307 try { get_blacklist().rename(name
+ "." + extension
, name2
+ "." + extension
); } catch(...) {}
311 void wxeditor_plugins::on_enable(wxCommandEvent
& e
)
314 int sel
= plugins
->GetSelection();
315 if(sel
== wxNOT_FOUND
|| sel
>= (ssize_t
)pluginstbl
.size())
318 if(pluginstbl
[sel
].second
)
319 get_blacklist().add(pluginstbl
[sel
].first
+ "." + extension
);
321 get_blacklist().remove(pluginstbl
[sel
].first
+ "." + extension
);
322 } catch(std::exception
& e
) {
323 show_message_ok(this, "Error", "Can't enable/disable plugin '" + pluginstbl
[sel
].first
+
324 "': " + e
.what(), wxICON_EXCLAMATION
);
328 pluginstbl
[sel
].second
= !pluginstbl
[sel
].second
;
332 void wxeditor_plugins::on_delete(wxCommandEvent
& e
)
335 int sel
= plugins
->GetSelection();
336 if(sel
== wxNOT_FOUND
|| sel
>= (ssize_t
)pluginstbl
.size())
338 std::string oname
= pathpfx
+ "/" + pluginstbl
[sel
].first
+ "." + extension
;
339 if(remove(oname
.c_str()) < 0) {
341 try { get_killlist().add(pluginstbl
[sel
].first
+ "." + extension
); } catch(...) {}
343 failed_plugins
.erase(pluginstbl
[sel
].first
+ "." + extension
);
347 void wxeditor_plugins::on_start(wxCommandEvent
& e
)
353 void wxeditor_plugins::on_close(wxCommandEvent
& e
)
356 EndModal(wxID_CANCEL
);
359 filelist
& wxeditor_plugins::get_blacklist()
361 static filelist
x(pathpfx
+ blacklist_file
, pathpfx
);
365 filelist
& wxeditor_plugins::get_killlist()
367 static filelist
x(pathpfx
+ killlist_file
, pathpfx
);
371 bool wxeditor_plugin_manager_display(wxWindow
* parent
)
375 modal_pause_holder
* hld
= NULL
;
378 hld
= new modal_pause_holder();
381 editor
= new wxeditor_plugins(parent
);
382 r
= editor
->ShowModal();
383 } catch(std::exception
& e
) {
385 messages
<< "Error opening plugin dialog: " << e
.what() << std::endl
;
387 std::cerr
<< "Error opening plugin dialog: " << e
.what() << std::endl
;
396 return (r
== wxID_OK
);
403 void wxeditor_plugin_manager_notify_fail(const std::string
& libname
)
405 failed_plugins
.insert(libname
);