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/messages.hpp"
16 #include "core/misc.hpp"
17 #include "core/instance.hpp"
18 #include "core/moviedata.hpp"
19 #include "core/random.hpp"
20 #include "core/rom.hpp"
21 #include "core/settings.hpp"
22 #include "core/window.hpp"
23 #include "library/directory.hpp"
24 #include "library/crandom.hpp"
25 #include "library/string.hpp"
32 bool hashing_in_progress
= false;
33 uint64_t hashing_left
= 0;
34 int64_t last_update
= 0;
36 std::string
do_download_movie(const std::string
& origname
)
38 if(!regex_match("[A-Za-z][A-Za-z0-9+.-]*:.+", origname
))
39 return origname
; //File.
40 if(directory::is_regular(origname
))
41 return origname
; //Even exists.
42 //Okay, we need to download this.
43 auto download_in_progress
= new file_download();
44 download_in_progress
->url
= lsnes_uri_rewrite(origname
);
45 if(download_in_progress
->url
!= origname
)
46 messages
<< "Internally redirecting to " << download_in_progress
->url
<< std::endl
;
47 download_in_progress
->target_slot
= "dumpavi_download_tmp";
48 download_in_progress
->do_async(*lsnes_instance
.rom
);
49 messages
<< "Downloading " << download_in_progress
->url
<< ":" << std::endl
;
50 while(!download_in_progress
->finished
) {
51 messages
<< download_in_progress
->statusmsg() << std::endl
;
54 if(download_in_progress
->errormsg
!= "") {
55 std::string err
= download_in_progress
->errormsg
;
56 delete download_in_progress
;
57 throw std::runtime_error(err
);
59 delete download_in_progress
;
60 messages
<< "Download finished." << std::endl
;
61 return "$MEMORY:dumpavi_download_tmp";
64 void hash_callback(uint64_t left
, uint64_t total
)
66 if(left
== 0xFFFFFFFFFFFFFFFFULL
) {
67 hashing_in_progress
= false;
68 std::cout
<< "Done." << std::endl
;
69 last_update
= framerate_regulator::get_utime() - 2000000;
72 if(!hashing_in_progress
) {
73 std::cout
<< "Hashing disc images..." << std::flush
;
75 hashing_in_progress
= true;
77 int64_t this_update
= framerate_regulator::get_utime();
78 if(this_update
< last_update
- 1000000 || this_update
> last_update
+ 1000000) {
79 std::cout
<< ((hashing_left
+ 524288) >> 20) << "..." << std::flush
;
80 last_update
= this_update
;
84 class myavsnoop
: public dumper_base
87 myavsnoop(dumper_base
& _dumper
, uint64_t frames_to_dump
)
91 total
= frames_to_dump
;
92 lsnes_instance
.mdumper
->add_dumper(*this);
97 lsnes_instance
.mdumper
->drop_dumper(*this);
100 void on_frame(struct framebuffer::raw
& _frame
, uint32_t fps_n
, uint32_t fps_d
)
103 if(frames_dumped
% 100 == 0) {
104 std::cout
<< "Dumping frame " << frames_dumped
<< "/" << total
<< " ("
105 << (100 * frames_dumped
/ total
) << "%)" << std::endl
;
107 if(frames_dumped
>= total
) {
108 //Rough way to end it.
109 CORE().command
->invoke("quit-emulator");
112 void on_sample(short l
, short r
)
114 //We aren't interested in samples.
116 void on_rate_change(uint32_t n
, uint32_t d
)
118 //We aren't interested in samples.
120 void on_gameinfo_change(const master_dumper::gameinfo
& gi
)
122 std::cout
<< "Game:" << gi
.gamename
<< std::endl
;
123 std::cout
<< "Length:" << gi
.get_readable_time(3) << std::endl
;
124 std::cout
<< "Rerecords:" << gi
.get_rerecords() << std::endl
;
125 for(unsigned i
= 0; i
< gi
.get_author_count(); i
++)
126 std::cout
<< "Author: " << gi
.get_author_long(i
) << std::endl
;
130 std::cout
<< "Finished!" << std::endl
;
134 uint64_t frames_dumped
;
139 void dumper_startup(dumper_factory_base
& dumper
, const std::string
& mode
, const std::string
& prefix
,
142 dumper_base
* _dumper
;
143 std::cout
<< "Invoking dumper" << std::endl
;
145 _dumper
= lsnes_instance
.mdumper
->start(dumper
, mode
, prefix
);
146 } catch(std::exception
& e
) {
147 std::cerr
<< "Can't start dumper: " << e
.what() << std::endl
;
150 if(lsnes_instance
.mdumper
->get_dumper_count()) {
151 std::cout
<< "Dumper attach confirmed" << std::endl
;
153 std::cout
<< "Can't start dumper!" << std::endl
;
156 auto d
= new myavsnoop(*_dumper
, length
);
157 d
->on_gameinfo_change(lsnes_instance
.mdumper
->get_gameinfo());
160 void startup_lua_scripts(const std::vector
<std::string
>& cmdline
)
162 for(auto i
= cmdline
.begin(); i
!= cmdline
.end(); i
++) {
164 if(a
.length() > 6 && a
.substr(0, 6) == "--lua=") {
165 lsnes_instance
.lua2
->add_startup_script(a
.substr(6));
170 struct dumper_factory_base
& locate_dumper(const std::string
& name
)
172 dumper_factory_base
* _dumper
= NULL
;
173 std::set
<dumper_factory_base
*> dumpers
= dumper_factory_base::get_dumper_set();
174 for(auto i
: dumpers
)
178 std::cerr
<< "No such dumper '" << name
<< "' found (try --dumper=list)" << std::endl
;
184 std::string
format_details(unsigned detail
)
187 if((detail
& dumper_factory_base::target_type_mask
) == dumper_factory_base::target_type_file
)
188 r
= r
+ "TARGET_FILE";
189 else if((detail
& dumper_factory_base::target_type_mask
) == dumper_factory_base::target_type_prefix
)
190 r
= r
+ "TARGET_PREFIX";
191 else if((detail
& dumper_factory_base::target_type_mask
) == dumper_factory_base::target_type_special
)
192 r
= r
+ "TARGET_SPECIAL";
194 r
= r
+ "TARGET_UNKNOWN";
198 dumper_factory_base
& get_dumper(const std::vector
<std::string
>& cmdline
, std::string
& mode
,
199 std::string
& prefix
, uint64_t& length
, bool& overdump_mode
, uint64_t& overdump_length
)
201 bool dumper_given
= false;
203 bool mode_given
= false;
206 overdump_mode
= false;
208 for(auto i
= cmdline
.begin(); i
!= cmdline
.end(); i
++) {
210 if(a
.length() >= 9 && a
.substr(0, 9) == "--dumper=") {
212 dumper
= a
.substr(9);
213 } else if(a
.length() >= 7 && a
.substr(0, 7) == "--mode=") {
216 } else if(a
.length() >= 9 && a
.substr(0, 9) == "--prefix=")
217 prefix
= a
.substr(9);
218 else if(a
.length() >= 9 && a
.substr(0, 9) == "--length=")
220 length
= boost::lexical_cast
<uint64_t>(a
.substr(9));
222 throw std::runtime_error("Length out of range (1-)");
224 throw std::runtime_error("--length and --overdump-length are "
225 "mutually exclusive.");
226 } catch(std::exception
& e
) {
227 std::cerr
<< "Bad --length: " << e
.what() << std::endl
;
230 else if(a
.length() >= 18 && a
.substr(0, 18) == "--overdump-length=")
232 overdump_length
= boost::lexical_cast
<uint64_t>(a
.substr(18));
233 overdump_mode
= true;
235 throw std::runtime_error("--length and --overdump-length are "
236 "mutually exclusive.");
237 } catch(std::exception
& e
) {
238 std::cerr
<< "Bad --overdump-length: " << e
.what() << std::endl
;
241 else if(a
.length() >= 9 && a
.substr(0, 9) == "--option=") {
242 std::string nameval
= a
.substr(9);
243 size_t s
= nameval
.find_first_of("=");
244 if(s
>= nameval
.length()) {
245 std::cerr
<< "Invalid option syntax (expected --option=foo=bar)" << std::endl
;
248 std::string name
= nameval
.substr(0, s
);
249 std::string val
= nameval
.substr(s
+ 1);
251 lsnes_instance
.setcache
->set(name
, val
);
252 } catch(std::exception
& e
) {
253 std::cerr
<< "Can't set '" << name
<< "' to '" << val
<< "': " << e
.what()
257 } else if(a
.length() >= 12 && a
.substr(0, 15) == "--load-library=")
259 with_loaded_library(*new loadlib::module(loadlib::library(a
.substr(15))));
260 handle_post_loadlibrary();
261 } catch(std::runtime_error
& e
) {
262 std::cerr
<< "Can't load '" << a
.substr(15) << "': " << e
.what() << std::endl
;
266 if(dumper
== "list") {
268 std::set
<dumper_factory_base
*> dumpers
= dumper_factory_base::get_dumper_set();
269 std::cout
<< "Dumpers available:" << std::endl
;
270 for(auto i
: dumpers
)
271 std::cout
<< i
->id() << "\t" << i
->name() << std::endl
;
275 std::cerr
<< "Dumper required (--dumper=foo)" << std::endl
;
280 dumper_factory_base
& _dumper
= locate_dumper(dumper
);
281 std::set
<std::string
> modes
= _dumper
.list_submodes();
283 unsigned d
= _dumper
.mode_details("");
284 std::cout
<< "No modes available for " << dumper
<< " (" << format_details(d
) << ")"
288 std::cout
<< "Modes available for " << dumper
<< ":" << std::endl
;
289 for(auto i
: modes
) {
290 unsigned d
= _dumper
.mode_details(i
);
291 std::cout
<< i
<< "\t" << _dumper
.modename(i
) << "\t(" << format_details(d
) << ")"
296 dumper_factory_base
& _dumper
= locate_dumper(dumper
);
297 if(!mode_given
&& !_dumper
.list_submodes().empty()) {
298 std::cerr
<< "Mode required for this dumper" << std::endl
;
301 if(mode_given
&& _dumper
.list_submodes().empty()) {
302 std::cerr
<< "This dumper does not have mode select" << std::endl
;
305 if(mode_given
&& !_dumper
.list_submodes().count(mode
)) {
306 std::cerr
<< "'" << mode
<< "' is not a valid mode for '" << dumper
<< "'" << std::endl
;
309 if(!length
&& !overdump_mode
) {
310 std::cerr
<< "--length=<frames> or --overdump-length=<frames> has to be specified"
314 return locate_dumper(dumper
);
318 int main(int argc
, char** argv
)
322 } catch(std::exception
& e
) {
323 std::cerr
<< "Error initializing system RNG" << std::endl
;
328 std::vector
<std::string
> cmdline
;
329 for(int i
= 1; i
< argc
; i
++)
330 cmdline
.push_back(argv
[i
]);
331 uint64_t length
, overdump_length
;
333 std::string mode
, prefix
;
335 dumper_factory_base
& dumper
= get_dumper(cmdline
, mode
, prefix
, length
, overdump_mode
, overdump_length
);
340 lsnes_instance
.mdumper
->set_output(&messages
.getstream());
341 set_hasher_callback(hash_callback
);
343 messages
<< "lsnes version: lsnes rr" << lsnes_version
<< std::endl
;
344 messages
<< "Command line is: ";
345 for(auto k
= cmdline
.begin(); k
!= cmdline
.end(); k
++)
346 messages
<< "\"" << *k
<< "\" ";
347 messages
<< std::endl
;
349 std::string cfgpath
= get_config_path();
350 autoload_libraries();
352 lsnes_uri_rewrite
.load(cfgpath
+ "/lsnesurirewrite.cfg");
354 for(auto i
: cmdline
) {
356 if(r
= regex("--firmware-path=(.*)", i
)) {
358 lsnes_instance
.setcache
->set("firmwarepath", r
[1]);
359 std::cerr
<< "Set firmware path to '" << r
[1] << "'" << std::endl
;
360 } catch(std::exception
& e
) {
361 std::cerr
<< "Can't set firmware path to '" << r
[1] << "': " << e
.what() << std::endl
;
364 if(r
= regex("--setting-(.*)=(.*)", i
)) {
366 lsnes_instance
.setcache
->set(r
[1], r
[2]);
367 std::cerr
<< "Set " << r
[1] << " to '" << r
[2] << "'" << std::endl
;
368 } catch(std::exception
& e
) {
369 std::cerr
<< "Can't set " << r
[1] << " to '" << r
[2] << "': " << e
.what()
376 for(auto i
: cmdline
) {
377 if(i
.length() > 0 && i
[0] != '-') {
382 messages
<< "Movie filename required" << std::endl
;
387 movfn
= do_download_movie(movfn
);
388 } catch(std::exception
& e
) {
389 messages
<< "FATAL: Can't download movie: " << e
.what() << std::endl
;
395 init_main_callbacks();
396 messages
<< "--- Loading ROM ---" << std::endl
;
399 std::map
<std::string
, std::string
> tmp
;
400 r
= construct_rom(movfn
, cmdline
);
401 r
.load(tmp
, 1000000000, 0);
402 messages
<< "Using core: " << r
.get_core_identifier() << std::endl
;
403 } catch(std::bad_alloc
& e
) {
405 } catch(std::exception
& e
) {
406 messages
<< "FATAL: Can't load ROM: " << e
.what() << std::endl
;
411 messages
<< "Detected region: " << r
.get_sysregion().get_name() << std::endl
;
412 lsnes_instance
.framerate
->set_nominal_framerate(r
.region_approx_framerate());
414 messages
<< "--- End of Startup --- " << std::endl
;
418 movie
= new moviefile(movfn
, r
.get_internal_rom_type());
419 //Load ROM before starting the dumper.
420 *lsnes_instance
.rom
= r
;
421 messages
<< "Using core: " << lsnes_instance
.rom
->get_core_identifier() << std::endl
;
422 lsnes_instance
.rom
->set_internal_region(movie
->gametype
->get_region());
423 lsnes_instance
.rom
->load(movie
->settings
, movie
->movie_rtc_second
, movie
->movie_rtc_subsecond
);
424 startup_lua_scripts(cmdline
);
426 length
= overdump_length
+ movie
->get_frame_count();
427 dumper_startup(dumper
, mode
, prefix
, length
);
428 main_loop(r
, *movie
, true);
429 } catch(std::bad_alloc
& e
) {
431 } catch(std::exception
& e
) {
432 messages
<< "FATAL: " << e
.what() << std::endl
;
438 lsnes_instance
.mlogic
->release_memory();
439 lsnes_instance
.buttons
->cleanup();