Use urandom / rtlgenrandom
[lsnes.git] / src / util / lsnes-dumpavi.cpp
blobbb9635ed4e6756f9cce9a4e4133545bc305dcbba
1 #include "lsnes.hpp"
3 #include "core/advdumper.hpp"
4 #include "core/controller.hpp"
5 #include "core/command.hpp"
6 #include "core/dispatch.hpp"
7 #include "core/mainloop.hpp"
8 #include "core/framerate.hpp"
9 #include "core/keymapper.hpp"
10 #include "interface/romtype.hpp"
11 #include "core/loadlib.hpp"
12 #include "lua/lua.hpp"
13 #include "core/filedownload.hpp"
14 #include "core/mainloop.hpp"
15 #include "core/misc.hpp"
16 #include "core/movie.hpp"
17 #include "core/moviedata.hpp"
18 #include "core/rom.hpp"
19 #include "core/romloader.hpp"
20 #include "core/settings.hpp"
21 #include "core/window.hpp"
22 #include "library/directory.hpp"
23 #include "library/crandom.hpp"
24 #include "library/string.hpp"
26 #include <sys/time.h>
27 #include <sstream>
29 namespace
31 bool hashing_in_progress = false;
32 uint64_t hashing_left = 0;
33 int64_t last_update = 0;
35 std::string do_download_movie(const std::string& origname)
37 if(!regex_match("[A-Za-z][A-Za-z0-9+.-]*:.+", origname))
38 return origname; //File.
39 if(file_is_regular(origname))
40 return origname; //Even exists.
41 //Okay, we need to download this.
42 auto download_in_progress = new file_download();
43 download_in_progress->url = lsnes_uri_rewrite(origname);
44 if(download_in_progress->url != origname)
45 messages << "Internally redirecting to " << download_in_progress->url << std::endl;
46 download_in_progress->target_slot = "dumpavi_download_tmp";
47 download_in_progress->do_async();
48 messages << "Downloading " << download_in_progress->url << ":" << std::endl;
49 while(!download_in_progress->finished) {
50 messages << download_in_progress->statusmsg() << std::endl;
51 usleep(1000000);
53 if(download_in_progress->errormsg != "") {
54 std::string err = download_in_progress->errormsg;
55 delete download_in_progress;
56 throw std::runtime_error(err);
58 delete download_in_progress;
59 messages << "Download finished." << std::endl;
60 return "$MEMORY:dumpavi_download_tmp";
63 void hash_callback(uint64_t left, uint64_t total)
65 if(left == 0xFFFFFFFFFFFFFFFFULL) {
66 hashing_in_progress = false;
67 std::cout << "Done." << std::endl;
68 last_update = get_utime() - 2000000;
69 return;
71 if(!hashing_in_progress) {
72 std::cout << "Hashing disc images..." << std::flush;
74 hashing_in_progress = true;
75 hashing_left = left;
76 int64_t this_update = get_utime();
77 if(this_update < last_update - 1000000 || this_update > last_update + 1000000) {
78 std::cout << ((hashing_left + 524288) >> 20) << "..." << std::flush;
79 last_update = this_update;
83 class myavsnoop : public information_dispatch
85 public:
86 myavsnoop(adv_dumper& _dumper, uint64_t frames_to_dump)
87 : information_dispatch("myavsnoop-monitor"), dumper(_dumper)
89 frames_dumped = 0;
90 total = frames_to_dump;
93 ~myavsnoop() throw()
97 void on_frame(struct framebuffer::raw& _frame, uint32_t fps_n, uint32_t fps_d)
99 frames_dumped++;
100 if(frames_dumped % 100 == 0) {
101 std::cout << "Dumping frame " << frames_dumped << "/" << total << " ("
102 << (100 * frames_dumped / total) << "%)" << std::endl;
104 if(frames_dumped == total) {
105 //Rough way to end it.
106 dumper.end();
107 information_dispatch::do_dump_end();
108 exit(0);
112 void on_dump_end()
114 std::cout << "Finished!" << std::endl;
116 private:
117 uint64_t frames_dumped;
118 uint64_t total;
119 adv_dumper& dumper;
122 void dumper_startup(adv_dumper& dumper, const std::string& mode, const std::string& prefix, uint64_t length)
124 std::cout << "Invoking dumper" << std::endl;
125 try {
126 dumper.start(mode, prefix);
127 } catch(std::exception& e) {
128 std::cerr << "Can't start dumper: " << e.what() << std::endl;
129 exit(1);
131 if(information_dispatch::get_dumper_count()) {
132 std::cout << "Dumper attach confirmed" << std::endl;
133 } else {
134 std::cout << "Can't start dumper!" << std::endl;
135 exit(1);
137 new myavsnoop(dumper, length);
140 void startup_lua_scripts(const std::vector<std::string>& cmdline)
142 for(auto i = cmdline.begin(); i != cmdline.end(); i++) {
143 std::string a = *i;
144 if(a.length() > 6 && a.substr(0, 6) == "--lua=") {
145 lua_add_startup_script(a.substr(6));
150 struct adv_dumper& locate_dumper(const std::string& name)
152 adv_dumper* _dumper = NULL;
153 std::set<adv_dumper*> dumpers = adv_dumper::get_dumper_set();
154 for(auto i : dumpers)
155 if(i->id() == name)
156 _dumper = i;
157 if(!_dumper) {
158 std::cerr << "No such dumper '" << name << "' found (try --dumper=list)" << std::endl;
159 exit(1);
161 return *_dumper;
164 std::string format_details(unsigned detail)
166 std::string r;
167 if((detail & adv_dumper::target_type_mask) == adv_dumper::target_type_file)
168 r = r + "TARGET_FILE";
169 else if((detail & adv_dumper::target_type_mask) == adv_dumper::target_type_prefix)
170 r = r + "TARGET_PREFIX";
171 else if((detail & adv_dumper::target_type_mask) == adv_dumper::target_type_special)
172 r = r + "TARGET_SPECIAL";
173 else
174 r = r + "TARGET_UNKNOWN";
175 return r;
178 adv_dumper& get_dumper(const std::vector<std::string>& cmdline, std::string& mode, std::string& prefix,
179 uint64_t& length, bool& overdump_mode, uint64_t& overdump_length)
181 bool dumper_given = false;
182 std::string dumper;
183 bool mode_given = false;
184 prefix = "avidump";
185 length = 0;
186 overdump_mode = false;
187 overdump_length = 0;
188 for(auto i = cmdline.begin(); i != cmdline.end(); i++) {
189 std::string a = *i;
190 if(a.length() >= 9 && a.substr(0, 9) == "--dumper=") {
191 dumper_given = true;
192 dumper = a.substr(9);
193 } else if(a.length() >= 7 && a.substr(0, 7) == "--mode=") {
194 mode_given = true;
195 mode = a.substr(7);
196 } else if(a.length() >= 9 && a.substr(0, 9) == "--prefix=")
197 prefix = a.substr(9);
198 else if(a.length() >= 9 && a.substr(0, 9) == "--length=")
199 try {
200 length = boost::lexical_cast<uint64_t>(a.substr(9));
201 if(!length)
202 throw std::runtime_error("Length out of range (1-)");
203 if(overdump_mode)
204 throw std::runtime_error("--length and --overdump-length are "
205 "mutually exclusive.");
206 } catch(std::exception& e) {
207 std::cerr << "Bad --length: " << e.what() << std::endl;
208 exit(1);
210 else if(a.length() >= 18 && a.substr(0, 18) == "--overdump-length=")
211 try {
212 overdump_length = boost::lexical_cast<uint64_t>(a.substr(18));
213 overdump_mode = true;
214 if(length)
215 throw std::runtime_error("--length and --overdump-length are "
216 "mutually exclusive.");
217 } catch(std::exception& e) {
218 std::cerr << "Bad --overdump-length: " << e.what() << std::endl;
219 exit(1);
221 else if(a.length() >= 9 && a.substr(0, 9) == "--option=") {
222 std::string nameval = a.substr(9);
223 size_t s = nameval.find_first_of("=");
224 if(s >= nameval.length()) {
225 std::cerr << "Invalid option syntax (expected --option=foo=bar)" << std::endl;
226 exit(1);
228 std::string name = nameval.substr(0, s);
229 std::string val = nameval.substr(s + 1);
230 try {
231 lsnes_vset[name].str(val);
232 } catch(std::exception& e) {
233 std::cerr << "Can't set '" << name << "' to '" << val << "': " << e.what()
234 << std::endl;
235 exit(1);
237 } else if(a.length() >= 12 && a.substr(0, 15) == "--load-library=")
238 try {
239 with_loaded_library(*new loadlib::module(loadlib::library(a.substr(15))));
240 handle_post_loadlibrary();
241 } catch(std::runtime_error& e) {
242 std::cerr << "Can't load '" << a.substr(15) << "': " << e.what() << std::endl;
243 exit(1);
246 if(dumper == "list") {
247 //Help on dumpers.
248 std::set<adv_dumper*> dumpers = adv_dumper::get_dumper_set();
249 std::cout << "Dumpers available:" << std::endl;
250 for(auto i : dumpers)
251 std::cout << i->id() << "\t" << i->name() << std::endl;
252 exit(0);
254 if(!dumper_given) {
255 std::cerr << "Dumper required (--dumper=foo)" << std::endl;
256 exit(1);
258 if(mode == "list") {
259 //Help on modes.
260 adv_dumper& _dumper = locate_dumper(dumper);
261 std::set<std::string> modes = _dumper.list_submodes();
262 if(modes.empty()) {
263 unsigned d = _dumper.mode_details("");
264 std::cout << "No modes available for " << dumper << " (" << format_details(d) << ")"
265 << std::endl;
266 exit(0);
268 std::cout << "Modes available for " << dumper << ":" << std::endl;
269 for(auto i : modes) {
270 unsigned d = _dumper.mode_details(i);
271 std::cout << i << "\t" << _dumper.modename(i) << "\t(" << format_details(d) << ")"
272 << std::endl;
274 exit(0);
276 adv_dumper& _dumper = locate_dumper(dumper);
277 if(!mode_given && !_dumper.list_submodes().empty()) {
278 std::cerr << "Mode required for this dumper" << std::endl;
279 exit(1);
281 if(mode_given && _dumper.list_submodes().empty()) {
282 std::cerr << "This dumper does not have mode select" << std::endl;
283 exit(1);
285 if(mode_given && !_dumper.list_submodes().count(mode)) {
286 std::cerr << "'" << mode << "' is not a valid mode for '" << dumper << "'" << std::endl;
287 exit(1);
289 if(!length && !overdump_mode) {
290 std::cerr << "--length=<frames> or --overdump-length=<frames> has to be specified"
291 << std::endl;
292 exit(1);
294 return locate_dumper(dumper);
298 int main(int argc, char** argv)
300 try {
301 crandom::init();
302 } catch(std::exception& e) {
303 std::cerr << "Error initializing system RNG" << std::endl;
304 return 1;
307 reached_main();
308 std::vector<std::string> cmdline;
309 for(int i = 1; i < argc; i++)
310 cmdline.push_back(argv[i]);
311 uint64_t length, overdump_length;
312 bool overdump_mode;
313 std::string mode, prefix;
315 adv_dumper& dumper = get_dumper(cmdline, mode, prefix, length, overdump_mode, overdump_length);
317 set_random_seed();
318 platform::init();
319 init_lua();
321 messages << "lsnes version: lsnes rr" << lsnes_version << std::endl;
322 messages << "Command line is: ";
323 for(auto k = cmdline.begin(); k != cmdline.end(); k++)
324 messages << "\"" << *k << "\" ";
325 messages << std::endl;
327 std::string cfgpath = get_config_path();
328 autoload_libraries();
330 lsnes_uri_rewrite.load(cfgpath + "/lsnesurirewrite.cfg");
332 for(auto i : cmdline) {
333 regex_results r;
334 if(r = regex("--firmware-path=(.*)", i)) {
335 try {
336 lsnes_vsetc.set("firmwarepath", r[1]);
337 std::cerr << "Set firmware path to '" << r[1] << "'" << std::endl;
338 } catch(std::exception& e) {
339 std::cerr << "Can't set firmware path to '" << r[1] << "': " << e.what() << std::endl;
342 if(r = regex("--setting-(.*)=(.*)", i)) {
343 try {
344 lsnes_vset[r[1]].str(r[2]);
345 std::cerr << "Set " << r[1] << " to '" << r[2] << "'" << std::endl;
346 } catch(std::exception& e) {
347 std::cerr << "Can't set " << r[1] << " to '" << r[2] << "': " << e.what()
348 << std::endl;
353 set_hasher_callback(hash_callback);
355 std::string movfn;
356 for(auto i : cmdline) {
357 if(i.length() > 0 && i[0] != '-') {
358 movfn = i;
361 if(movfn == "") {
362 messages << "Movie filename required" << std::endl;
363 return 0;
366 try {
367 movfn = do_download_movie(movfn);
368 } catch(std::exception& e) {
369 messages << "FATAL: Can't download movie: " << e.what() << std::endl;
370 quit_lua();
371 fatal_error();
372 exit(1);
375 init_main_callbacks();
376 messages << "--- Loading ROM ---" << std::endl;
377 struct loaded_rom r;
378 try {
379 std::map<std::string, std::string> tmp;
380 r = construct_rom(movfn, cmdline);
381 r.load(tmp, 1000000000, 0);
382 messages << "Using core: " << r.rtype->get_core_identifier() << std::endl;
383 } catch(std::bad_alloc& e) {
384 OOM_panic();
385 } catch(std::exception& e) {
386 messages << "FATAL: Can't load ROM: " << e.what() << std::endl;
387 quit_lua();
388 fatal_error();
389 exit(1);
391 messages << "Detected region: " << r.rtype->combine_region(*r.region).get_name() << std::endl;
392 set_nominal_framerate(r.region->approx_framerate());
394 messages << "--- End of Startup --- " << std::endl;
396 moviefile* movie;
397 try {
398 movie = new moviefile(movfn, *r.rtype);
399 //Load ROM before starting the dumper.
400 our_rom = r;
401 messages << "Using core: " << our_rom.rtype->get_core_identifier() << std::endl;
402 our_rom.region = &movie->gametype->get_region();
403 our_rom.load(movie->settings, movie->movie_rtc_second, movie->movie_rtc_subsecond);
404 startup_lua_scripts(cmdline);
405 if(overdump_mode)
406 length = overdump_length + movie->get_frame_count();
407 dumper_startup(dumper, mode, prefix, length);
408 main_loop(r, *movie, true);
409 } catch(std::bad_alloc& e) {
410 OOM_panic();
411 } catch(std::exception& e) {
412 messages << "FATAL: " << e.what() << std::endl;
413 quit_lua();
414 fatal_error();
415 return 1;
417 information_dispatch::do_dump_end();
418 quit_lua();
419 movb.release_memory();
420 kill_alias_binds();
421 cleanup_all_keys();
422 return 0;