Various improvements and cleanups to video dumping

This commit is contained in:
Ilari Liusvaara 2011-11-05 00:37:32 +02:00
parent f73a2debd3
commit 02ad621b2c
18 changed files with 741 additions and 378 deletions

View file

@ -33,6 +33,7 @@ namespace
{
public:
avi_avsnoop(const std::string& prefix, struct avi_info parameters) throw(std::bad_alloc)
: av_snooper("AVI")
{
_parameters = parameters;
avi_cscd_dumper::global_parameters gp;
@ -65,8 +66,8 @@ namespace
delete soxdumper;
}
void frame(struct lcscreen& _frame, uint32_t fps_n, uint32_t fps_d)
throw(std::bad_alloc, std::runtime_error)
void frame(struct lcscreen& _frame, uint32_t fps_n, uint32_t fps_d, const uint32_t* raw, bool hires,
bool interlaced, bool overscan, unsigned region) throw(std::bad_alloc, std::runtime_error)
{
uint32_t hscl = 1;
uint32_t vscl = 1;
@ -130,13 +131,6 @@ namespace
vid_dumper->end();
soxdumper->close();
}
void gameinfo(const std::string& gamename, const std::list<std::pair<std::string, std::string>>&
authors, double gametime, const std::string& rerecords) throw(std::bad_alloc,
std::runtime_error)
{
//We don't have place for this info and thus ignore it.
}
private:
avi_cscd_dumper* vid_dumper;
sox_dumper* soxdumper;
@ -160,16 +154,17 @@ namespace
if(prefix == "")
throw std::runtime_error("Expected prefix");
if(vid_dumper)
throw std::runtime_error("AVI dumping already in progress");
throw std::runtime_error("AVI(CSCD) dumping already in progress");
unsigned long level2;
try {
level2 = parse_value<unsigned long>(level);
if(level2 > 18)
throw std::runtime_error("Level must be 0-18");
throw std::runtime_error("AVI(CSCD) level must be 0-18");
} catch(std::bad_alloc& e) {
throw;
} catch(std::runtime_error& e) {
throw std::runtime_error("Bad AVI compression level '" + level + "': " + e.what());
throw std::runtime_error("Bad AVI(CSCD) compression level '" + level + "': " +
e.what());
}
struct avi_info parameters;
parameters.compression_level = (level2 > 9) ? (level2 - 9) : level2;
@ -182,24 +177,24 @@ namespace
throw;
} catch(std::exception& e) {
std::ostringstream x;
x << "Error starting dump: " << e.what();
x << "Error starting AVI(CSCD) dump: " << e.what();
throw std::runtime_error(x.str());
}
messages << "Dumping to " << prefix << " at level " << level2 << std::endl;
messages << "Dumping AVI(CSCD) to " << prefix << " at level " << level2 << std::endl;
});
function_ptr_command<> end_avi("end-avi", "End AVI capture",
"Syntax: end-avi\nEnd a AVI capture.\n",
[]() throw(std::bad_alloc, std::runtime_error) {
if(!vid_dumper)
throw std::runtime_error("No video dump in progress");
throw std::runtime_error("No AVI(CSCD) video dump in progress");
try {
vid_dumper->end();
messages << "Dump finished" << std::endl;
messages << "AVI(CSCD) Dump finished" << std::endl;
} catch(std::bad_alloc& e) {
throw;
} catch(std::exception& e) {
messages << "Error ending dump: " << e.what() << std::endl;
messages << "Error ending AVI(CSCD) dump: " << e.what() << std::endl;
}
delete vid_dumper;
vid_dumper = NULL;

View file

@ -17,6 +17,7 @@ namespace
{
public:
jmd_avsnoop(const std::string& filename, unsigned level) throw(std::bad_alloc)
: av_snooper("JMD")
{
vid_dumper = new jmd_dumper(filename, level);
have_dumped_frame = false;
@ -26,6 +27,11 @@ namespace
video_n = 0;
maxtc = 0;
soundrate = av_snooper::get_sound_rate();
try {
gameinfo(av_snooper::get_gameinfo());
} catch(std::exception& e) {
messages << "Can't write gameinfo: " << e.what() << std::endl;
}
}
~jmd_avsnoop() throw()
@ -33,8 +39,8 @@ namespace
delete vid_dumper;
}
void frame(struct lcscreen& _frame, uint32_t fps_n, uint32_t fps_d)
throw(std::bad_alloc, std::runtime_error)
void frame(struct lcscreen& _frame, uint32_t fps_n, uint32_t fps_d, const uint32_t* raw, bool hires,
bool interlaced, bool overscan, unsigned region) throw(std::bad_alloc, std::runtime_error)
{
struct lua_render_context lrc;
render_queue rq;
@ -46,6 +52,7 @@ namespace
lrc.width = _frame.width;
lrc.height = _frame.height;
lua_callback_do_video(&lrc);
dscr.set_palette(0, 8, 16);
dscr.reallocate(lrc.left_gap + _frame.width + lrc.right_gap, lrc.top_gap + _frame.height +
lrc.bottom_gap, lrc.left_gap, lrc.top_gap, false);
dscr.copy_from(_frame, 1, 1);
@ -67,11 +74,15 @@ namespace
vid_dumper->end(maxtc);
}
void gameinfo(const std::string& gamename, const std::list<std::pair<std::string, std::string>>&
authors, double gametime, const std::string& rerecords) throw(std::bad_alloc,
std::runtime_error)
void gameinfo(const struct gameinfo_struct& gi) throw(std::bad_alloc, std::runtime_error)
{
vid_dumper->gameinfo(gamename, authors, gametime, rerecords);
std::string authstr;
for(size_t i = 0; i < gi.get_author_count(); i++) {
if(i != 0)
authstr = authstr + ", ";
authstr = authstr + gi.get_author_short(i);
}
vid_dumper->gameinfo(gi.gamename, authstr, 1000000000ULL * gi.length, gi.get_rerecords());
}
private:
uint64_t get_next_video_ts(uint32_t fps_n, uint32_t fps_d)
@ -129,7 +140,7 @@ namespace
try {
level2 = parse_value<unsigned long>(level);
if(level2 > 9)
throw std::runtime_error("Level must be 0-9");
throw std::runtime_error("JMD Level must be 0-9");
} catch(std::bad_alloc& e) {
throw;
} catch(std::runtime_error& e) {
@ -141,7 +152,7 @@ namespace
throw;
} catch(std::exception& e) {
std::ostringstream x;
x << "Error starting dump: " << e.what();
x << "Error starting JMD dump: " << e.what();
throw std::runtime_error(x.str());
}
messages << "Dumping to " << prefix << " at level " << level2 << std::endl;
@ -151,14 +162,14 @@ namespace
"Syntax: end-jmd\nEnd a JMD capture.\n",
[]() throw(std::bad_alloc, std::runtime_error) {
if(!vid_dumper)
throw std::runtime_error("No video dump in progress");
throw std::runtime_error("No JMD video dump in progress");
try {
vid_dumper->end();
messages << "Dump finished" << std::endl;
messages << "JMD Dump finished" << std::endl;
} catch(std::bad_alloc& e) {
throw;
} catch(std::exception& e) {
messages << "Error ending dump: " << e.what() << std::endl;
messages << "Error ending JMD dump: " << e.what() << std::endl;
}
delete vid_dumper;
vid_dumper = NULL;

View file

@ -91,8 +91,8 @@ void jmd_dumper::end(uint64_t ts)
jmd.close();
}
void jmd_dumper::gameinfo(const std::string& gamename, const std::list<std::pair<std::string, std::string>>&
authors, double gametime, const std::string& rerecords)
void jmd_dumper::gameinfo(const std::string& gamename, const std::string& authors, uint64_t gametime,
uint64_t rerecords)
{
//FIXME: Implement this.
}

View file

@ -15,8 +15,7 @@ public:
~jmd_dumper();
void video(uint64_t ts, uint32_t* memory, uint32_t width, uint32_t height);
void audio(uint64_t ts, short l, short r);
void gameinfo(const std::string& gamename, const std::list<std::pair<std::string, std::string>>&
authors, double gametime, const std::string& rerecords);
void gameinfo(const std::string& gamename, const std::string& authors, uint64_t gametime, uint64_t rerecords);
void end(uint64_t ts);
private:
struct frame_buffer

108
avidump/sdmp-control.cpp Normal file
View file

@ -0,0 +1,108 @@
#include "lua.hpp"
#include "sdmp.hpp"
#include "settings.hpp"
#include "misc.hpp"
#include <iomanip>
#include <cassert>
#include <cstring>
#include <sstream>
#include <zlib.h>
#include "misc.hpp"
#include "avsnoop.hpp"
#include "command.hpp"
namespace
{
class sdmp_avsnoop : public av_snooper
{
public:
sdmp_avsnoop(const std::string& prefix, bool ssflag) throw(std::bad_alloc)
: av_snooper("SDMP")
{
dumper = new sdump_dumper(prefix, ssflag);
}
~sdmp_avsnoop() throw()
{
delete dumper;
}
void frame(struct lcscreen& _frame, uint32_t fps_n, uint32_t fps_d, const uint32_t* raw, bool hires,
bool interlaced, bool overscan, unsigned region) throw(std::bad_alloc, std::runtime_error)
{
unsigned flags = 0;
dumper->frame(raw, (hires ? SDUMP_FLAG_HIRES : 0) | (interlaced ? SDUMP_FLAG_INTERLACED : 0) |
(overscan ? SDUMP_FLAG_OVERSCAN : 0) | (region == SNOOP_REGION_PAL ? SDUMP_FLAG_PAL :
0));
}
void sample(short l, short r) throw(std::bad_alloc, std::runtime_error)
{
dumper->sample(l, r);
}
void end() throw(std::bad_alloc, std::runtime_error)
{
dumper->end();
}
private:
sdump_dumper* dumper;
};
sdmp_avsnoop* vid_dumper;
function_ptr_command<const std::string&> jmd_dump("dump-sdmp", "Start sdmp capture",
"Syntax: dump-sdmp <prefix>\nStart SDMP capture to <prefix>\n",
[](const std::string& prefix) throw(std::bad_alloc, std::runtime_error) {
if(prefix == "")
throw std::runtime_error("Expected prefix");
if(vid_dumper)
throw std::runtime_error("SDMP Dump already in progress");
try {
vid_dumper = new sdmp_avsnoop(prefix, false);
} catch(std::bad_alloc& e) {
throw;
} catch(std::exception& e) {
std::ostringstream x;
x << "Error starting SDMP dump: " << e.what();
throw std::runtime_error(x.str());
}
messages << "Dumping SDMP to " << prefix << std::endl;
});
function_ptr_command<const std::string&> jmd_dumpss("dump-sdmpss", "Start SS sdmp capture",
"Syntax: dump-sdmpss <file>\nStart SS SDMP capture to <file>\n",
[](const std::string& prefix) throw(std::bad_alloc, std::runtime_error) {
if(prefix == "")
throw std::runtime_error("Expected filename");
if(vid_dumper)
throw std::runtime_error("SDMP Dump already in progress");
try {
vid_dumper = new sdmp_avsnoop(prefix, true);
} catch(std::bad_alloc& e) {
throw;
} catch(std::exception& e) {
std::ostringstream x;
x << "Error starting SDMP dump: " << e.what();
throw std::runtime_error(x.str());
}
messages << "Dumping SDMP to " << prefix << std::endl;
});
function_ptr_command<> end_avi("end-sdmp", "End SDMP capture",
"Syntax: end-sdmp\nEnd a SDMP capture.\n",
[]() throw(std::bad_alloc, std::runtime_error) {
if(!vid_dumper)
throw std::runtime_error("No SDMP video dump in progress");
try {
vid_dumper->end();
messages << "SDMP Dump finished" << std::endl;
} catch(std::bad_alloc& e) {
throw;
} catch(std::exception& e) {
messages << "Error ending SDMP dump: " << e.what() << std::endl;
}
delete vid_dumper;
vid_dumper = NULL;
});
}

99
avidump/sdmp.cpp Normal file
View file

@ -0,0 +1,99 @@
#include "lsnes.hpp"
#include <snes/snes.hpp>
#include "sdmp.hpp"
#include "avsnoop.hpp"
#include <sstream>
#include <iomanip>
#define CUTOFF 2100000000
sdump_dumper::sdump_dumper(const std::string& prefix, bool ss)
{
oprefix = prefix;
sdump_ss = ss;
ssize = 0;
next_seq = 0;
sdump_iopen = false;
}
sdump_dumper::~sdump_dumper() throw()
{
try {
end();
} catch(...) {
}
}
void sdump_dumper::frame(const uint32_t* buffer, unsigned flags)
{
flags &= 0xF;
unsigned char tbuffer[2049];
if(!sdump_iopen || (ssize > CUTOFF && !sdump_ss)) {
std::cerr << "Starting new segment" << std::endl;
if(sdump_iopen)
out.close();
std::ostringstream str;
if(sdump_ss)
str << oprefix;
else
str << oprefix << "_" << std::setw(4) << std::setfill('0') << (next_seq++) << ".sdmp";
std::string str2 = str.str();
out.open(str2.c_str(), std::ios::out | std::ios::binary);
if(!out)
throw std::runtime_error("Failed to open '" + str2 + "'");
sdump_iopen = true;
tbuffer[0] = 'S';
tbuffer[1] = 'D';
tbuffer[2] = 'M';
tbuffer[3] = 'P';
uint32_t apufreq = SNES::system.apu_frequency();
uint32_t cpufreq = SNES::system.cpu_frequency();
tbuffer[4] = cpufreq >> 24;
tbuffer[5] = cpufreq >> 16;
tbuffer[6] = cpufreq >> 8;
tbuffer[7] = cpufreq;
tbuffer[8] = apufreq >> 24;
tbuffer[9] = apufreq >> 16;
tbuffer[10] = apufreq >> 8;
tbuffer[11] = apufreq;
out.write(reinterpret_cast<char*>(tbuffer), 12);
if(!out)
throw std::runtime_error("Failed to write header to '" + str2 + "'");
ssize = 12;
}
tbuffer[0] = flags;
for(unsigned i = 0; i < 512; i++) {
for(unsigned j = 0; j < 512; j++) {
tbuffer[4 * j + 1] = buffer[512 * i + j] >> 24;
tbuffer[4 * j + 2] = buffer[512 * i + j] >> 16;
tbuffer[4 * j + 3] = buffer[512 * i + j] >> 8;
tbuffer[4 * j + 4] = buffer[512 * i + j];
}
out.write(reinterpret_cast<char*>(tbuffer + (i ? 1 : 0)), i ? 2048 : 2049);
}
if(!out)
throw std::runtime_error("Failed to write frame");
ssize += 1048577;
}
void sdump_dumper::sample(short left, short right)
{
if(!sdump_iopen)
return;
unsigned char pkt[5];
pkt[0] = 16;
pkt[1] = static_cast<unsigned short>(left) >> 8;
pkt[2] = static_cast<unsigned short>(left);
pkt[3] = static_cast<unsigned short>(right) >> 8;
pkt[4] = static_cast<unsigned short>(right);
out.write(reinterpret_cast<char*>(pkt), 5);
if(!out)
throw std::runtime_error("Failed to write sample");
ssize += 5;
}
void sdump_dumper::end()
{
if(sdump_iopen)
out.close();
}

30
avidump/sdmp.hpp Normal file
View file

@ -0,0 +1,30 @@
#ifndef _sdmp__hpp__included__
#define _sdmp__hpp__included__
#include <string>
#include <fstream>
#include <cstdint>
#define SDUMP_FLAG_HIRES 1
#define SDUMP_FLAG_INTERLACED 2
#define SDUMP_FLAG_OVERSCAN 4
#define SDUMP_FLAG_PAL 8
class sdump_dumper
{
public:
sdump_dumper(const std::string& prefix, bool ssflag);
~sdump_dumper() throw();
void frame(const uint32_t* rawdata, unsigned flags);
void sample(short l, short r);
void end();
private:
std::string oprefix;
bool sdump_ss;
uint64_t ssize;
uint64_t next_seq;
bool sdump_iopen;
std::ofstream out;
};
#endif

View file

@ -1,57 +1,121 @@
#include "avsnoop.hpp"
#include "misc.hpp"
#include "globalwrap.hpp"
#include <sstream>
#include <iomanip>
#include <cmath>
gameinfo_struct::gameinfo_struct() throw(std::bad_alloc)
{
length = 0;
rerecords = "0";
}
std::string gameinfo_struct::get_readable_time(unsigned digits) const throw(std::bad_alloc)
{
double bias = 0.5 * pow(10, -static_cast<int>(digits));
double len = length + bias;
std::ostringstream str;
if(length >= 3600) {
double hours = floor(len / 3600);
str << hours << ":";
len -= hours * 3600;
}
double minutes = floor(len / 60);
len -= minutes * 60;
double seconds = floor(len);
len -= seconds;
str << std::setw(2) << std::setfill('0') << minutes << ":" << seconds;
if(digits > 0)
str << ".";
while(digits > 0) {
len = 10 * len;
str << '0' + static_cast<int>(len);
len -= floor(len);
digits--;
}
}
size_t gameinfo_struct::get_author_count() const throw()
{
return authors.size();
}
std::string gameinfo_struct::get_author_short(size_t idx) const throw(std::bad_alloc)
{
if(idx >= authors.size())
return "";
const std::pair<std::string, std::string>& x = authors[idx];
if(x.second != "")
return x.second;
else
return x.first;
}
uint64_t gameinfo_struct::get_rerecords() const throw()
{
uint64_t v = 0;
uint64_t max = 0xFFFFFFFFFFFFFFFFULL;
for(size_t i = 0; i < rerecords.length(); i++) {
if(v < max / 10)
//No risk of overflow.
v = v * 10 + static_cast<unsigned>(rerecords[i] - '0');
else if(v == max / 10) {
//THis may overflow.
v = v * 10;
if(v + static_cast<unsigned>(rerecords[i] - '0') < v)
return max;
v = v + static_cast<unsigned>(rerecords[i] - '0');
} else
//Definite overflow.
return max;
}
return v;
}
namespace
{
globalwrap<std::list<av_snooper*>> snoopers;
std::list<av_snooper::dump_notification*> notifiers;
std::string s_gamename;
std::string s_rerecords;
double s_gametime;
std::list<std::pair<std::string, std::string>> s_authors;
bool gameinfo_set;
uint32_t srate_n;
uint32_t srate_d;
globalwrap<std::list<av_snooper::dump_notification*>> notifiers;
uint32_t srate_n = 32000;
uint32_t srate_d = 1;
gameinfo_struct sgi;
globalwrap<std::set<std::string>> sactive_dumpers;
}
av_snooper::av_snooper() throw(std::bad_alloc)
av_snooper::av_snooper(const std::string& name) throw(std::bad_alloc)
{
snoopers().push_back(this);
for(auto i : notifiers)
i->dump_starting();
for(auto i : notifiers())
i->dump_starting(name);
sactive_dumpers().insert(s_name = name);
}
av_snooper::~av_snooper() throw()
{
sactive_dumpers().erase(s_name);
for(auto i = snoopers().begin(); i != snoopers().end(); i++)
if(*i == this) {
snoopers().erase(i);
break;
}
for(auto i : notifiers)
i->dump_ending();
for(auto i : notifiers())
i->dump_ending(s_name);
}
void av_snooper::set_sound_rate(uint32_t rate_n, uint32_t rate_d)
void av_snooper::frame(struct lcscreen& _frame, uint32_t fps_n, uint32_t fps_d, const uint32_t* raw, bool hires,
bool interlaced, bool overscan, unsigned region) throw(std::bad_alloc, std::runtime_error)
{
srate_n = rate_n;
srate_d = rate_d;
uint32_t g = gcd(srate_n, srate_d);
srate_n /= g;
srate_d /= g;
//Nothing.
}
std::pair<uint32_t, uint32_t> av_snooper::get_sound_rate()
{
return std::make_pair(srate_n, srate_d);
}
void av_snooper::frame(struct lcscreen& _frame, uint32_t fps_n, uint32_t fps_d, bool dummy) throw(std::bad_alloc)
void av_snooper::_frame(struct lcscreen& _frame, uint32_t fps_n, uint32_t fps_d, const uint32_t* raw, bool hires,
bool interlaced, bool overscan, unsigned region) throw(std::bad_alloc)
{
for(auto i : snoopers())
try {
i->frame(_frame, fps_n, fps_d);
i->frame(_frame, fps_n, fps_d, raw, hires, interlaced, overscan, region);
} catch(std::bad_alloc& e) {
throw;
} catch(std::exception& e) {
@ -62,7 +126,12 @@ void av_snooper::frame(struct lcscreen& _frame, uint32_t fps_n, uint32_t fps_d,
}
}
void av_snooper::sample(short l, short r, bool dummy) throw(std::bad_alloc)
void av_snooper::sample(short l, short r) throw(std::bad_alloc, std::runtime_error)
{
//Nothing.
}
void av_snooper::_sample(short l, short r) throw(std::bad_alloc)
{
for(auto i : snoopers())
try {
@ -77,7 +146,12 @@ void av_snooper::sample(short l, short r, bool dummy) throw(std::bad_alloc)
}
}
void av_snooper::end(bool dummy) throw(std::bad_alloc)
void av_snooper::end() throw(std::bad_alloc, std::runtime_error)
{
//Nothing.
}
void av_snooper::_end() throw(std::bad_alloc)
{
for(auto i : snoopers())
try {
@ -92,54 +166,90 @@ void av_snooper::end(bool dummy) throw(std::bad_alloc)
}
}
void av_snooper::gameinfo(const std::string& gamename, const std::list<std::pair<std::string, std::string>>&
authors, double gametime, const std::string& rerecords, bool dummy) throw(std::bad_alloc)
void av_snooper::sound_rate(uint32_t rate_n, uint32_t rate_d) throw(std::bad_alloc, std::runtime_error)
{
s_gamename = gamename;
s_authors = authors;
s_gametime = gametime;
s_rerecords = rerecords;
gameinfo_set = true;
for(auto i : snoopers())
i->send_gameinfo();
std::ostringstream str;
str << s_name << " dumper does not support variable samping rate.";
throw std::runtime_error(str.str());
}
void av_snooper::send_gameinfo() throw()
void av_snooper::_sound_rate(uint32_t rate_n, uint32_t rate_d)
{
if(gameinfo_set)
uint32_t g = gcd(rate_n, rate_d);
srate_n = rate_n / g;
srate_d = rate_d / g;
for(auto i : snoopers())
try {
gameinfo(s_gamename, s_authors, s_gametime, s_rerecords);
} catch(...) {
i->sound_rate(srate_n, srate_d);
} catch(std::bad_alloc& e) {
throw;
} catch(std::exception& e) {
try {
messages << "Error setting sound frequency: " << e.what() << std::endl;
} catch(...) {
}
}
}
std::pair<uint32_t, uint32_t> av_snooper::get_sound_rate() throw()
{
return std::make_pair(srate_n, srate_d);
}
void av_snooper::gameinfo(const struct gameinfo_struct& gi) throw(std::bad_alloc, std::runtime_error)
{
//Nothing.
}
void av_snooper::_gameinfo(const struct gameinfo_struct& gi) throw(std::bad_alloc)
{
sgi = gi;
for(auto i : snoopers())
try {
i->gameinfo(gi);
} catch(std::bad_alloc& e) {
throw;
} catch(std::exception& e) {
try {
messages << "Error sending game info: " << e.what() << std::endl;
} catch(...) {
}
}
}
const struct gameinfo_struct& av_snooper::get_gameinfo() throw(std::bad_alloc)
{
return sgi;
}
bool av_snooper::dump_in_progress() throw()
{
return !snoopers().empty();
}
av_snooper::dump_notification::dump_notification() throw(std::bad_alloc)
{
notifiers().push_back(this);
}
av_snooper::dump_notification::~dump_notification() throw()
{
}
void av_snooper::dump_notification::dump_starting() throw()
{
}
void av_snooper::dump_notification::dump_ending() throw()
{
}
void av_snooper::add_dump_notifier(av_snooper::dump_notification& notifier) throw(std::bad_alloc)
{
notifiers.push_back(&notifier);
}
void av_snooper::remove_dump_notifier(av_snooper::dump_notification& notifier) throw()
{
for(auto i = notifiers.begin(); i != notifiers.end(); i++)
if(*i == &notifier) {
notifiers.erase(i);
for(auto i = notifiers().begin(); i != notifiers().end(); i++)
if(*i == this) {
notifiers().erase(i);
return;
}
}
void av_snooper::dump_notification::dump_starting(const std::string& type) throw()
{
}
void av_snooper::dump_notification::dump_ending(const std::string& type) throw()
{
}
const std::set<std::string>& av_snooper::active_dumpers()
{
return sactive_dumpers();
}

View file

@ -4,8 +4,75 @@
#include "render.hpp"
#include <list>
#include <string>
#include <set>
#include <stdexcept>
/**
* Video data region is NTSC.
*/
#define SNOOP_REGION_NTSC 0
/**
* Video data region is PAL.
*/
#define SNOOP_REGION_PAL 1
/**
* Information about run.
*/
struct gameinfo_struct
{
public:
/**
* Construct game info.
*/
gameinfo_struct() throw(std::bad_alloc);
/**
* Game name.
*/
std::string gamename;
/**
* Run length in seconds.
*/
double length;
/**
* Rerecord count (base 10 ASCII)
*/
std::string rerecords;
/**
* Authors. The first components are real names, the second components are nicknames. Either (but not both) may be
* blank.
*/
std::vector<std::pair<std::string, std::string>> authors;
/**
* Format human-redable representation of the length.
*
* Parameter digits: Number of sub-second digits to use.
* Returns: The time formated.
* Throws std::bad_alloc: Not enough memory.
*/
std::string get_readable_time(unsigned digits) const throw(std::bad_alloc);
/**
* Get number of authors.
*
* Returns: Number of authors.
*/
size_t get_author_count() const throw();
/**
* Get short name of author (nickname if present, otherwise full name).
*
* Parameter idx: Index of author (0-based).
* Returns: The short name.
* Throws std::bad_alloc: Not enough memory.
*/
std::string get_author_short(size_t idx) const throw(std::bad_alloc);
/**
* Get rerecord count as a number. If rerecord count is too high, returns the maximum representatible count.
*
* Returns: The rerecord count.
*/
uint64_t get_rerecords() const throw();
};
/**
* A/V snooper. A/V snoopers allow code to snoop on audio samples and video frames, usually for purpose of dumping
* them to video file.
@ -14,39 +81,51 @@ class av_snooper
{
public:
/**
* Create new A/V snooper.
* Create new A/V snooper, registering it for receiving callbacks.
*
* throws std::bad_alloc: Not enough memory.
* Parameter name: Name of the dumper.
*
* Throws std::bad_alloc: Not enough memory.
*/
av_snooper() throw(std::bad_alloc);
av_snooper(const std::string& name) throw(std::bad_alloc);
/**
* Destroy A/V snooper. This will not call end method.
* Destroy A/V snooper, deregistering it. This will not call end() method.
*/
~av_snooper() throw();
/**
* Dump a frame.
*
* parameter _frame: The frame to dump.
* parameter fps_n: Current fps numerator.
* parameter fps_d: Current fps denomerator.
* Parameter _frame: The frame to dump.
* Parameter fps_n: Current fps numerator.
* Parameter fps_d: Current fps denomerator.
* Parameter raw: Raw frame data from bsnes.
* Parameter hires: True if bsnes signals hires mode, false otherwise.
* Parameter interlaced: True if bsnes signals interlaced mode, false otherwise.
* Parameter overscan: True if bsnes signals overscan mode, false otherwise.
* Parameter region: The region video data is for.
* throws std::bad_alloc: Not enough memory.
* throws std::runtime_error: Error dumping frame.
*/
virtual void frame(struct lcscreen& _frame, uint32_t fps_n, uint32_t fps_d) throw(std::bad_alloc,
std::runtime_error) = 0;
virtual void frame(struct lcscreen& _frame, uint32_t fps_n, uint32_t fps_d, const uint32_t* raw, bool hires,
bool interlaced, bool overscan, unsigned region) throw(std::bad_alloc, std::runtime_error);
/**
* Call frame() on all known A/V snoopers.
*
* parameter _frame: The frame to dump.
* parameter fps_n: Current fps numerator.
* parameter fps_d: Current fps denomerator.
* Parameter _frame: The frame to dump.
* Parameter fps_n: Current fps numerator.
* Parameter fps_d: Current fps denomerator.
* Parameter raw: Raw frame data from bsnes.
* Parameter hires: True if bsnes signals hires mode, false otherwise.
* Parameter interlaced: True if bsnes signals interlaced mode, false otherwise.
* Parameter overscan: True if bsnes signals overscan mode, false otherwise.
* Parameter region: The region video data is for.
* throws std::bad_alloc: Not enough memory.
*/
static void frame(struct lcscreen& _frame, uint32_t fps_n, uint32_t fps_d, bool dummy)
throw(std::bad_alloc);
static void _frame(struct lcscreen& _frame, uint32_t fps_n, uint32_t fps_d, const uint32_t* raw, bool hires,
bool interlaced, bool overscan, unsigned region) throw(std::bad_alloc);
/**
* Dump a sample.
@ -56,7 +135,7 @@ public:
* throws std::bad_alloc: Not enough memory.
* throws std::runtime_error: Error dumping sample.
*/
virtual void sample(short l, short r) throw(std::bad_alloc, std::runtime_error) = 0;
virtual void sample(short l, short r) throw(std::bad_alloc, std::runtime_error);
/**
* Call sample() on all known A/V snoopers.
@ -65,7 +144,7 @@ public:
* parameter r: Right channel sample.
* throws std::bad_alloc: Not enough memory.
*/
static void sample(short l, short r, bool dummy) throw(std::bad_alloc);
static void _sample(short l, short r) throw(std::bad_alloc);
/**
* End dump.
@ -73,18 +152,14 @@ public:
* throws std::bad_alloc: Not enough memory.
* throws std::runtime_error: Error dumping sample.
*/
virtual void end() throw(std::bad_alloc, std::runtime_error) = 0;
/**
* Set sound rate.
*/
virtual void end() throw(std::bad_alloc, std::runtime_error);
/**
* Call end() on all known A/V snoopers.
*
* throws std::bad_alloc: Not enough memory.
*/
static void end(bool dummy) throw(std::bad_alloc);
static void _end() throw(std::bad_alloc);
/**
* Set sound sampling rate.
@ -92,43 +167,46 @@ public:
* parameter rate_n: Numerator of sampling rate.
* parameter rate_d: Denomerator of sampling rate.
*/
static void set_sound_rate(uint32_t rate_n, uint32_t rate_d);
virtual void sound_rate(uint32_t rate_n, uint32_t rate_d) throw(std::bad_alloc, std::runtime_error);
/**
* Get sound sampling rate.
* Call set_sound_rate() on all known A/V snoopers (and record the rate).
*
* parameter rate_n: Numerator of sampling rate.
* parameter rate_d: Denomerator of sampling rate.
*/
std::pair<uint32_t, uint32_t> get_sound_rate();
static void _sound_rate(uint32_t rate_n, uint32_t rate_d);
/**
* Get the sound rate most recently set by _set_sound_rate().
*
* Returns: The first component is numerator of the sampling rate, the second component is the denomerator.
*/
std::pair<uint32_t, uint32_t> get_sound_rate() throw();
/**
* Notify game information.
*
* parameter gamename: Name of the game.
* parameter authors: Authors of the run.
* parameter gametime: Game time.
* parameter rercords: Rerecord count.
* parameter gi: The information. Not guaranteed to remain stable after the call.
* throws std::bad_alloc: Not enough memory.
* throws std::runtime_error: Error recording this info.
*/
virtual void gameinfo(const std::string& gamename, const std::list<std::pair<std::string, std::string>>&
authors, double gametime, const std::string& rerecords) throw(std::bad_alloc, std::runtime_error) = 0;
virtual void gameinfo(const struct gameinfo_struct& gi) throw(std::bad_alloc, std::runtime_error);
/**
* Call gameinfo() on all known A/V snoopers. Also records the gameinfo.
*
* parameter gamename: Name of the game.
* parameter authors: Authors of the run.
* parameter gametime: Game time.
* parameter rercords: Rerecord count.
* parameter gi: The information. Not guaranteed to remain stable after the call.
* throws std::bad_alloc Not enough memory.
*/
static void gameinfo(const std::string& gamename, const std::list<std::pair<std::string, std::string>>&
authors, double gametime, const std::string& rerecords, bool dummy) throw(std::bad_alloc);
static void _gameinfo(const struct gameinfo_struct& gi) throw(std::bad_alloc);
/**
* Send game info. If av_snooper::gameinfo() has been called, this causes gameinfo() method of this object to be
* called with previously recorded information.
* Get the last recorded game information.
*
* Returns: The last recorded gameinfo.
*/
void send_gameinfo() throw();
const struct gameinfo_struct& get_gameinfo() throw(std::bad_alloc);
/**
* Is there at least one known A/V snooper?
@ -136,7 +214,10 @@ public:
* returns: True if there is at least one known A/V snooper, false if there are none.
*/
static bool dump_in_progress() throw();
/**
* Get the names of the active dumpers.
*/
static const std::set<std::string>& active_dumpers();
/**
* Notifier for dumps starting/ending.
*/
@ -144,32 +225,30 @@ public:
{
public:
/**
* Destructor.
* Register dump notifier.
*
* Throws std::bad_alloc: Not enough memory.
*/
dump_notification() throw(std::bad_alloc);
/**
* Destructor. Deregister notifier.
*/
virtual ~dump_notification() throw();
/**
* Called on new dump starting.
*
* Parameter type: The type of new dumper.
*/
virtual void dump_starting() throw();
virtual void dump_starting(const std::string& type) throw();
/**
* Called on dump ending.
*
* Parameter type: The type of dumper going away.
*/
virtual void dump_ending() throw();
virtual void dump_ending(const std::string& type) throw();
};
/**
* Add a notifier for dumps starting/ending.
*
* parameter notifier: New notifier to add.
*/
static void add_dump_notifier(dump_notification& notifier) throw(std::bad_alloc);
/**
* Remove a notifier for dumps starting/ending.
*
* parameter notifier: Existing notifier to remove.
*/
static void remove_dump_notifier(dump_notification& notifier) throw();
private:
std::string s_name;
};
#endif

View file

@ -149,9 +149,14 @@ namespace
//Do button action.
void do_button_action(unsigned ui_id, unsigned button, short newstate, bool do_xor = false)
{
if(do_xor)
if(do_xor) {
do_button_action(ui_id, button, newstate, do_xor, autoheld_controls);
else
int x = controller_index_by_logical(ui_id);
enum devicetype_t p = controller_type_by_logical(ui_id);
int y = get_physcial_id_for_control(p, button);
if(x >= 0 && y >= 0)
window_callback::do_autohold_update(x, y, get_autohold(x, y));
} else
do_button_action(ui_id, button, newstate, do_xor, curcontrols);
}
@ -315,6 +320,7 @@ void controller_set_port_type(unsigned port, porttype_t ptype, bool set_core) th
snes_set_controller_port_device(port != 0, port_types[ptype].bsnes_type);
porttypes[port] = ptype;
update_analog_indices();
window_callback::do_autohold_reconfigure();
}
controls_t get_current_controls(uint64_t frame)
@ -357,3 +363,72 @@ void set_curcontrols_reset(int32_t delay)
}
}
void change_autohold(unsigned pid, unsigned idx, bool newstate)
{
if(pid >= MAX_PORTS * MAX_CONTROLLERS_PER_PORT || idx >= CONTROLLER_CONTROLS)
return;
autoheld_controls(pid / MAX_CONTROLLERS_PER_PORT, pid % MAX_CONTROLLERS_PER_PORT, idx) = (newstate ? 1 : 0);
window_callback::do_autohold_update(pid, idx, newstate);
update_movie_state();
window::notify_screen_update();
}
bool get_autohold(unsigned pid, unsigned idx)
{
if(pid >= MAX_PORTS * MAX_CONTROLLERS_PER_PORT || idx >= CONTROLLER_CONTROLS)
return false;
return (autoheld_controls(pid / MAX_CONTROLLERS_PER_PORT, pid % MAX_CONTROLLERS_PER_PORT, idx) != 0);
}
std::string get_button_name(unsigned lidx)
{
if(lidx < 16)
return buttonnames[lidx];
else
return "";
}
int get_physcial_id_for_control(devicetype_t dtype, unsigned lidx)
{
switch(dtype) {
case DT_NONE: return -1;
case DT_GAMEPAD:
switch(lidx) {
case BUTTON_UP: return SNES_DEVICE_ID_JOYPAD_UP;
case BUTTON_DOWN: return SNES_DEVICE_ID_JOYPAD_DOWN;
case BUTTON_LEFT: return SNES_DEVICE_ID_JOYPAD_LEFT;
case BUTTON_RIGHT: return SNES_DEVICE_ID_JOYPAD_RIGHT;
case BUTTON_A: return SNES_DEVICE_ID_JOYPAD_A;
case BUTTON_B: return SNES_DEVICE_ID_JOYPAD_B;
case BUTTON_X: return SNES_DEVICE_ID_JOYPAD_X;
case BUTTON_Y: return SNES_DEVICE_ID_JOYPAD_Y;
case BUTTON_L: return SNES_DEVICE_ID_JOYPAD_L;
case BUTTON_R: return SNES_DEVICE_ID_JOYPAD_R;
case BUTTON_SELECT: return SNES_DEVICE_ID_JOYPAD_SELECT;
case BUTTON_START: return SNES_DEVICE_ID_JOYPAD_START;
default: return -1;
};
case DT_MOUSE:
switch(lidx) {
case BUTTON_L: return SNES_DEVICE_ID_MOUSE_LEFT;
case BUTTON_R: return SNES_DEVICE_ID_MOUSE_RIGHT;
default: return -1;
};
case DT_SUPERSCOPE:
switch(lidx) {
case BUTTON_TRIGGER: return SNES_DEVICE_ID_SUPER_SCOPE_TRIGGER;
case BUTTON_CURSOR: return SNES_DEVICE_ID_SUPER_SCOPE_CURSOR;
case BUTTON_PAUSE: return SNES_DEVICE_ID_SUPER_SCOPE_PAUSE;
case BUTTON_TURBO: return SNES_DEVICE_ID_SUPER_SCOPE_TURBO;
default: return -1;
};
case DT_JUSTIFIER:
switch(lidx) {
case BUTTON_START: return SNES_DEVICE_ID_JUSTIFIER_START;
case BUTTON_TRIGGER: return SNES_DEVICE_ID_JUSTIFIER_TRIGGER;
default: return -1;
};
default: return -1;
}
}

