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"
33 #include <boost/filesystem.hpp>
34 #if defined(_WIN32) || defined(_WIN64)
38 #ifdef USE_LIBGCRYPT_SHA256
45 bool reached_main_flag
;
46 threads::lock seed_mutex
;
48 uint64_t arch_get_tsc()
52 asm volatile("rdtsc" : "=a"(a
), "=d"(b
));
53 return ((uint64_t)b
<< 32) | a
;
59 uint64_t arch_get_random()
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");
72 void arch_random_256(uint8_t* buf
)
75 uint64_t tsc
= arch_get_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
));
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
));
100 std::string
get_random_hexstring_64(size_t index
)
102 threads::alock
h(seed_mutex
);
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
);
130 str
<< (loops
- base
) << " ";
135 str
<< arch_get_tsc() << " ";
136 for(unsigned i
= 0; i
< 256; i
++)
137 str
<< arch_get_random() << " ";
141 char endian_char(int e
)
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
);
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
;
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
))
184 o
<< "%" << hex::to8(ch
);
189 std::string
get_random_hexstring(size_t length
) throw(std::bad_alloc
)
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
);
213 r
.read(buf
, sizeof(buf
));
214 std::string
s(buf
, sizeof(buf
));
219 //If libgcrypt is available, use that.
220 #ifdef USE_LIBGCRYPT_SHA256
223 gcry_randomize((unsigned char*)buf
, sizeof(buf
), GCRY_STRONG_RANDOM
);
224 std::string
s(buf
, sizeof(buf
));
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
,
241 for(auto i
: cmdline
) {
242 if(!(optp
= regex("--rom=(.+)", i
)))
250 } catch(std::bad_alloc
& e
) {
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
;
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
;
289 std::string
get_config_path() throw(std::bad_alloc
)
293 if((tmp
= getenv("APPDATA"))) {
294 //If $APPDATA exists, it is the base directory
296 } else if((tmp
= getenv("XDG_CONFIG_HOME"))) {
297 //If $XDG_CONFIG_HOME exists, it is the base directory
299 } else if((tmp
= getenv("HOME"))) {
300 //If $HOME exists, the base directory is '.config' there.
301 basedir
= std::string(tmp
) + "/.config";
303 //Last chance: Return current directory.
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
;
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());
316 if(!(x
= fopen(tfile
.c_str(), "w+"))) {
317 messages
<< "FATAL: Configuration directory '" << lsnes_path
<< "' is not writable" << std::endl
;
321 remove(tfile
.c_str());
327 if(movb
) emerg_save_movie(movb
.get_mfile(), movb
.get_rrdata());
328 messages
<< "FATAL: Out of memory!" << std::endl
;
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()
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
;
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
);
362 signal(SIGHUP
, fatal_signal_handler
);
365 signal(SIGINT
, fatal_signal_handler
);
368 signal(SIGQUIT
, fatal_signal_handler
);
371 signal(SIGILL
, fatal_signal_handler
);
374 signal(SIGABRT
, fatal_signal_handler
);
377 signal(SIGSEGV
, fatal_signal_handler
);
380 signal(SIGFPE
, fatal_signal_handler
);
383 signal(SIGPIPE
, fatal_signal_handler
);
386 signal(SIGBUS
, fatal_signal_handler
);
389 signal(SIGTRAP
, fatal_signal_handler
);
392 signal(SIGTERM
, fatal_signal_handler
);
396 std::string
mangle_name(const std::string
& orig
)
398 std::ostringstream out
;
405 out
<< "\xE2\x8F\xBF";
407 out
<< "\xE2\x8B\xBF";
414 void random_mix_timing_entropy()
419 //Generate highly random stuff.
420 void highrandom_256(uint8_t* buf
)
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
430 gcry_randomize((unsigned char*)buf
, 32, GCRY_STRONG_RANDOM
);
431 for(unsigned i
= 0; i
< 32; i
++)
436 std::string
get_temp_file()
438 #if !defined(_WIN32) && !defined(_WIN64)
440 strcpy(tname
, "/tmp/lsnestmp_XXXXXX");
441 int h
= mkstemp(tname
);
443 throw std::runtime_error("Failed to get new tempfile name");
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");
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
);
461 messages
<< "Bad syntax" << std::endl
;
464 unsigned ctrl
= parse_value
<unsigned>(r
[1]);
465 auto pcid
= controls
.lcid_to_pcid(ctrl
);
467 messages
<< "Bad controller" << std::endl
;
471 const port_controller
* _ctrl
= controls
.get_blank().porttypes().port_type(pcid
.first
).
472 controller_info
->get(pcid
.second
);
474 messages
<< "No controller data for controller" << std::endl
;
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
;