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/mainloop.hpp"
14 #include "core/misc.hpp"
15 #include "core/movie.hpp"
16 #include "core/moviedata.hpp"
17 #include "core/rom.hpp"
18 #include "core/romloader.hpp"
19 #include "core/settings.hpp"
20 #include "core/window.hpp"
21 #include "library/string.hpp"
28 bool hashing_in_progress
= false;
29 uint64_t hashing_left
= 0;
30 int64_t last_update
= 0;
32 void hash_callback(uint64_t left
, uint64_t total
)
34 if(left
== 0xFFFFFFFFFFFFFFFFULL
) {
35 hashing_in_progress
= false;
36 std::cout
<< "Done." << std::endl
;
37 last_update
= get_utime() - 2000000;
40 if(!hashing_in_progress
) {
41 std::cout
<< "Hashing disc images..." << std::flush
;
43 hashing_in_progress
= true;
45 uint64_t this_update
= get_utime();
46 if(this_update
< last_update
- 1000000 || this_update
> last_update
+ 1000000) {
47 std::cout
<< ((hashing_left
+ 524288) >> 20) << "..." << std::flush
;
48 last_update
= this_update
;
52 class myavsnoop
: public information_dispatch
55 myavsnoop(adv_dumper
& _dumper
, uint64_t frames_to_dump
)
56 : information_dispatch("myavsnoop-monitor"), dumper(_dumper
)
59 total
= frames_to_dump
;
66 void on_frame(struct framebuffer::raw
& _frame
, uint32_t fps_n
, uint32_t fps_d
)
69 if(frames_dumped
% 100 == 0) {
70 std::cout
<< "Dumping frame " << frames_dumped
<< "/" << total
<< " ("
71 << (100 * frames_dumped
/ total
) << "%)" << std::endl
;
73 if(frames_dumped
== total
) {
74 //Rough way to end it.
76 information_dispatch::do_dump_end();
83 std::cout
<< "Finished!" << std::endl
;
86 uint64_t frames_dumped
;
91 void dumper_startup(adv_dumper
& dumper
, const std::string
& mode
, const std::string
& prefix
, uint64_t length
)
93 std::cout
<< "Invoking dumper" << std::endl
;
95 dumper
.start(mode
, prefix
);
96 } catch(std::exception
& e
) {
97 std::cerr
<< "Can't start dumper: " << e
.what() << std::endl
;
100 if(information_dispatch::get_dumper_count()) {
101 std::cout
<< "Dumper attach confirmed" << std::endl
;
103 std::cout
<< "Can't start dumper!" << std::endl
;
106 new myavsnoop(dumper
, length
);
109 void startup_lua_scripts(const std::vector
<std::string
>& cmdline
)
111 for(auto i
= cmdline
.begin(); i
!= cmdline
.end(); i
++) {
113 if(a
.length() > 6 && a
.substr(0, 6) == "--lua=") {
114 lsnes_cmd
.invoke("run-lua " + a
.substr(6));
119 struct adv_dumper
& locate_dumper(const std::string
& name
)
121 adv_dumper
* _dumper
= NULL
;
122 std::set
<adv_dumper
*> dumpers
= adv_dumper::get_dumper_set();
123 for(auto i
: dumpers
)
127 std::cerr
<< "No such dumper '" << name
<< "' found (try --dumper=list)" << std::endl
;
133 std::string
format_details(unsigned detail
)
136 if((detail
& adv_dumper::target_type_mask
) == adv_dumper::target_type_file
)
137 r
= r
+ "TARGET_FILE";
138 else if((detail
& adv_dumper::target_type_mask
) == adv_dumper::target_type_prefix
)
139 r
= r
+ "TARGET_PREFIX";
140 else if((detail
& adv_dumper::target_type_mask
) == adv_dumper::target_type_special
)
141 r
= r
+ "TARGET_SPECIAL";
143 r
= r
+ "TARGET_UNKNOWN";
147 adv_dumper
& get_dumper(const std::vector
<std::string
>& cmdline
, std::string
& mode
, std::string
& prefix
,
148 uint64_t& length
, bool& overdump_mode
, uint64_t& overdump_length
)
150 bool dumper_given
= false;
152 bool mode_given
= false;
155 overdump_mode
= false;
157 for(auto i
= cmdline
.begin(); i
!= cmdline
.end(); i
++) {
159 if(a
== "--core=list") {
160 preferred_core_default
= "list";
161 } else if(a
.length() >= 9 && a
.substr(0, 9) == "--dumper=") {
163 dumper
= a
.substr(9);
164 } else if(a
.length() >= 7 && a
.substr(0, 7) == "--mode=") {
167 } else if(a
.length() >= 9 && a
.substr(0, 9) == "--prefix=")
168 prefix
= a
.substr(9);
169 else if(a
.length() >= 9 && a
.substr(0, 9) == "--length=")
171 length
= boost::lexical_cast
<uint64_t>(a
.substr(9));
173 throw std::runtime_error("Length out of range (1-)");
175 throw std::runtime_error("--length and --overdump-length are "
176 "mutually exclusive.");
177 } catch(std::exception
& e
) {
178 std::cerr
<< "Bad --length: " << e
.what() << std::endl
;
181 else if(a
.length() >= 18 && a
.substr(0, 18) == "--overdump-length=")
183 overdump_length
= boost::lexical_cast
<uint64_t>(a
.substr(18));
184 overdump_mode
= true;
186 throw std::runtime_error("--length and --overdump-length are "
187 "mutually exclusive.");
188 } catch(std::exception
& e
) {
189 std::cerr
<< "Bad --overdump-length: " << e
.what() << std::endl
;
192 else if(a
.length() >= 9 && a
.substr(0, 9) == "--option=") {
193 std::string nameval
= a
.substr(9);
194 size_t s
= nameval
.find_first_of("=");
195 if(s
>= nameval
.length()) {
196 std::cerr
<< "Invalid option syntax (expected --option=foo=bar)" << std::endl
;
199 std::string name
= nameval
.substr(0, s
);
200 std::string val
= nameval
.substr(s
+ 1);
202 lsnes_vset
[name
].str(val
);
203 } catch(std::exception
& e
) {
204 std::cerr
<< "Can't set '" << name
<< "' to '" << val
<< "': " << e
.what()
208 } else if(a
.length() >= 12 && a
.substr(0, 15) == "--load-library=")
210 with_loaded_library(*new loadlib::module(loadlib::library(a
.substr(15))));
211 handle_post_loadlibrary();
212 } catch(std::runtime_error
& e
) {
213 std::cerr
<< "Can't load '" << a
.substr(15) << "': " << e
.what() << std::endl
;
217 if(preferred_core_default
== "list") {
219 std::set
<std::pair
<std::string
, std::string
>> cores
;
220 for(auto i
: core_type::get_core_types())
221 cores
.insert(std::make_pair(i
->get_core_shortname(), i
->get_core_identifier()));
223 std::cout
<< i
.first
<< " -> " << i
.second
<< std::endl
;
226 if(dumper
== "list") {
228 std::set
<adv_dumper
*> dumpers
= adv_dumper::get_dumper_set();
229 std::cout
<< "Dumpers available:" << std::endl
;
230 for(auto i
: dumpers
)
231 std::cout
<< i
->id() << "\t" << i
->name() << std::endl
;
235 std::cerr
<< "Dumper required (--dumper=foo)" << std::endl
;
240 adv_dumper
& _dumper
= locate_dumper(dumper
);
241 std::set
<std::string
> modes
= _dumper
.list_submodes();
243 unsigned d
= _dumper
.mode_details("");
244 std::cout
<< "No modes available for " << dumper
<< " (" << format_details(d
) << ")"
248 std::cout
<< "Modes available for " << dumper
<< ":" << std::endl
;
249 for(auto i
: modes
) {
250 unsigned d
= _dumper
.mode_details(i
);
251 std::cout
<< i
<< "\t" << _dumper
.modename(i
) << "\t(" << format_details(d
) << ")"
256 adv_dumper
& _dumper
= locate_dumper(dumper
);
257 if(!mode_given
&& !_dumper
.list_submodes().empty()) {
258 std::cerr
<< "Mode required for this dumper" << std::endl
;
261 if(mode_given
&& _dumper
.list_submodes().empty()) {
262 std::cerr
<< "This dumper does not have mode select" << std::endl
;
265 if(mode_given
&& !_dumper
.list_submodes().count(mode
)) {
266 std::cerr
<< "'" << mode
<< "' is not a valid mode for '" << dumper
<< "'" << std::endl
;
269 if(!length
&& !overdump_mode
) {
270 std::cerr
<< "--length=<frames> or --overdump-length=<frames> has to be specified"
274 return locate_dumper(dumper
);
278 int main(int argc
, char** argv
)
281 std::vector
<std::string
> cmdline
;
282 for(int i
= 1; i
< argc
; i
++)
283 cmdline
.push_back(argv
[i
]);
284 uint64_t length
, overdump_length
;
286 std::string mode
, prefix
;
288 adv_dumper
& dumper
= get_dumper(cmdline
, mode
, prefix
, length
, overdump_mode
, overdump_length
);
294 messages
<< "lsnes version: lsnes rr" << lsnes_version
<< std::endl
;
295 messages
<< "Command line is: ";
296 for(auto k
= cmdline
.begin(); k
!= cmdline
.end(); k
++)
297 messages
<< "\"" << *k
<< "\" ";
298 messages
<< std::endl
;
300 std::string cfgpath
= get_config_path();
301 autoload_libraries();
303 for(auto i
: cmdline
) {
305 if(r
= regex("--firmware-path=(.*)", i
)) {
307 lsnes_vsetc
.set("firmwarepath", r
[1]);
308 std::cerr
<< "Set firmware path to '" << r
[1] << "'" << std::endl
;
309 } catch(std::exception
& e
) {
310 std::cerr
<< "Can't set firmware path to '" << r
[1] << "': " << e
.what() << std::endl
;
313 if(r
= regex("--setting-(.*)=(.*)", i
)) {
315 lsnes_vset
[r
[1]].str(r
[2]);
316 std::cerr
<< "Set " << r
[1] << " to '" << r
[2] << "'" << std::endl
;
317 } catch(std::exception
& e
) {
318 std::cerr
<< "Can't set " << r
[1] << " to '" << r
[2] << "': " << e
.what()
324 set_hasher_callback(hash_callback
);
327 for(auto i
: cmdline
) {
328 if(i
.length() > 0 && i
[0] != '-') {
333 messages
<< "Movie filename required" << std::endl
;
337 messages
<< "--- Loading ROM ---" << std::endl
;
340 std::map
<std::string
, std::string
> tmp
;
341 r
= construct_rom(movfn
, cmdline
);
342 r
.load(tmp
, 1000000000, 0);
343 messages
<< "Using core: " << r
.rtype
->get_core_identifier() << std::endl
;
344 } catch(std::bad_alloc
& e
) {
346 } catch(std::exception
& e
) {
347 messages
<< "FATAL: Can't load ROM: " << e
.what() << std::endl
;
352 messages
<< "Detected region: " << r
.rtype
->combine_region(*r
.region
).get_name() << std::endl
;
353 set_nominal_framerate(r
.region
->approx_framerate());
355 messages
<< "--- End of Startup --- " << std::endl
;
359 movie
= new moviefile(movfn
, *r
.rtype
);
360 //Load ROM before starting the dumper.
362 messages
<< "Using core: " << our_rom
.rtype
->get_core_identifier() << std::endl
;
363 our_rom
.region
= &movie
->gametype
->get_region();
364 our_rom
.load(movie
->settings
, movie
->movie_rtc_second
, movie
->movie_rtc_subsecond
);
365 startup_lua_scripts(cmdline
);
367 length
= overdump_length
+ movie
->get_frame_count();
368 dumper_startup(dumper
, mode
, prefix
, length
);
369 main_loop(r
, *movie
, true);
370 } catch(std::bad_alloc
& e
) {
372 } catch(std::exception
& e
) {
373 messages
<< "FATAL: " << e
.what() << std::endl
;
378 information_dispatch::do_dump_end();
380 movb
.release_memory();