Fix SA1 open bus
[lsnes.git] / src / core / misc.cpp
blobd5a74d58a24de7d944898b83443addb6c4ee5c8d
1 #include "lsnes.hpp"
3 #include "core/command.hpp"
4 #include "core/controller.hpp"
5 #include "core/memorymanip.hpp"
6 #include "core/misc.hpp"
7 #include "core/movie.hpp"
8 #include "core/rom.hpp"
9 #include "core/moviedata.hpp"
10 #include "core/settings.hpp"
11 #include "core/window.hpp"
12 #include "library/directory.hpp"
13 #include "library/hex.hpp"
14 #include "library/loadlib.hpp"
15 #include "library/sha256.hpp"
16 #include "library/string.hpp"
17 #include "library/skein.hpp"
18 #include "library/serialization.hpp"
19 #include "library/arch-detect.hpp"
21 #include <cstdlib>
22 #include <csignal>
23 #include <sstream>
24 #include <iostream>
25 #include <iomanip>
26 #include <fstream>
27 #include <string>
28 #include <vector>
29 #include <algorithm>
30 #include <ctime>
31 #include <cstdlib>
32 #include <cstring>
33 #include <boost/filesystem.hpp>
34 #if defined(_WIN32) || defined(_WIN64)
35 #include <windows.h>
36 #endif
38 #ifdef USE_LIBGCRYPT_SHA256
39 #include <gcrypt.h>
40 #endif
42 namespace
44 skein::prng prng;
45 bool reached_main_flag;
46 threads::lock seed_mutex;
48 uint64_t arch_get_tsc()
50 #ifdef ARCH_IS_I386
51 uint32_t a, b;
52 asm volatile("rdtsc" : "=a"(a), "=d"(b));
53 return ((uint64_t)b << 32) | a;
54 #else
55 return 0;
56 #endif
59 uint64_t arch_get_random()
61 #ifdef ARCH_IS_I386
62 uint32_t r;
63 asm volatile (".byte 0xb8, 0x01, 0x00, 0x00, 0x00, 0x0f, 0xa2, 0xf7, 0xc1, 0x00, 0x00, 0x00, 0x40, "
64 "0x74, 0x03, 0x0f, 0xc7, 0xf0" : "=a"(r) : : "ebx", "ecx", "edx");
65 return r;
66 #else
67 return 0;
68 #endif
71 //Returns 32 bytes.
72 void arch_random_256(uint8_t* buf)
74 uint32_t tmp[1026];
75 uint64_t tsc = arch_get_tsc();
76 tmp[1024] = tsc;
77 tmp[1025] = tsc >> 32;
78 for(unsigned i = 0; i < 1024; i++)
79 tmp[i] = arch_get_random();
80 sha256::hash(buf, reinterpret_cast<uint8_t*>(tmp), sizeof(tmp));
83 void do_mix_tsc()
85 const unsigned slots = 32;
86 static unsigned count = 0;
87 static uint64_t last_reseed = 0;
88 static uint64_t buf[slots + 1];
89 buf[count++] = arch_get_tsc();
90 threads::alock h(seed_mutex);
91 if(count == 0) count = 1; //Shouldn't happen.
92 if(count == slots || buf[count - 1] - last_reseed > 300000000) {
93 last_reseed = buf[count - 1];
94 buf[slots] = arch_get_random();
95 prng.write(buf, sizeof(buf));
96 count = 0;
100 std::string get_random_hexstring_64(size_t index)
102 threads::alock h(seed_mutex);
103 uint64_t buf[6];
104 uint8_t out[32];
105 buf[0] = time(NULL);
106 buf[1] = arch_get_tsc();
107 buf[2] = arch_get_random();
108 buf[3] = arch_get_random();
109 buf[4] = arch_get_random();
110 buf[5] = arch_get_random();
111 prng.write(buf, sizeof(buf));
112 prng.read(out, sizeof(out));
113 return hex::b_to(out, sizeof(out));
116 std::string collect_identifying_information()
118 //TODO: Collect as much identifying information as possible.
119 std::ostringstream str;
120 time_t told = time(NULL);
121 time_t tnew;
122 uint64_t loops = 0;
123 uint64_t base = 0;
124 int cnt = 0;
125 while(cnt < 3) {
126 tnew = time(NULL);
127 if(tnew > told) {
128 told = tnew;
129 cnt++;
130 str << (loops - base) << " ";
131 base = loops;
133 loops++;
135 str << arch_get_tsc() << " ";
136 for(unsigned i = 0; i < 256; i++)
137 str << arch_get_random() << " ";
138 return str.str();
141 char endian_char(int e)
143 if(e < 0)
144 return 'L';
145 if(e > 0)
146 return 'B';
147 return 'N';
150 void fatal_signal_handler(int sig)
152 write(2, "Caught fatal signal!\n", 21);
153 if(movb) emerg_save_movie(movb.get_mfile(), movb.get_rrdata());
154 signal(sig, SIG_DFL);
155 raise(sig);
158 void terminate_handler()
160 write(2, "Terminating abnormally!\n", 24);
161 if(movb) emerg_save_movie(movb.get_mfile(), movb.get_rrdata());
162 std::cerr << "Exiting on fatal error" << std::endl;
163 exit(1);
166 command::fnptr<const std::string&> test4(lsnes_cmd, "panicsave-movie", "", "",
167 [](const std::string& args) throw(std::bad_alloc, std::runtime_error) {
168 if(movb) emerg_save_movie(movb.get_mfile(), movb.get_rrdata());
171 //% is intentionally missing.
172 const char* allowed_filename_chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
173 "^&'@{}[],$?!-#().+~_";
176 std::string safe_filename(const std::string& str)
178 std::ostringstream o;
179 for(size_t i = 0; i < str.length(); i++) {
180 unsigned char ch = static_cast<unsigned char>(str[i]);
181 if(strchr(allowed_filename_chars, ch))
182 o << str[i];
183 else
184 o << "%" << hex::to8(ch);
186 return o.str();
189 std::string get_random_hexstring(size_t length) throw(std::bad_alloc)
191 std::string out;
192 for(size_t i = 0; i < length; i += 64)
193 out = out + get_random_hexstring_64(i);
194 return out.substr(0, length);
197 void set_random_seed(const std::string& seed) throw(std::bad_alloc)
199 std::vector<char> x(seed.begin(), seed.end());
201 threads::alock h(seed_mutex);
202 prng.write(&x[0], x.size());
206 void set_random_seed() throw(std::bad_alloc)
208 //Try /dev/urandom first.
210 std::ifstream r("/dev/urandom", std::ios::binary);
211 if(r.is_open()) {
212 char buf[128];
213 r.read(buf, sizeof(buf));
214 std::string s(buf, sizeof(buf));
215 set_random_seed(s);
216 return;
219 //If libgcrypt is available, use that.
220 #ifdef USE_LIBGCRYPT_SHA256
222 char buf[128];
223 gcry_randomize((unsigned char*)buf, sizeof(buf), GCRY_STRONG_RANDOM);
224 std::string s(buf, sizeof(buf));
225 set_random_seed(s);
226 return;
228 #endif
229 //Fall back to time.
230 std::ostringstream str;
231 str << collect_identifying_information() << " " << time(NULL);
232 set_random_seed(str.str());
236 struct loaded_rom load_rom_from_commandline(std::vector<std::string> cmdline) throw(std::bad_alloc,
237 std::runtime_error)
239 std::string f;
240 regex_results optp;
241 for(auto i : cmdline) {
242 if(!(optp = regex("--rom=(.+)", i)))
243 continue;
244 f = optp[1];
247 struct loaded_rom r;
248 try {
249 r = loaded_rom(f);
250 } catch(std::bad_alloc& e) {
251 OOM_panic();
252 } catch(std::exception& e) {
253 throw std::runtime_error(std::string("Can't load ROM: ") + e.what());
256 std::string not_present = "N/A";
257 for(size_t i = 0; i < ROM_SLOT_COUNT; i++) {
258 std::string romname = "UNKNOWN ROM";
259 std::string xmlname = "UNKNOWN XML";
260 if(i < r.rtype->get_image_count()) {
261 romname = (r.rtype->get_image_info(i).hname == "ROM") ? std::string("ROM") :
262 (r.rtype->get_image_info(i).hname + " ROM");
263 xmlname = r.rtype->get_image_info(i).hname + " XML";
265 if(r.romimg[i].data) messages << romname << " hash: " << r.romimg[i].sha_256.read() << std::endl;
266 if(r.romxml[i].data) messages << xmlname << " hash: " << r.romxml[i].sha_256.read() << std::endl;
268 return r;
271 void dump_region_map() throw(std::bad_alloc)
273 std::list<struct memory_region*> regions = lsnes_memory.get_regions();
274 for(auto i : regions) {
275 std::ostringstream x;
276 x << hex::to(i->base) << "-" << hex::to(i->last_address()) << " " << hex::to(i->size) << " ";
277 messages << x.str() << (i->readonly ? "R-" : "RW") << endian_char(i->endian)
278 << (i->special ? 'I' : 'M') << " " << i->name << std::endl;
282 void fatal_error() throw()
284 platform::fatal_error();
285 std::cout << "PANIC: Fatal error, can't continue." << std::endl;
286 exit(1);
289 std::string get_config_path() throw(std::bad_alloc)
291 const char* tmp;
292 std::string basedir;
293 if((tmp = getenv("APPDATA"))) {
294 //If $APPDATA exists, it is the base directory
295 basedir = tmp;
296 } else if((tmp = getenv("XDG_CONFIG_HOME"))) {
297 //If $XDG_CONFIG_HOME exists, it is the base directory
298 basedir = tmp;
299 } else if((tmp = getenv("HOME"))) {
300 //If $HOME exists, the base directory is '.config' there.
301 basedir = std::string(tmp) + "/.config";
302 } else {
303 //Last chance: Return current directory.
304 return ".";
306 //Try to create 'lsnes'. If it exists (or is created) and is directory, great. Otherwise error out.
307 std::string lsnes_path = basedir + "/lsnes";
308 if(!ensure_directory_exists(lsnes_path)) {
309 messages << "FATAL: Can't create configuration directory '" << lsnes_path << "'" << std::endl;
310 fatal_error();
312 //Yes, this is racy, but portability is more important than being absolutely correct...
313 std::string tfile = lsnes_path + "/test";
314 remove(tfile.c_str());
315 FILE* x;
316 if(!(x = fopen(tfile.c_str(), "w+"))) {
317 messages << "FATAL: Configuration directory '" << lsnes_path << "' is not writable" << std::endl;
318 fatal_error();
320 fclose(x);
321 remove(tfile.c_str());
322 return lsnes_path;
325 void OOM_panic()
327 if(movb) emerg_save_movie(movb.get_mfile(), movb.get_rrdata());
328 messages << "FATAL: Out of memory!" << std::endl;
329 fatal_error();
332 std::ostream& messages_relay_class::getstream() { return platform::out(); }
333 messages_relay_class messages;
335 uint32_t gcd(uint32_t a, uint32_t b) throw()
337 if(b == 0)
338 return a;
339 else
340 return gcd(b, a % b);
343 std::string format_address(void* addr)
345 return hex::to((uint64_t)addr);
348 bool in_global_ctors()
350 return !reached_main_flag;
353 void reached_main()
355 new_core_flag = false; //We'll process the static cores anyway.
356 reached_main_flag = true;
357 lsnes_cmd.set_oom_panic(OOM_panic);
358 lsnes_cmd.set_output(platform::out());
359 loadlib::module::run_initializers();
360 std::set_terminate(terminate_handler);
361 #ifdef SIGHUP
362 signal(SIGHUP, fatal_signal_handler);
363 #endif
364 #ifdef SIGINT
365 signal(SIGINT, fatal_signal_handler);
366 #endif
367 #ifdef SIGQUIT
368 signal(SIGQUIT, fatal_signal_handler);
369 #endif
370 #ifdef SIGILL
371 signal(SIGILL, fatal_signal_handler);
372 #endif
373 #ifdef SIGABRT
374 signal(SIGABRT, fatal_signal_handler);
375 #endif
376 #ifdef SIGSEGV
377 signal(SIGSEGV, fatal_signal_handler);
378 #endif
379 #ifdef SIGFPE
380 signal(SIGFPE, fatal_signal_handler);
381 #endif
382 #ifdef SIGPIPE
383 signal(SIGPIPE, fatal_signal_handler);
384 #endif
385 #ifdef SIGBUS
386 signal(SIGBUS, fatal_signal_handler);
387 #endif
388 #ifdef SIGTRAP
389 signal(SIGTRAP, fatal_signal_handler);
390 #endif
391 #ifdef SIGTERM
392 signal(SIGTERM, fatal_signal_handler);
393 #endif
396 std::string mangle_name(const std::string& orig)
398 std::ostringstream out;
399 for(auto i : orig) {
400 if(i == '(')
401 out << "[";
402 else if(i == ')')
403 out << "]";
404 else if(i == '|')
405 out << "\xE2\x8F\xBF";
406 else if(i == '/')
407 out << "\xE2\x8B\xBF";
408 else
409 out << i;
411 return out.str();
414 void random_mix_timing_entropy()
416 do_mix_tsc();
419 //Generate highly random stuff.
420 void highrandom_256(uint8_t* buf)
422 uint8_t tmp[104];
423 std::string s = get_random_hexstring(64);
424 std::copy(s.begin(), s.end(), reinterpret_cast<char*>(tmp));
425 arch_random_256(tmp + 64);
426 serialization::u64b(tmp + 96, arch_get_tsc());
427 sha256::hash(buf, tmp, 104);
428 #ifdef USE_LIBGCRYPT_SHA256
429 memset(tmp, 0, 32);
430 gcry_randomize((unsigned char*)buf, 32, GCRY_STRONG_RANDOM);
431 for(unsigned i = 0; i < 32; i++)
432 buf[i] ^= tmp[i];
433 #endif
436 std::string get_temp_file()
438 #if !defined(_WIN32) && !defined(_WIN64)
439 char tname[512];
440 strcpy(tname, "/tmp/lsnestmp_XXXXXX");
441 int h = mkstemp(tname);
442 if(h < 0)
443 throw std::runtime_error("Failed to get new tempfile name");
444 close(h);
445 return tname;
446 #else
447 char tpath[512];
448 char tname[512];
449 if(!GetTempPathA(512, tpath))
450 throw std::runtime_error("Failed to get new tempfile name");
451 if(!GetTempFileNameA(tpath, "lsn", 0, tname))
452 throw std::runtime_error("Failed to get new tempfile name");
453 return tname;
454 #endif
457 command::fnptr<const std::string&> macro_test(lsnes_cmd, "test-macro", "", "",
458 [](const std::string& args) throw(std::bad_alloc, std::runtime_error) {
459 regex_results r = regex("([0-9]+)[ \t](.*)", args);
460 if(!r) {
461 messages << "Bad syntax" << std::endl;
462 return;
464 unsigned ctrl = parse_value<unsigned>(r[1]);
465 auto pcid = controls.lcid_to_pcid(ctrl);
466 if(pcid.first < 0) {
467 messages << "Bad controller" << std::endl;
468 return;
470 try {
471 const port_controller* _ctrl = controls.get_blank().porttypes().port_type(pcid.first).
472 controller_info->get(pcid.second);
473 if(!_ctrl) {
474 messages << "No controller data for controller" << std::endl;
475 return;
477 controller_macro_data mdata(r[2].c_str(), controller_macro_data::make_descriptor(*_ctrl), 0);
478 messages << "Macro: " << mdata.dump(*_ctrl) << std::endl;
479 } catch(std::exception& e) {
480 messages << "Exception: " << e.what() << std::endl;