View file

@ -64,4 +64,39 @@ void send_analog_input(int32_t x, int32_t y, unsigned index);
*/
void set_curcontrols_reset(int32_t delay);
/**
* Change the state of autohold.
*
* Parameter pid: The physical ID of controller.
* Parameter idx: The physical Index of the control.
* Parameter newstate: New state for autohold.
*/
void change_autohold(unsigned pid, unsigned idx, bool newstate);
/**
* Read the state of autohold.
*
* Parameter pid: The physical ID of controller.
* Parameter idx: The physical Index of the control.
* Returns: True if autohold is in progress, false otherwise.
*/
bool get_autohold(unsigned pid, unsigned idx);
/**
* Get the name of button.
*
* Parameter lidx: The logical index of the controller.
* Returns:
*/
std::string get_button_name(unsigned lidx);
/**
* Logical to physical control ID transition.
*
* Parameter dtype: The device type.
* Parameter lidx: The logical index.
* Returns: The physical index or -1 if the logical index is invalid for type.
*/
int get_physcial_id_for_control(devicetype_t dtype, unsigned lidx);
#endif

View file

@ -4,7 +4,6 @@
#include "controller.hpp"
#include "framebuffer.hpp"
#include "moviedata.hpp"
#include "sdump.hpp"
#include <iomanip>
#include "framerate.hpp"
#include "memorywatch.hpp"
@ -201,11 +200,11 @@ namespace
class dump_watch : public av_snooper::dump_notification
{
void dump_starting() throw()
void dump_starting(const std::string& n) throw()
{
update_movie_state();
}
void dump_ending() throw()
void dump_ending(const std::string& n) throw()
{
update_movie_state();
}
@ -361,12 +360,6 @@ class my_interface : public SNES::Interface
window::message("Got video refresh in runtosave, expect desyncs!");
video_refresh_done = true;
bool region = (SNES::system.region() == SNES::System::Region::PAL);
try {
sdump_frame(data, (hires ? SDUMP_FLAG_HIRES : 0) | (interlace ? SDUMP_FLAG_INTERLACED : 0) |
(overscan ? SDUMP_FLAG_OVERSCAN : 0) | (region ? SDUMP_FLAG_PAL : 0));
} catch(std::exception& e) {
messages << "Error dumping frame: " << e.what() << std::endl;
}
//std::cerr << "Frame: hires flag is " << (hires ? " " : "un") << "set." << std::endl;
//std::cerr << "Frame: interlace flag is " << (interlace ? " " : "un") << "set." << std::endl;
//std::cerr << "Frame: overscan flag is " << (overscan ? " " : "un") << "set." << std::endl;
@ -387,21 +380,16 @@ class my_interface : public SNES::Interface
uint32_t g = gcd(fps_n, fps_d);
fps_n /= g;
fps_d /= g;
av_snooper::frame(ls, fps_n, fps_d, true);
av_snooper::_frame(ls, fps_n, fps_d, data, hires, interlace, overscan, region ? SNOOP_REGION_PAL :
SNOOP_REGION_NTSC);
}
void audioSample(int16_t l_sample, int16_t r_sample)
{
try {
sdump_sample(l_sample, r_sample);
} catch(std::exception& e) {
messages << "Error dumping sample: " << e.what() << std::endl;
}
uint16_t _l = l_sample;
uint16_t _r = r_sample;
window::play_audio_sample(_l + 32768, _r + 32768);
av_snooper::sample(_l, _r, true);
av_snooper::_sample(l_sample, r_sample);
//The SMP emits a sample every 768 ticks of its clock. Use this in order to keep track of time.
our_movie.rtc_subsecond += 768;
while(our_movie.rtc_subsecond >= SNES::system.apu_frequency()) {
@ -869,7 +857,6 @@ void main_loop(struct loaded_rom& rom, struct moviefile& initial, bool load_has_
lua_callback_startup();
//print_controller_mappings();
av_snooper::add_dump_notifier(dumpwatch);
window::set_main_surface(main_screen);
redraw_framebuffer();
window::paused(false);
@ -925,6 +912,6 @@ void main_loop(struct loaded_rom& rom, struct moviefile& initial, bool load_has_
window::wait_usec(to_wait_frame(get_utime()));
first_round = false;
}
av_snooper::end(true);
av_snooper::_end();
SNES::interface = old_inteface;
}

View file

@ -515,7 +515,7 @@ void loaded_rom::load() throw(std::bad_alloc, std::runtime_error)
else
set_nominal_framerate(SNES::system.cpu_frequency() / DURATION_NTSC_FRAME);
window::set_sound_rate(SNES::system.apu_frequency(), 768);
av_snooper::set_sound_rate(SNES::system.apu_frequency(), 768);
av_snooper::_sound_rate(SNES::system.apu_frequency(), 768);
current_rom_type = rtype;
current_region = region;
refresh_cart_mappings();

View file

@ -1,175 +0,0 @@
#include "lsnes.hpp"
#include <snes/snes.hpp>
#include "sdump.hpp"
#include <fstream>
#include <stdexcept>
#include <iomanip>
#include "avsnoop.hpp"
#include "command.hpp"
#include "misc.hpp"
#define CUTOFF 2100000000
namespace
{
bool sdump_in_progress;
std::string oprefix;
std::ofstream out;
uint32_t ssize;
uint32_t next_seq;
bool sdump_iopen;
bool sdump_ss;
class dummy_avsnoop : public av_snooper
{
public:
dummy_avsnoop() throw(std::bad_alloc)
{
}
~dummy_avsnoop() throw()
{
}
void frame(struct lcscreen& _frame, uint32_t fps_n, uint32_t fps_d) throw(std::bad_alloc,
std::runtime_error)
{
}
void sample(short l, short r) throw(std::bad_alloc, std::runtime_error)
{
}
void end() throw(std::bad_alloc, std::runtime_error)
{
if(sdump_iopen)
out.close();
if(sdump_in_progress)
sdump_in_progress = false;
}
void gameinfo(const std::string& gamename, const std::list<std::pair<std::string, std::string>>&
authors, double gametime, const std::string& rerecords) throw(std::bad_alloc, std::runtime_error)
{
}
};
dummy_avsnoop* snooper;
function_ptr_command<const std::string&> jmd_dump("dump-sdmp", "Start sdmp capture",
"Syntax: dump-sdmp <prefix>\nStart SDMP capture to <prefix>\n",
[](const std::string& prefix) throw(std::bad_alloc, std::runtime_error) {
if(prefix == "")
throw std::runtime_error("Expected filename");
sdump_open(prefix, false);
messages << "Dumping to " << prefix << std::endl;
});
function_ptr_command<const std::string&> jmd_dumpss("dump-sdmpss", "Start SS sdmp capture",
"Syntax: dump-sdmpss <file>\nStart SS SDMP capture to <file>\n",
[](const std::string& prefix) throw(std::bad_alloc, std::runtime_error) {
if(prefix == "")
throw std::runtime_error("Expected filename");
sdump_open(prefix, true);
messages << "Dumping to " << prefix << std::endl;
});
function_ptr_command<> end_avi("end-sdmp", "End SDMP capture",
"Syntax: end-sdmp\nEnd a SDMP capture.\n",
[]() throw(std::bad_alloc, std::runtime_error) {
sdump_close();
messages << "Dump finished" << std::endl;
});
}
void sdump_open(const std::string& prefix, bool ss)
{
if(sdump_in_progress)
throw std::runtime_error("Dump already in progress");
oprefix = prefix;
snooper = new dummy_avsnoop;
sdump_ss = ss;
sdump_in_progress = true;
ssize = 0;
next_seq = 0;
sdump_iopen = false;
}
void sdump_close()
{
if(!sdump_in_progress)
throw std::runtime_error("No dump in progress");
if(sdump_iopen)
out.close();
sdump_in_progress = false;
delete snooper;
}
void sdump_frame(const uint32_t* buffer, unsigned flags)
{
flags &= 0xF;
unsigned char tbuffer[2049];
if(!sdump_in_progress)
return;
if(!sdump_iopen || (ssize > CUTOFF && !sdump_ss)) {
std::cerr << "Starting new segment" << std::endl;
if(sdump_iopen)
out.close();
std::ostringstream str;
if(sdump_ss)
str << oprefix;
else
str << oprefix << "_" << std::setw(4) << std::setfill('0') << (next_seq++) << ".sdmp";
std::string str2 = str.str();
out.open(str2.c_str(), std::ios::out | std::ios::binary);
if(!out)
throw std::runtime_error("Failed to open '" + str2 + "'");
sdump_iopen = true;
tbuffer[0] = 'S';
tbuffer[1] = 'D';
tbuffer[2] = 'M';
tbuffer[3] = 'P';
uint32_t apufreq = SNES::system.apu_frequency();
uint32_t cpufreq = SNES::system.cpu_frequency();
tbuffer[4] = cpufreq >> 24;
tbuffer[5] = cpufreq >> 16;
tbuffer[6] = cpufreq >> 8;
tbuffer[7] = cpufreq;
tbuffer[8] = apufreq >> 24;
tbuffer[9] = apufreq >> 16;
tbuffer[10] = apufreq >> 8;
tbuffer[11] = apufreq;
out.write(reinterpret_cast<char*>(tbuffer), 12);
if(!out)
throw std::runtime_error("Failed to write header to '" + str2 + "'");
ssize = 12;
}
tbuffer[0] = flags;
for(unsigned i = 0; i < 512; i++) {
for(unsigned j = 0; j < 512; j++) {
tbuffer[4 * j + 1] = buffer[512 * i + j] >> 24;
tbuffer[4 * j + 2] = buffer[512 * i + j] >> 16;
tbuffer[4 * j + 3] = buffer[512 * i + j] >> 8;
tbuffer[4 * j + 4] = buffer[512 * i + j];
}
out.write(reinterpret_cast<char*>(tbuffer + (i ? 1 : 0)), i ? 2048 : 2049);
}
if(!out)
throw std::runtime_error("Failed to write frame");
ssize += 1048577;
}
void sdump_sample(short left, short right)
{
if(!sdump_in_progress || !sdump_iopen)
return;
unsigned char pkt[5];
pkt[0] = 16;
pkt[1] = static_cast<unsigned short>(left) >> 8;
pkt[2] = static_cast<unsigned short>(left);
pkt[3] = static_cast<unsigned short>(right) >> 8;
pkt[4] = static_cast<unsigned short>(right);
out.write(reinterpret_cast<char*>(pkt), 5);
if(!out)
throw std::runtime_error("Failed to write sample");
ssize += 5;
}

View file

@ -1,17 +0,0 @@
#ifndef _sdump__hpp__included__
#define _sdump__hpp__included__
#include <string>
#include <cstdint>
#define SDUMP_FLAG_HIRES 1
#define SDUMP_FLAG_INTERLACED 2
#define SDUMP_FLAG_OVERSCAN 4
#define SDUMP_FLAG_PAL 8
void sdump_open(const std::string& prefix, bool ss);
void sdump_close();
void sdump_frame(const uint32_t* buffer, unsigned flags);
void sdump_sample(short left, short right);
#endif

View file

@ -295,6 +295,14 @@ void window_callback::on_mode_change(bool readonly) throw()
{
}
void window_callback::on_autohold_update(unsigned pid, unsigned ctrlnum, bool newstate)
{
}
void window_callback::on_autohold_reconfigure()
{
}
void window_callback::on_sound_change(const std::string& dev) throw()
{
}
@ -330,3 +338,15 @@ void window_callback::do_mode_change(bool readonly) throw()
for(auto i : wcbs())
i->on_mode_change(readonly);
}
void window_callback::do_autohold_update(unsigned pid, unsigned ctrlnum, bool newstate)
{
for(auto i : wcbs())
i->on_autohold_update(pid, ctrlnum, newstate);
}
void window_callback::do_autohold_reconfigure()
{
for(auto i : wcbs())
i->on_autohold_reconfigure();
}

View file

@ -49,6 +49,14 @@ public:
* Called when mode gets (possibly) changed.
*/
virtual void on_mode_change(bool readonly) throw();
/**
* Called when autohold (possibly) changes.
*/
virtual void on_autohold_update(unsigned pid, unsigned ctrlnum, bool newstate);
/**
* Called when controllers (possibly) change.
*/
virtual void on_autohold_reconfigure();
/**
* Do try to close the window.
*/
@ -69,6 +77,14 @@ public:
* Do on_mode_change
*/
static void do_mode_change(bool readonly) throw();
/**
* Do on_autohold_update
*/
static void do_autohold_update(unsigned pid, unsigned ctrlnum, bool newstate);
/**
* Do on_autohold_reconfigure
*/
static void do_autohold_reconfigure();
private:
window_callback(window_callback&);
window_callback& operator=(window_callback&);

View file

@ -21,6 +21,7 @@ namespace
{
public:
myavsnoop(uint64_t frames_to_dump)
: av_snooper("myavsnoop-monitor")
{
frames_dumped = 0;
total = frames_to_dump;
@ -30,8 +31,8 @@ namespace
{
}
void frame(struct lcscreen& _frame, uint32_t fps_n, uint32_t fps_d) throw(std::bad_alloc,
std::runtime_error)
void frame(struct lcscreen& _frame, uint32_t fps_n, uint32_t fps_d, const uint32_t* raw, bool hires,
bool interlaced, bool overscan, unsigned region) throw(std::bad_alloc, std::runtime_error)
{
frames_dumped++;
if(frames_dumped % 100 == 0) {
@ -40,25 +41,15 @@ namespace
}
if(frames_dumped == total) {
//Rough way to end it.
av_snooper::end(true);
av_snooper::_end();
exit(1);
}
}
void sample(short l, short r) throw(std::bad_alloc, std::runtime_error)
{
}
void end() throw(std::bad_alloc, std::runtime_error)
{
std::cout << "Finished!" << std::endl;
}
void gameinfo(const std::string& gamename, const std::list<std::pair<std::string, std::string>>&
authors, double gametime, const std::string& rerecords) throw(std::bad_alloc,
std::runtime_error)
{
}
private:
uint64_t frames_dumped;
uint64_t total;