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"
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
;
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;
71 if(!hashing_in_progress
) {
72 std::cout
<< "Hashing disc images..." << std::flush
;
74 hashing_in_progress
= true;
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
86 myavsnoop(adv_dumper
& _dumper
, uint64_t frames_to_dump
)
87 : information_dispatch("myavsnoop-monitor"), dumper(_dumper
)
90 total
= frames_to_dump
;
97 void on_frame(struct framebuffer::raw
& _frame
, uint32_t fps_n
, uint32_t fps_d
)
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.
107 information_dispatch::do_dump_end();
114 std::cout
<< "Finished!" << std::endl
;
117 uint64_t frames_dumped
;
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
;
126 dumper
.start(mode
, prefix
);
127 } catch(std::exception
& e
) {
128 std::cerr
<< "Can't start dumper: " << e
.what() << std::endl
;
131 if(information_dispatch::get_dumper_count()) {
132 std::cout
<< "Dumper attach confirmed" << std::endl
;
134 std::cout
<< "Can't start dumper!" << std::endl
;
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
++) {
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
)
158 std::cerr
<< "No such dumper '" << name
<< "' found (try --dumper=list)" << std::endl
;
164 std::string
format_details(unsigned detail
)
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";
174 r
= r
+ "TARGET_UNKNOWN";
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;
183 bool mode_given
= false;
186 overdump_mode
= false;
188 for(auto i
= cmdline
.begin(); i
!= cmdline
.end(); i
++) {
190 if(a
.length() >= 9 && a
.substr(0, 9) == "--dumper=") {
192 dumper
= a
.substr(9);
193 } else if(a
.length() >= 7 && a
.substr(0, 7) == "--mode=") {
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=")
200 length
= boost::lexical_cast
<uint64_t>(a
.substr(9));
202 throw std::runtime_error("Length out of range (1-)");
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
;
210 else if(a
.length() >= 18 && a
.substr(0, 18) == "--overdump-length=")
212 overdump_length
= boost::lexical_cast
<uint64_t>(a
.substr(18));
213 overdump_mode
= true;
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
;
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
;
228 std::string name
= nameval
.substr(0, s
);
229 std::string val
= nameval
.substr(s
+ 1);
231 lsnes_vset
[name
].str(val
);
232 } catch(std::exception
& e
) {
233 std::cerr
<< "Can't set '" << name
<< "' to '" << val
<< "': " << e
.what()
237 } else if(a
.length() >= 12 && a
.substr(0, 15) == "--load-library=")
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
;
246 if(dumper
== "list") {
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
;
255 std::cerr
<< "Dumper required (--dumper=foo)" << std::endl
;
260 adv_dumper
& _dumper
= locate_dumper(dumper
);
261 std::set
<std::string
> modes
= _dumper
.list_submodes();
263 unsigned d
= _dumper
.mode_details("");
264 std::cout
<< "No modes available for " << dumper
<< " (" << format_details(d
) << ")"
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
) << ")"
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
;
281 if(mode_given
&& _dumper
.list_submodes().empty()) {
282 std::cerr
<< "This dumper does not have mode select" << std::endl
;
285 if(mode_given
&& !_dumper
.list_submodes().count(mode
)) {
286 std::cerr
<< "'" << mode
<< "' is not a valid mode for '" << dumper
<< "'" << std::endl
;
289 if(!length
&& !overdump_mode
) {
290 std::cerr
<< "--length=<frames> or --overdump-length=<frames> has to be specified"
294 return locate_dumper(dumper
);
298 int main(int argc
, char** argv
)
302 } catch(std::exception
& e
) {
303 std::cerr
<< "Error initializing system RNG" << std::endl
;
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
;
313 std::string mode
, prefix
;
315 adv_dumper
& dumper
= get_dumper(cmdline
, mode
, prefix
, length
, overdump_mode
, overdump_length
);
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
) {
334 if(r
= regex("--firmware-path=(.*)", i
)) {
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
)) {
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()
353 set_hasher_callback(hash_callback
);
356 for(auto i
: cmdline
) {
357 if(i
.length() > 0 && i
[0] != '-') {
362 messages
<< "Movie filename required" << std::endl
;
367 movfn
= do_download_movie(movfn
);
368 } catch(std::exception
& e
) {
369 messages
<< "FATAL: Can't download movie: " << e
.what() << std::endl
;
375 init_main_callbacks();
376 messages
<< "--- Loading ROM ---" << std::endl
;
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
) {
385 } catch(std::exception
& e
) {
386 messages
<< "FATAL: Can't load ROM: " << e
.what() << std::endl
;
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
;
398 movie
= new moviefile(movfn
, *r
.rtype
);
399 //Load ROM before starting the dumper.
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
);
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
) {
411 } catch(std::exception
& e
) {
412 messages
<< "FATAL: " << e
.what() << std::endl
;
417 information_dispatch::do_dump_end();
419 movb
.release_memory();