Namespace library port-controller stuff
[lsnes.git] / src / core / misc.cpp
bloba2f396c266c2a1a6764a425fda3a1a9c87754365
1 #include "lsnes.hpp"
3 #include "core/command.hpp"
4 #include "core/controllerframe.hpp"
5 #include "core/dispatch.hpp"
6 #include "core/instance.hpp"
7 #include "core/messages.hpp"
8 #include "core/misc.hpp"
9 #include "core/movie.hpp"
10 #include "core/window.hpp"
11 #include "library/crandom.hpp"
12 #include "library/directory.hpp"
13 #include "library/hex.hpp"
14 #include "library/loadlib.hpp"
15 #include "library/string.hpp"
17 #include <cstdlib>
18 #include <csignal>
19 #include <sstream>
20 #include <iostream>
21 #include <iomanip>
22 #include <fstream>
23 #include <string>
24 #include <vector>
25 #include <algorithm>
26 #include <ctime>
27 #include <cstdlib>
28 #include <cstring>
29 #include <sys/time.h>
30 #include <unistd.h>
31 #include <boost/filesystem.hpp>
32 #if defined(_WIN32) || defined(_WIN64)
33 #include <windows.h>
34 #endif
36 #ifdef USE_LIBGCRYPT_SHA256
37 #include <gcrypt.h>
38 #endif
40 namespace
42 bool reached_main_flag;
44 void fatal_signal_handler(int sig)
46 write(2, "Caught fatal signal!\n", 21);
47 if(lsnes_instance.mlogic) emerg_save_movie(lsnes_instance.mlogic->get_mfile(),
48 lsnes_instance.mlogic->get_rrdata());
49 signal(sig, SIG_DFL);
50 raise(sig);
53 void terminate_handler()
55 write(2, "Terminating abnormally!\n", 24);
56 if(lsnes_instance.mlogic) emerg_save_movie(lsnes_instance.mlogic->get_mfile(),
57 lsnes_instance.mlogic->get_rrdata());
58 std::cerr << "Exiting on fatal error" << std::endl;
59 exit(1);
62 command::fnptr<const std::string&> test4(lsnes_cmds, "panicsave-movie", "", "",
63 [](const std::string& args) throw(std::bad_alloc, std::runtime_error) {
64 auto& core = CORE();
65 if(*core.mlogic) emerg_save_movie(core.mlogic->get_mfile(), core.mlogic->get_rrdata());
66 });
68 //% is intentionally missing.
69 const char* allowed_filename_chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
70 "^&'@{}[],$?!-#().+~_";
73 std::string safe_filename(const std::string& str)
75 std::ostringstream o;
76 for(size_t i = 0; i < str.length(); i++) {
77 unsigned char ch = static_cast<unsigned char>(str[i]);
78 if(strchr(allowed_filename_chars, ch))
79 o << str[i];
80 else
81 o << "%" << hex::to8(ch);
83 return o.str();
86 void fatal_error() throw()
88 platform::fatal_error();
89 std::cout << "PANIC: Fatal error, can't continue." << std::endl;
90 exit(1);
93 std::string get_config_path() throw(std::bad_alloc)
95 const char* tmp;
96 std::string basedir;
97 if((tmp = getenv("APPDATA"))) {
98 //If $APPDATA exists, it is the base directory
99 basedir = tmp;
100 } else if((tmp = getenv("XDG_CONFIG_HOME"))) {
101 //If $XDG_CONFIG_HOME exists, it is the base directory
102 basedir = tmp;
103 } else if((tmp = getenv("HOME"))) {
104 //If $HOME exists, the base directory is '.config' there.
105 basedir = std::string(tmp) + "/.config";
106 } else {
107 //Last chance: Return current directory.
108 return ".";
110 //Try to create 'lsnes'. If it exists (or is created) and is directory, great. Otherwise error out.
111 std::string lsnes_path = basedir + "/lsnes";
112 if(!directory::ensure_exists(lsnes_path)) {
113 messages << "FATAL: Can't create configuration directory '" << lsnes_path << "'" << std::endl;
114 fatal_error();
116 //Yes, this is racy, but portability is more important than being absolutely correct...
117 std::string tfile = lsnes_path + "/test";
118 remove(tfile.c_str());
119 FILE* x;
120 if(!(x = fopen(tfile.c_str(), "w+"))) {
121 messages << "FATAL: Configuration directory '" << lsnes_path << "' is not writable" << std::endl;
122 fatal_error();
124 fclose(x);
125 remove(tfile.c_str());
126 return lsnes_path;
129 void OOM_panic()
131 if(lsnes_instance.mlogic) emerg_save_movie(lsnes_instance.mlogic->get_mfile(),
132 lsnes_instance.mlogic->get_rrdata());
133 messages << "FATAL: Out of memory!" << std::endl;
134 fatal_error();
137 uint32_t gcd(uint32_t a, uint32_t b) throw()
139 if(b == 0)
140 return a;
141 else
142 return gcd(b, a % b);
145 std::string format_address(void* addr)
147 return hex::to((uint64_t)addr);
150 bool in_global_ctors()
152 return !reached_main_flag;
155 void reached_main()
157 notify_new_core.errors_to(&messages.getstream());
158 crandom::init();
159 new_core_flag = false; //We'll process the static cores anyway.
160 reached_main_flag = true;
161 lsnes_instance.command->set_oom_panic(OOM_panic);
162 lsnes_instance.command->set_output(platform::out());
163 loadlib::module::run_initializers();
164 std::set_terminate(terminate_handler);
165 #ifdef SIGHUP
166 signal(SIGHUP, fatal_signal_handler);
167 #endif
168 #ifdef SIGINT
169 signal(SIGINT, fatal_signal_handler);
170 #endif
171 #ifdef SIGQUIT
172 signal(SIGQUIT, fatal_signal_handler);
173 #endif
174 #ifdef SIGILL
175 signal(SIGILL, fatal_signal_handler);
176 #endif
177 #ifdef SIGABRT
178 signal(SIGABRT, fatal_signal_handler);
179 #endif
180 #ifdef SIGSEGV
181 signal(SIGSEGV, fatal_signal_handler);
182 #endif
183 #ifdef SIGFPE
184 signal(SIGFPE, fatal_signal_handler);
185 #endif
186 #ifdef SIGPIPE
187 signal(SIGPIPE, fatal_signal_handler);
188 #endif
189 #ifdef SIGBUS
190 signal(SIGBUS, fatal_signal_handler);
191 #endif
192 #ifdef SIGTRAP
193 signal(SIGTRAP, fatal_signal_handler);
194 #endif
195 #ifdef SIGTERM
196 signal(SIGTERM, fatal_signal_handler);
197 #endif
200 std::string mangle_name(const std::string& orig)
202 std::ostringstream out;
203 for(auto i : orig) {
204 if(i == '(')
205 out << "[";
206 else if(i == ')')
207 out << "]";
208 else if(i == '|')
209 out << "\xE2\x8F\xBF";
210 else if(i == '/')
211 out << "\xE2\x8B\xBF";
212 else
213 out << i;
215 return out.str();
218 std::string get_temp_file()
220 #if !defined(_WIN32) && !defined(_WIN64)
221 char tname[512];
222 strcpy(tname, "/tmp/lsnestmp_XXXXXX");
223 int h = mkstemp(tname);
224 if(h < 0)
225 throw std::runtime_error("Failed to get new tempfile name");
226 close(h);
227 return tname;
228 #else
229 char tpath[512];
230 char tname[512];
231 if(!GetTempPathA(512, tpath))
232 throw std::runtime_error("Failed to get new tempfile name");
233 if(!GetTempFileNameA(tpath, "lsn", 0, tname))
234 throw std::runtime_error("Failed to get new tempfile name");
235 return tname;
236 #endif
239 command::fnptr<const std::string&> macro_test(lsnes_cmds, "test-macro", "", "",
240 [](const std::string& args) throw(std::bad_alloc, std::runtime_error) {
241 auto& core = CORE();
242 regex_results r = regex("([0-9]+)[ \t](.*)", args);
243 if(!r) {
244 messages << "Bad syntax" << std::endl;
245 return;
247 unsigned ctrl = parse_value<unsigned>(r[1]);
248 auto pcid = core.controls->lcid_to_pcid(ctrl);
249 if(pcid.first < 0) {
250 messages << "Bad controller" << std::endl;
251 return;
253 try {
254 const portctrl::controller* _ctrl =
255 core.controls->get_blank().porttypes().port_type(pcid.first).
256 controller_info->get(pcid.second);
257 if(!_ctrl) {
258 messages << "No controller data for controller" << std::endl;
259 return;
261 portctrl::macro_data mdata(r[2].c_str(), portctrl::macro_data::make_descriptor(*_ctrl), 0);
262 messages << "Macro: " << mdata.dump(*_ctrl) << std::endl;
263 } catch(std::exception& e) {
264 messages << "Exception: " << e.what() << std::endl;