lsnes rr2-β24
[lsnes.git] / src / core / loadlib.cpp
blob15e5f4c003b838cde1cf626a3388f72d332149c9
1 #include "cmdhelp/loadlib.hpp"
2 #include "core/command.hpp"
3 #include "core/dispatch.hpp"
4 #include "core/instance.hpp"
5 #include "core/loadlib.hpp"
6 #include "core/messages.hpp"
7 #include "core/misc.hpp"
8 #include "core/rom.hpp"
9 #include "interface/c-interface.hpp"
10 #include "interface/romtype.hpp"
11 #include "library/command.hpp"
12 #include "library/directory.hpp"
13 #include "library/filelist.hpp"
14 #include "library/loadlib.hpp"
15 #include "library/opus.hpp"
16 #include "library/running-executable.hpp"
18 #include <stdexcept>
19 #include <sstream>
20 #include <dirent.h>
22 namespace
24 std::map<unsigned, loadlib::module*> modules;
25 unsigned next_mod = 0;
27 std::string get_name(std::string path)
29 #if defined(_WIN32) || defined(_WIN64)
30 const char* sep = "\\/";
31 #else
32 const char* sep = "/";
33 #endif
34 size_t p = path.find_last_of(sep);
35 std::string name;
36 if(p == std::string::npos)
37 name = path;
38 else
39 name = path.substr(p + 1);
40 return name;
43 std::string get_user_library_dir()
45 return get_config_path() + "/autoload";
48 std::string get_system_library_dir()
50 std::string path;
51 try {
52 path = running_executable();
53 } catch(...) {
54 return "";
56 #if __WIN32__ || __WIN64__
57 const char* sep = "\\/";
58 #else
59 const char* sep = "/";
60 #endif
61 size_t p = path.find_last_of(sep);
62 if(p >= path.length())
63 path = ".";
64 else if(p == 0)
65 path = "/";
66 else
67 path = path.substr(0, p);
68 #if !__WIN32__ && !__WIN64__
69 //If executable is in /bin, translate library path to corresponding /lib/lsnes.
70 regex_results r = regex("(.*)/bin", path);
71 if(r) path = r[1] + "/lib/lsnes";
72 else
73 #endif
74 path = path + "/plugins";
75 return path;
79 void handle_post_loadlibrary()
81 if(new_core_flag) {
82 new_core_flag = false;
83 core_core::initialize_new_cores();
84 notify_new_core();
88 void with_loaded_library(const loadlib::module& l)
90 try {
91 if(!opus::libopus_loaded())
92 opus::load_libopus(l);
93 } catch(...) {
94 //This wasn't libopus.
96 try {
97 module_loading = &l;
98 try_init_c_module(l);
99 module_loading = NULL;
100 } catch(...) {
101 module_loading = NULL;
102 //Ignored.
104 messages << "Loaded library '" << l.get_libname() << "'" << std::endl;
105 modules[next_mod++] = const_cast<loadlib::module*>(&l);
108 bool with_unloaded_library(loadlib::module& l)
110 //Check that this module is not needed.
111 if(!CORE().rom->get_internal_rom_type().safe_to_unload(l)) {
112 messages << "Current core is from this module, can't unload" << std::endl;
113 return false;
115 try {
116 try_uninit_c_module(l);
117 } catch(...) {
119 messages << "Unloading library '" << l.get_libname() << "'" << std::endl;
120 //Spot removed cores.
121 notify_new_core();
122 delete &l;
123 notify_new_core(); //In case only unload made it go away.
124 return true;
127 namespace
129 void load_libraries(std::set<std::string> libs, bool system,
130 void(*on_error)(const std::string& libname, const std::string& err, bool system))
132 std::set<std::string> blacklist;
133 std::set<std::string> killlist;
134 //System plugins can't be killlisted nor blacklisted.
135 if(!system) {
136 filelist _blacklist(get_user_library_dir() + "/blacklist", get_user_library_dir());
137 filelist _killlist(get_user_library_dir() + "/killlist", get_user_library_dir());
138 killlist = _killlist.enumerate();
139 blacklist = _blacklist.enumerate();
140 //Try to kill the libs that don't exist anymore.
141 for(auto i : libs)
142 if(killlist.count(get_name(i)))
143 remove(i.c_str());
144 killlist = _killlist.enumerate();
145 //All killlisted plugins are automatically blacklisted.
146 for(auto i : killlist)
147 blacklist.insert(i);
150 std::string extension = loadlib::library::extension();
151 for(auto i : libs) {
152 if(i.length() < extension.length() + 1)
153 continue;
154 if(i[i.length() - extension.length() - 1] != '.')
155 continue;
156 std::string tmp = i;
157 if(tmp.substr(i.length() - extension.length()) != extension)
158 continue;
159 if(blacklist.count(get_name(i)))
160 continue;
161 try {
162 with_loaded_library(*new loadlib::module(loadlib::library(i)));
163 } catch(std::exception& e) {
164 std::string x = "Can't load '" + i + "': " + e.what();
166 if(on_error)
167 on_error(get_name(i), e.what(), system);
168 messages << x << std::endl;
173 command::fnptr<command::arg_filename> CMD_load_library(lsnes_cmds, CLOADLIB::load,
174 [](command::arg_filename args) throw(std::bad_alloc, std::runtime_error) {
175 with_loaded_library(*new loadlib::module(loadlib::library(args)));
176 handle_post_loadlibrary();
179 command::fnptr<const std::string&> CMD_unload_library(lsnes_cmds, CLOADLIB::unload,
180 [](const std::string& args) throw(std::bad_alloc, std::runtime_error) {
181 unsigned libid = parse_value<unsigned>(args);
182 if(!modules.count(libid))
183 throw std::runtime_error("No such library loaded");
184 if(with_unloaded_library(*modules[libid]))
185 modules.erase(libid);
188 command::fnptr<> CMD_list_library(lsnes_cmds, CLOADLIB::list,
189 []() throw(std::bad_alloc, std::runtime_error) {
190 for(auto i : modules)
191 messages << "#" << i.first << " [" << i.second->get_libname() << "]" << std::endl;
195 void autoload_libraries(void(*on_error)(const std::string& libname, const std::string& err, bool system))
197 try {
198 auto libs = directory::enumerate(get_user_library_dir(), ".*");
199 load_libraries(libs, false, on_error);
200 } catch(std::exception& e) {
201 messages << e.what() << std::endl;
203 try {
204 auto libs = directory::enumerate(get_system_library_dir(), ".*");
205 load_libraries(libs, true, on_error);
206 } catch(std::exception& e) {
207 messages << e.what() << std::endl;
209 handle_post_loadlibrary();
212 std::string loadlib_debug_get_user_library_dir() { return get_user_library_dir(); }
213 std::string loadlib_debug_get_system_library_dir() { return get_system_library_dir(); }