Various improvements and cleanups to video dumping
This commit is contained in:
parent
f73a2debd3
commit
02ad621b2c
18 changed files with 741 additions and 378 deletions
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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.
|
||||
}
|
||||
|
|
|
@ -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
108
avidump/sdmp-control.cpp
Normal 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
99
avidump/sdmp.cpp
Normal 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
30
avidump/sdmp.hpp
Normal 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
|
|
@ -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(¬ifier);
|
||||
}
|
||||
|
||||
void av_snooper::remove_dump_notifier(av_snooper::dump_notification& notifier) throw()
|
||||
{
|
||||
for(auto i = notifiers.begin(); i != notifiers.end(); i++)
|
||||
if(*i == ¬ifier) {
|
||||
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();
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -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
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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&);
|
||||
|
|
|
@ -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;
|
||||
|
|
Loading…
Add table
Reference in a new issue