Back movie data off movie file structure
[lsnes.git] / src / core / misc.cpp
blobcb99bf95e82375c27ff57e38855147ae1fe44ace
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/rom.hpp"
8 #include "core/rrdata.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 uint64_t rcounter = 0;
46 bool reached_main_flag;
47 mutex_class seed_mutex;
49 uint64_t arch_get_tsc()
51 #ifdef ARCH_IS_I386
52 uint32_t a, b;
53 asm volatile("rdtsc" : "=a"(a), "=d"(b));
54 return ((uint64_t)b << 32) | a;
55 #else
56 return 0;
57 #endif
60 uint64_t arch_get_random()
62 #ifdef ARCH_IS_I386
63 uint32_t r;
64 asm volatile (".byte 0xb8, 0x01, 0x00, 0x00, 0x00, 0x0f, 0xa2, 0xf7, 0xc1, 0x00, 0x00, 0x00, 0x40, "
65 "0x74, 0x03, 0x0f, 0xc7, 0xf0" : "=a"(r) : : "ebx", "ecx", "edx");
66 return r;
67 #else
68 return 0;
69 #endif
72 //Returns 32 bytes.
73 void arch_random_256(uint8_t* buf)
75 uint32_t tmp[1026];
76 uint64_t tsc = arch_get_tsc();
77 tmp[1024] = tsc;
78 tmp[1025] = tsc >> 32;
79 for(unsigned i = 0; i < 1024; i++)
80 tmp[i] = arch_get_random();
81 sha256::hash(buf, reinterpret_cast<uint8_t*>(buf), sizeof(buf));
84 void do_mix_tsc()
86 const int slots = 32;
87 static unsigned count = 0;
88 static uint64_t last_reseed = 0;
89 static uint64_t buf[slots + 1];
90 buf[count++] = arch_get_tsc();
91 umutex_class h(seed_mutex);
92 if(count == 0) count = 1; //Shouldn't happen.
93 if(count == slots || buf[count - 1] - last_reseed > 300000000) {
94 last_reseed = buf[count - 1];
95 buf[slots] = arch_get_random();
96 prng.write(buf, sizeof(buf));
97 count = 0;
101 std::string get_random_hexstring_64(size_t index)
103 umutex_class h(seed_mutex);
104 uint64_t buf[6];
105 uint8_t out[32];
106 buf[0] = time(NULL);
107 buf[1] = arch_get_tsc();
108 buf[2] = arch_get_random();
109 buf[3] = arch_get_random();
110 buf[4] = arch_get_random();
111 buf[5] = arch_get_random();
112 prng.write(buf, sizeof(buf));
113 prng.read(out, sizeof(out));
114 return hex::b_to(out, sizeof(out));
117 std::string collect_identifying_information()
119 //TODO: Collect as much identifying information as possible.
120 std::ostringstream str;
121 time_t told = time(NULL);
122 time_t tnew;
123 uint64_t loops = 0;
124 uint64_t base = 0;
125 int cnt = 0;
126 while(cnt < 3) {
127 tnew = time(NULL);
128 if(tnew > told) {
129 told = tnew;
130 cnt++;
131 str << (loops - base) << " ";
132 base = loops;
134 loops++;
136 str << arch_get_tsc() << " ";
137 for(unsigned i = 0; i < 256; i++)
138 str << arch_get_random() << " ";
139 return str.str();
142 char endian_char(int e)
144 if(e < 0)
145 return 'L';
146 if(e > 0)
147 return 'B';
148 return 'N';
151 void fatal_signal_handler(int sig)
153 write(2, "Caught fatal signal!\n", 21);
154 emerg_save_movie(our_movie);
155 signal(sig, SIG_DFL);
156 raise(sig);
159 void terminate_handler()
161 write(2, "Terminating abnormally!\n", 24);
162 emerg_save_movie(our_movie);
163 std::cerr << "Exiting on fatal error" << std::endl;
164 exit(1);
167 command::fnptr<const std::string&> test4(lsnes_cmd, "panicsave-movie", "", "",
168 [](const std::string& args) throw(std::bad_alloc, std::runtime_error) {
169 emerg_save_movie(our_movie);
172 //% is intentionally missing.
173 const char* allowed_filename_chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
174 "^&'@{}[],$?!-#().+~_";
177 std::string safe_filename(const std::string& str)
179 std::ostringstream o;
180 for(size_t i = 0; i < str.length(); i++) {
181 unsigned char ch = static_cast<unsigned char>(str[i]);
182 if(strchr(allowed_filename_chars, ch))
183 o << str[i];
184 else
185 o << "%" << hex::to8(ch);
187 return o.str();
190 std::string get_random_hexstring(size_t length) throw(std::bad_alloc)
192 std::string out;
193 for(size_t i = 0; i < length; i += 64)
194 out = out + get_random_hexstring_64(i);
195 return out.substr(0, length);
198 void set_random_seed(const std::string& seed) throw(std::bad_alloc)
200 std::vector<char> x(seed.begin(), seed.end());
202 umutex_class h(seed_mutex);
203 prng.write(&x[0], x.size());
205 rrdata.set_internal(random_rrdata());
208 void set_random_seed() throw(std::bad_alloc)
210 //Try /dev/urandom first.
212 std::ifstream r("/dev/urandom", std::ios::binary);
213 if(r.is_open()) {
214 char buf[128];
215 r.read(buf, sizeof(buf));
216 std::string s(buf, sizeof(buf));
217 set_random_seed(s);
218 return;
221 //If libgcrypt is available, use that.
222 #ifdef USE_LIBGCRYPT_SHA256
224 char buf[128];
225 gcry_randomize((unsigned char*)buf, sizeof(buf), GCRY_STRONG_RANDOM);
226 std::string s(buf, sizeof(buf));
227 set_random_seed(s);
228 return;
230 #endif
231 //Fall back to time.
232 std::ostringstream str;
233 str << collect_identifying_information() << " " << time(NULL);
234 set_random_seed(str.str());
238 struct loaded_rom load_rom_from_commandline(std::vector<std::string> cmdline) throw(std::bad_alloc,
239 std::runtime_error)
241 std::string f;
242 regex_results optp;
243 for(auto i : cmdline) {
244 if(!(optp = regex("--rom=(.+)", i)))
245 continue;
246 f = optp[1];
249 struct loaded_rom r;
250 try {
251 r = loaded_rom(f);
252 } catch(std::bad_alloc& e) {
253 OOM_panic();
254 } catch(std::exception& e) {
255 throw std::runtime_error(std::string("Can't load ROM: ") + e.what());
258 std::string not_present = "N/A";
259 for(size_t i = 0; i < ROM_SLOT_COUNT; i++) {
260 std::string romname = "UNKNOWN ROM";
261 std::string xmlname = "UNKNOWN XML";
262 if(i < r.rtype->get_image_count()) {
263 romname = (r.rtype->get_image_info(i).hname == "ROM") ? std::string("ROM") :
264 (r.rtype->get_image_info(i).hname + " ROM");
265 xmlname = r.rtype->get_image_info(i).hname + " XML";
267 if(r.romimg[i].data) messages << romname << " hash: " << r.romimg[i].sha_256.read() << std::endl;
268 if(r.romxml[i].data) messages << xmlname << " hash: " << r.romxml[i].sha_256.read() << std::endl;
270 return r;
273 void dump_region_map() throw(std::bad_alloc)
275 std::list<struct memory_region*> regions = lsnes_memory.get_regions();
276 for(auto i : regions) {
277 std::ostringstream x;
278 x << hex::to(i->base) << "-" << hex::to(i->last_address()) << " " << hex::to(i->size) << " ";
279 messages << x.str() << (i->readonly ? "R-" : "RW") << endian_char(i->endian)
280 << (i->special ? 'I' : 'M') << " " << i->name << std::endl;
284 void fatal_error() throw()
286 platform::fatal_error();
287 std::cout << "PANIC: Fatal error, can't continue." << std::endl;
288 exit(1);
291 std::string get_config_path() throw(std::bad_alloc)
293 const char* tmp;
294 std::string basedir;
295 if((tmp = getenv("APPDATA"))) {
296 //If $APPDATA exists, it is the base directory
297 basedir = tmp;
298 } else if((tmp = getenv("XDG_CONFIG_HOME"))) {
299 //If $XDG_CONFIG_HOME exists, it is the base directory
300 basedir = tmp;
301 } else if((tmp = getenv("HOME"))) {
302 //If $HOME exists, the base directory is '.config' there.
303 basedir = std::string(tmp) + "/.config";
304 } else {
305 //Last chance: Return current directory.
306 return ".";
308 //Try to create 'lsnes'. If it exists (or is created) and is directory, great. Otherwise error out.
309 std::string lsnes_path = basedir + "/lsnes";
310 if(!ensure_directory_exists(lsnes_path)) {
311 messages << "FATAL: Can't create configuration directory '" << lsnes_path << "'" << std::endl;
312 fatal_error();
314 //Yes, this is racy, but portability is more important than being absolutely correct...
315 std::string tfile = lsnes_path + "/test";
316 remove(tfile.c_str());
317 FILE* x;
318 if(!(x = fopen(tfile.c_str(), "w+"))) {
319 messages << "FATAL: Configuration directory '" << lsnes_path << "' is not writable" << std::endl;
320 fatal_error();
322 fclose(x);
323 remove(tfile.c_str());
324 return lsnes_path;
327 void OOM_panic()
329 emerg_save_movie(our_movie);
330 messages << "FATAL: Out of memory!" << std::endl;
331 fatal_error();
334 std::ostream& messages_relay_class::getstream() { return platform::out(); }
335 messages_relay_class messages;
337 uint32_t gcd(uint32_t a, uint32_t b) throw()
339 if(b == 0)
340 return a;
341 else
342 return gcd(b, a % b);
345 std::string format_address(void* addr)
347 return hex::to((uint64_t)addr);
350 bool in_global_ctors()
352 return !reached_main_flag;
355 void reached_main()
357 new_core_flag = false; //We'll process the static cores anyway.
358 reached_main_flag = true;
359 lsnes_cmd.set_oom_panic(OOM_panic);
360 lsnes_cmd.set_output(platform::out());
361 loadlib::module::run_initializers();
362 std::set_terminate(terminate_handler);
363 #ifdef SIGHUP
364 signal(SIGHUP, fatal_signal_handler);
365 #endif
366 #ifdef SIGINT
367 signal(SIGINT, fatal_signal_handler);
368 #endif
369 #ifdef SIGQUIT
370 signal(SIGQUIT, fatal_signal_handler);
371 #endif
372 #ifdef SIGILL
373 signal(SIGILL, fatal_signal_handler);
374 #endif
375 #ifdef SIGABRT
376 signal(SIGABRT, fatal_signal_handler);
377 #endif
378 #ifdef SIGSEGV
379 signal(SIGSEGV, fatal_signal_handler);
380 #endif
381 #ifdef SIGFPE
382 signal(SIGFPE, fatal_signal_handler);
383 #endif
384 #ifdef SIGPIPE
385 signal(SIGPIPE, fatal_signal_handler);
386 #endif
387 #ifdef SIGBUS
388 signal(SIGBUS, fatal_signal_handler);
389 #endif
390 #ifdef SIGTRAP
391 signal(SIGTRAP, fatal_signal_handler);
392 #endif
393 #ifdef SIGTERM
394 signal(SIGTERM, fatal_signal_handler);
395 #endif
398 std::string mangle_name(const std::string& orig)
400 std::ostringstream out;
401 for(auto i : orig) {
402 if(i == '(')
403 out << "[";
404 else if(i == ')')
405 out << "]";
406 else if(i == '|')
407 out << "\xE2\x8F\xBF";
408 else if(i == '/')
409 out << "\xE2\x8B\xBF";
410 else
411 out << i;
413 return out.str();
416 void random_mix_timing_entropy()
418 do_mix_tsc();
421 //Generate highly random stuff.
422 void highrandom_256(uint8_t* buf)
424 uint8_t tmp[104];
425 std::string s = get_random_hexstring(64);
426 std::copy(s.begin(), s.end(), reinterpret_cast<char*>(tmp));
427 arch_random_256(tmp + 64);
428 serialization::u64b(tmp + 96, arch_get_tsc());
429 sha256::hash(buf, tmp, 104);
430 #ifdef USE_LIBGCRYPT_SHA256
431 memset(tmp, 0, 32);
432 gcry_randomize((unsigned char*)buf, 32, GCRY_STRONG_RANDOM);
433 for(unsigned i = 0; i < 32; i++)
434 buf[i] ^= tmp[i];
435 #endif
438 std::string get_temp_file()
440 #if !defined(_WIN32) && !defined(_WIN64)
441 char tname[512];
442 strcpy(tname, "/tmp/lsnestmp_XXXXXX");
443 int h = mkstemp(tname);
444 if(h < 0)
445 throw std::runtime_error("Failed to get new tempfile name");
446 close(h);
447 return tname;
448 #else
449 char tpath[512];
450 char tname[512];
451 if(!GetTempPathA(512, tpath))
452 throw std::runtime_error("Failed to get new tempfile name");
453 if(!GetTempFileNameA(tpath, "lsn", 0, tname))
454 throw std::runtime_error("Failed to get new tempfile name");
455 return tname;
456 #endif
459 command::fnptr<const std::string&> macro_test(lsnes_cmd, "test-macro", "", "",
460 [](const std::string& args) throw(std::bad_alloc, std::runtime_error) {
461 regex_results r = regex("([0-9]+)[ \t](.*)", args);
462 if(!r) {
463 messages << "Bad syntax" << std::endl;
464 return;
466 unsigned ctrl = parse_value<unsigned>(r[1]);
467 auto pcid = controls.lcid_to_pcid(ctrl);
468 if(pcid.first < 0) {
469 messages << "Bad controller" << std::endl;
470 return;
472 try {
473 const port_controller* _ctrl = controls.get_blank().porttypes().port_type(pcid.first).
474 controller_info->get(pcid.second);
475 if(!_ctrl) {
476 messages << "No controller data for controller" << std::endl;
477 return;
479 controller_macro_data mdata(r[2].c_str(), controller_macro_data::make_descriptor(*_ctrl), 0);
480 messages << "Macro: " << mdata.dump(*_ctrl) << std::endl;
481 } catch(std::exception& e) {
482 messages << "Exception: " << e.what() << std::endl;