Merge branch 'rr1-maint'

Conflicts:
	src/video/sdmp.cpp
This commit is contained in:
Ilari Liusvaara 2012-04-07 15:43:35 +03:00
commit 906f3caa43
21 changed files with 1252 additions and 766 deletions

View file

@ -10,6 +10,13 @@
class adv_dumper
{
public:
/**
* Detail flags.
*/
static unsigned target_type_mask;
static unsigned target_type_file;
static unsigned target_type_prefix;
static unsigned target_type_special;
/**
* Register a dumper.
*
@ -42,11 +49,12 @@ public:
*/
virtual std::set<std::string> list_submodes() throw(std::bad_alloc) = 0;
/**
* Does this dumper want a prefix?
* Get mode details
*
* parameter mode: The submode.
* Returns: Mode details flags
*/
virtual bool wants_prefix(const std::string& mode) throw() = 0;
virtual unsigned mode_details(const std::string& mode) throw() = 0;
/**
* Get human-readable name for this dumper.
*

View file

@ -16,6 +16,11 @@ class wxwin_status;
class wxWindow;
class wxKeyEvent;
//Scaling
extern double horizontal_scale_factor;
extern double vertical_scale_factor;
extern int scaling_flags;
wxString towxstring(const std::string& str) throw(std::bad_alloc);
std::string tostdstring(const wxString& str) throw(std::bad_alloc);
void bring_app_foreground();
@ -28,13 +33,12 @@ void signal_resize_needed();
void _runuifun_async(void (*fn)(void*), void* arg);
//Editor dialogs.
void wxeditor_axes_display(wxWindow* parent);
void wxeditor_authors_display(wxWindow* parent);
void wxeditor_settings_display(wxWindow* parent);
void wxeditor_hotkeys_display(wxWindow* parent);
void wxeditor_paths_display(wxWindow* parent);
std::string wxeditor_keyselect(wxWindow* parent, bool clearable);
void wxeditor_screen_display(wxWindow* parent, double& horiz, double& vert, int& flags);
void wxeditor_screen_display(wxWindow* parent);
void wxsetingsdialog_display(wxWindow* parent);
//Auxillary windows.
void wxwindow_memorysearch_display();

View file

@ -69,6 +69,8 @@ struct avi_video_codec
* Parameter fps_n: fps numerator.
* Parameter fps_d: fps denominator.
* Returns: Stream format.
*
* Note: The next frame emitted MUST be a keyframe.
*/
virtual format reset(uint32_t width, uint32_t height, uint32_t fps_n, uint32_t fps_d) = 0;
/**

26
include/video/tcp.hpp Normal file
View file

@ -0,0 +1,26 @@
#ifndef _tcp__hpp__included__
#define _tcp__hpp__included__
#include <iostream>
#include <string>
#include <vector>
typedef void (*deleter_fn_t)(void*);
class socket_address
{
public:
socket_address(const std::string& spec);
socket_address next();
std::ostream& connect();
static deleter_fn_t deleter();
static bool supported();
private:
socket_address(int f, int st, int p);
int family;
int socktype;
int protocol;
std::vector<char> memory;
};
#endif

View file

@ -66,12 +66,22 @@ namespace
return;
}
for(auto j : b) {
if(i->wants_prefix(j))
unsigned d = i->mode_details(j);
if((d & adv_dumper::target_type_mask) ==
adv_dumper::target_type_prefix)
messages << "P " << x << "\t" << j << "\t"
<< i->modename(j) << std::endl;
else
else if((d & adv_dumper::target_type_mask) ==
adv_dumper::target_type_file)
messages << "F " << x << "\t" << j << "\t"
<< i->modename(j) << std::endl;
else if((d & adv_dumper::target_type_mask) ==
adv_dumper::target_type_special)
messages << "S " << x << "\t" << j << "\t"
<< i->modename(j) << std::endl;
else
messages << "U " << x << "\t" << j << "\t"
<< i->modename(j) << std::endl;
}
return;
}
@ -106,6 +116,11 @@ adv_dumper::adv_dumper(const std::string& id) throw(std::bad_alloc)
dumpers()[d_id] = this;
}
unsigned adv_dumper::target_type_mask = 3;
unsigned adv_dumper::target_type_file = 0;
unsigned adv_dumper::target_type_prefix = 1;
unsigned adv_dumper::target_type_special = 2;
template<bool X> void render_video_hud(struct screen<X>& target, struct lcscreen& source, uint32_t hscl, uint32_t vscl,
uint32_t roffset, uint32_t goffset, uint32_t boffset, uint32_t lgap, uint32_t tgap, uint32_t rgap,
uint32_t bgap, void(*fn)())

View file

@ -1,4 +1,4 @@
PLATFORMS=dummy evdev portaudio sdl wxwidgets
PLATFORMS=dummy evdev portaudio sdl wxwidgets win32mm
ALLOBJECT=__all__.$(OBJECT_SUFFIX)
ALLFLAGS=__all__.ldflags
PLATFORMS_OBJS=$(patsubst %,%/$(ALLOBJECT),$(PLATFORMS))
@ -14,6 +14,9 @@ dummy/$(ALLOBJECT): forcelook
evdev/$(ALLOBJECT): forcelook
$(MAKE) -C evdev
win32mm/$(ALLOBJECT): forcelook
$(MAKE) -C win32mm
portaudio/$(ALLOBJECT): forcelook
$(MAKE) -C portaudio
@ -29,6 +32,7 @@ wxwidgets/$(ALLOBJECT): forcelook
precheck:
$(MAKE) -C dummy precheck
$(MAKE) -C evdev precheck
$(MAKE) -C win32mm precheck
$(MAKE) -C portaudio precheck
$(MAKE) -C sdl precheck
$(MAKE) -C wxwidgets precheck
@ -37,6 +41,7 @@ clean:
rm -f *.$(OBJECT_SUFFIX) *.ldflags
$(MAKE) -C dummy clean
$(MAKE) -C evdev clean
$(MAKE) -C win32mm clean
$(MAKE) -C portaudio clean
$(MAKE) -C sdl clean
$(MAKE) -C wxwidgets clean

View file

@ -0,0 +1,20 @@
ifeq ($(JOYSTICK), WIN32MM)
OBJECTS=$(patsubst %.cpp,%.$(OBJECT_SUFFIX),$(wildcard *.cpp))
else
OBJECTS=dummy.$(OBJECT_SUFFIX)
endif
.PRECIOUS: %.$(OBJECT_SUFFIX)
__all__.$(OBJECT_SUFFIX): $(OBJECTS)
$(REALLD) -r -o $@ $^
touch __all__.ldflags
%.$(OBJECT_SUFFIX): %.cpp
$(REALCC) -c -o $@ $< -I../../../include $(CFLAGS)
precheck:
@true
clean:
rm -f *.$(OBJECT_SUFFIX) *.ldflags

View file

@ -0,0 +1 @@
char SYMBOL_5825392786327896327896798437692738969832786;

View file

@ -0,0 +1,216 @@
#include "core/command.hpp"
#include "core/framerate.hpp"
#include "core/keymapper.hpp"
#include "core/window.hpp"
#include "library/minmax.hpp"
#include "library/string.hpp"
#include <windows.h>
#include <mmsystem.h>
#include <regstr.h>
#include <cstdlib>
#include <string>
#include <sstream>
#include <iostream>
#include <stdexcept>
#include <sys/time.h>
#include <unistd.h>
namespace
{
std::set<keygroup*> keygroups;
std::map<std::pair<unsigned, unsigned>, keygroup*> buttons;
std::map<std::pair<unsigned, unsigned>, keygroup*> axes;
std::map<unsigned, keygroup*> hats;
std::map<std::pair<unsigned, unsigned>, short> lbuttons;
std::map<std::pair<unsigned, unsigned>, short> laxes;
std::map<unsigned, short> lhats;
std::set<unsigned> joysticks;
std::map<unsigned, JOYCAPS> capabilities;
volatile bool quit_signaled;
volatile bool quit_ack;
void create_hat(unsigned i)
{
std::string n = (stringfmt() << "joystick" << i << "hat").str();
keygroup* k = new keygroup(n, keygroup::KT_HAT);
hats[i] = k;
}
void create_button(unsigned i, unsigned j)
{
std::string n = (stringfmt() << "joystick" << i << "button" << j).str();
keygroup* k = new keygroup(n, keygroup::KT_KEY);
buttons[std::make_pair(i, j)] = k;
}
void create_axis(unsigned i, unsigned j, unsigned min, unsigned max)
{
std::string n = (stringfmt() << "joystick" << i << "axis" << j).str();
keygroup* k;
k = new keygroup(n, keygroup::KT_AXIS_PAIR);
axes[std::make_pair(i, j)] = k;
}
void read_axis(unsigned i, unsigned j, unsigned pos, unsigned pmin, unsigned pmax)
{
auto key = std::make_pair(i, j);
if(!axes.count(key))
return;
short cpos;
double _pos = pos;
double _pmin = pmin;
double _pmax = pmax;
_pos = 65535 * (_pos - _pmin) / (_pmax - _pmin) - 32768;
if(_pos < -32768)
cpos = -32768;
else if(_pos > 32767)
cpos = 32767;
else
cpos = _pos;
if(laxes[key] != cpos) {
platform::queue(keypress(modifier_set(), *axes[key], cpos));
laxes[key] = cpos;
}
}
void init_joysticks()
{
unsigned max_joysticks = joyGetNumDevs();
if(!max_joysticks)
return; //No joystick support.
for(unsigned i = 0; i < max_joysticks; i++) {
JOYINFOEX info;
JOYCAPS caps;
info.dwSize = sizeof(info);
info.dwFlags = JOY_RETURNALL;
if(joyGetPosEx(i, &info) != JOYERR_NOERROR)
continue; //Not usable.
if(joyGetDevCaps(i, &caps, sizeof(caps)) != JOYERR_NOERROR)
continue; //Not usable.
joysticks.insert(i);
capabilities[i] = caps;
messages << "Joystick #" << i << ": " << caps.szPname << " (by '" << caps.szOEMVxD << "')"
<< std::endl;
if(caps.wCaps & JOYCAPS_HASPOV)
create_hat(i);
for(unsigned j = 0; j < caps.wNumButtons && j < 32; j++)
create_button(i, j);
unsigned axcount = 2;
create_axis(i, 0, caps.wXmin, caps.wXmax);
create_axis(i, 1, caps.wYmin, caps.wYmax);
if(caps.wCaps & JOYCAPS_HASZ) {
create_axis(i, 2, caps.wZmin, caps.wZmax);
axcount++;
}
if(caps.wCaps & JOYCAPS_HASR) {
create_axis(i, 3, caps.wRmin, caps.wRmax);
axcount++;
}
if(caps.wCaps & JOYCAPS_HASU) {
create_axis(i, 4, caps.wUmin, caps.wUmax);
axcount++;
}
if(caps.wCaps & JOYCAPS_HASV) {
create_axis(i, 5, caps.wVmin, caps.wVmax);
axcount++;
}
if(caps.wCaps & JOYCAPS_HASPOV)
messages << "1 hat, ";
messages << axcount << " axes, " << min((int)caps.wNumButtons, 32) << " buttons" << std::endl;
}
}
void quit_joysticks()
{
for(auto i : keygroups)
delete i;
buttons.clear();
axes.clear();
hats.clear();
keygroups.clear();
joysticks.clear();
capabilities.clear();
}
void poll_joysticks()
{
modifier_set mod;
for(auto i : capabilities) {
unsigned jnum = i.first;
JOYINFOEX info;
JOYCAPS caps = capabilities[jnum];
info.dwSize = sizeof(info);
info.dwFlags = JOY_RETURNALL;
if(joyGetPosEx(jnum, &info) != JOYERR_NOERROR)
continue; //Not usable.
if(caps.wCaps & JOYCAPS_HASPOV) {
//Read POV hat.
short m = 0;
int pov = info.dwPOV;
if((pov >= 0 && pov <= 6000) || (pov >= 30000 && pov <= 36000))
m |= 1;
if(pov >= 3000 && pov <= 15000)
m |= 2;
if(pov >= 12000 && pov <= 24000)
m |= 4;
if(pov >= 21000 && pov <= 33000)
m |= 8;
if(lhats[jnum] != m) {
platform::queue(keypress(modifier_set(), *hats[jnum], m));
lhats[jnum] = m;
}
}
for(unsigned j = 0; j < caps.wMaxButtons; j++) {
//Read buttons
auto key = std::make_pair(jnum, j);
short x = (info.dwButtons >> j) & 1;
if(buttons.count(key) && lbuttons[key] != x) {
platform::queue(keypress(modifier_set(), *buttons[key], x));
lbuttons[key] = x;
}
}
read_axis(jnum, 0, info.dwXpos, caps.wXmin, caps.wXmax);
read_axis(jnum, 1, info.dwYpos, caps.wYmin, caps.wYmax);
if(caps.wCaps & JOYCAPS_HASZ)
read_axis(jnum, 2, info.dwZpos, caps.wZmin, caps.wZmax);
if(caps.wCaps & JOYCAPS_HASR)
read_axis(jnum, 3, info.dwRpos, caps.wRmin, caps.wRmax);
if(caps.wCaps & JOYCAPS_HASU)
read_axis(jnum, 4, info.dwUpos, caps.wUmin, caps.wUmax);
if(caps.wCaps & JOYCAPS_HASV)
read_axis(jnum, 5, info.dwVpos, caps.wVmin, caps.wVmax);
}
}
}
void joystick_plugin::init() throw()
{
init_joysticks();
quit_ack = quit_signaled = false;
}
void joystick_plugin::quit() throw()
{
quit_signaled = true;
while(!quit_ack);
quit_joysticks();
}
#define POLL_WAIT 20000
void joystick_plugin::thread_fn() throw()
{
while(!quit_signaled) {
poll_joysticks();
usleep(POLL_WAIT);
}
quit_ack = true;
}
void joystick_plugin::signal() throw()
{
quit_signaled = true;
}
const char* joystick_plugin::name = "Win32mm joystick plugin";

View file

@ -96,25 +96,58 @@ void dumper_menu::on_select(wxCommandEvent& e)
if(id < wxid_range_low || id > wxid_range_high)
return;
for(auto i : menustructure) {
std::string error_str;
adv_dumper* t = existing_dumpers[i.first].instance;
if(i.second.end_wxid == id) {
//Execute end of dump operation.
runemufn([t]() { t->end(); });
runemufn([t, &error_str]() {
try {
t->end();
} catch(std::exception& e) {
error_str = e.what();
}});
if(error_str != "")
wxMessageBox(towxstring(error_str), _T("Error ending dump"), wxICON_EXCLAMATION | wxOK,
pwin);
return;
}
if(i.second.start_wxids.count(id)) {
//Execute start of dump operation.
std::string mode = i.second.start_wxids[id];
bool prefixed = t->wants_prefix(mode);
unsigned d = t->mode_details(mode);
std::string prefix;
wxFileDialog* d = new wxFileDialog(pwin, towxstring(prefixed ? std::string("Choose prefix") :
std::string("Choose file")), wxT("."));
if((d & adv_dumper::target_type_mask) == adv_dumper::target_type_file) {
wxFileDialog* d = new wxFileDialog(pwin, wxT("Choose file"), wxT("."));
if(d->ShowModal() == wxID_OK)
prefix = tostdstring(d->GetPath());
d->Destroy();
} else if((d & adv_dumper::target_type_mask) == adv_dumper::target_type_prefix) {
wxFileDialog* d = new wxFileDialog(pwin, wxT("Choose prefix"), wxT("."));
if(d->ShowModal() == wxID_OK)
prefix = tostdstring(d->GetPath());
d->Destroy();
} else if((d & adv_dumper::target_type_mask) == adv_dumper::target_type_special) {
try {
prefix = pick_text(pwin, "Choose target", "Enter target to dump to", "");
} catch(...) {
return;
}
} else {
wxMessageBox(wxT("Unsupported target type"), _T("Dumper error"), wxICON_EXCLAMATION |
wxOK, pwin);
return;
}
if(prefix == "")
return;
runemufn([t, mode, prefix]() { t->start(mode, prefix); });
runemufn([t, mode, prefix, &error_str]() {
try {
t->start(mode, prefix);
} catch(std::exception& e) {
error_str = e.what();
}});
if(error_str != "")
wxMessageBox(towxstring(error_str), _T("Error starting dump"), wxICON_EXCLAMATION |
wxOK, pwin);
return;
}
}

View file

@ -1,292 +0,0 @@
#include "core/keymapper.hpp"
#include "platform/wxwidgets/platform.hpp"
#include <boost/lexical_cast.hpp>
#include <sstream>
#define AMODE_DISABLED "Disabled"
#define AMODE_AXIS_PAIR "Axis"
#define AMODE_AXIS_PAIR_INVERSE "Axis (inverted)"
#define AMODE_PRESSURE_M0 "Pressure - to 0"
#define AMODE_PRESSURE_MP "Pressure - to +"
#define AMODE_PRESSURE_0M "Pressure 0 to -"
#define AMODE_PRESSURE_0P "Pressure 0 to +"
#define AMODE_PRESSURE_PM "Pressure + to -"
#define AMODE_PRESSURE_P0 "Pressure + to 0"
#include <wx/wx.h>
#include <wx/event.h>
#include <wx/control.h>
#include <wx/combobox.h>
#include <vector>
#include <string>
class wxeditor_axes_axis
{
public:
wxeditor_axes_axis(wxSizer* sizer, wxWindow* window, const std::string& name);
bool is_ok();
void apply();
private:
std::string a_name;
wxComboBox* a_type;
wxTextCtrl* a_low;
wxTextCtrl* a_mid;
wxTextCtrl* a_high;
wxTextCtrl* a_tolerance;
};
class wxeditor_axes : public wxDialog
{
public:
wxeditor_axes(wxWindow* parent);
~wxeditor_axes();
bool ShouldPreventAppExit() const;
void on_value_change(wxCommandEvent& e);
void on_cancel(wxCommandEvent& e);
void on_ok(wxCommandEvent& e);
bool has_axes();
private:
std::vector<wxeditor_axes_axis*> axes;
wxButton* okbutton;
wxButton* cancel;
};
//Should be called in modal pause mode.
wxeditor_axes_axis::wxeditor_axes_axis(wxSizer* sizer, wxWindow* window, const std::string& name)
{
wxString choices[9];
choices[0] = wxT(AMODE_DISABLED);
choices[1] = wxT(AMODE_AXIS_PAIR);
choices[2] = wxT(AMODE_AXIS_PAIR_INVERSE);
choices[3] = wxT(AMODE_PRESSURE_M0);
choices[4] = wxT(AMODE_PRESSURE_MP);
choices[5] = wxT(AMODE_PRESSURE_0M);
choices[6] = wxT(AMODE_PRESSURE_0P);
choices[7] = wxT(AMODE_PRESSURE_PM);
choices[8] = wxT(AMODE_PRESSURE_P0);
size_t defaultidx = 0;
std::string low;
std::string mid;
std::string high;
std::string tolerance;
keygroup* k;
runemufn([&k, name]() { k = keygroup::lookup_by_name(name); });
if(!k) {
return;
}
struct keygroup::parameters p = k->get_parameters();
{
switch(p.ktype) {
case keygroup::KT_DISABLED: defaultidx = 0; break;
case keygroup::KT_AXIS_PAIR: defaultidx = 1; break;
case keygroup::KT_AXIS_PAIR_INVERSE: defaultidx = 2; break;
case keygroup::KT_PRESSURE_M0: defaultidx = 3; break;
case keygroup::KT_PRESSURE_MP: defaultidx = 4; break;
case keygroup::KT_PRESSURE_0M: defaultidx = 5; break;
case keygroup::KT_PRESSURE_0P: defaultidx = 6; break;
case keygroup::KT_PRESSURE_PM: defaultidx = 7; break;
case keygroup::KT_PRESSURE_P0: defaultidx = 8; break;
};
std::ostringstream x1;
std::ostringstream x2;
std::ostringstream x3;
std::ostringstream x4;
x1 << p.cal_left;
x2 << p.cal_center;
x3 << p.cal_right;
x4 << p.cal_tolerance;
low = x1.str();
mid = x2.str();
high = x3.str();
tolerance = x4.str();
}
a_name = name;
sizer->Add(new wxStaticText(window, wxID_ANY, towxstring(name)), 0, wxGROW);
sizer->Add(a_type = new wxComboBox(window, wxID_ANY, choices[defaultidx], wxDefaultPosition, wxDefaultSize,
9, choices, wxCB_READONLY), 0, wxGROW);
sizer->Add(a_low = new wxTextCtrl(window, wxID_ANY, towxstring(low)), 0, wxGROW);
sizer->Add(a_mid = new wxTextCtrl(window, wxID_ANY, towxstring(mid)), 0, wxGROW);
sizer->Add(a_high = new wxTextCtrl(window, wxID_ANY, towxstring(high)), 0, wxGROW);
sizer->Add(a_tolerance = new wxTextCtrl(window, wxID_ANY, towxstring(tolerance)), 0, wxGROW);
a_low->Connect(wxEVT_COMMAND_TEXT_UPDATED, wxCommandEventHandler(wxeditor_axes::on_value_change), NULL,
window);
a_mid->Connect(wxEVT_COMMAND_TEXT_UPDATED, wxCommandEventHandler(wxeditor_axes::on_value_change), NULL,
window);
a_high->Connect(wxEVT_COMMAND_TEXT_UPDATED, wxCommandEventHandler(wxeditor_axes::on_value_change), NULL,
window);
a_tolerance->Connect(wxEVT_COMMAND_TEXT_UPDATED, wxCommandEventHandler(wxeditor_axes::on_value_change), NULL,
window);
}
bool wxeditor_axes_axis::is_ok()
{
int32_t low, mid, high;
double tolerance;
try {
low = boost::lexical_cast<int32_t>(tostdstring(a_low->GetValue()));
mid = boost::lexical_cast<int32_t>(tostdstring(a_mid->GetValue()));
high = boost::lexical_cast<int32_t>(tostdstring(a_high->GetValue()));
tolerance = boost::lexical_cast<double>(tostdstring(a_tolerance->GetValue()));
} catch(...) {
return false;
}
if(low < -32768 || low > 32767 || low > mid)
return false;
if(mid < -32768 || mid > 32767 || mid > high)
return false;
if(high < -32768 || high > 32767)
return false;
if(tolerance <= 0 || tolerance >= 1)
return false;
return true;
}
//Should be called in modal pause mode.
void wxeditor_axes_axis::apply()
{
keygroup* k;
runemufn([&k, a_name]() { k = keygroup::lookup_by_name(a_name); });
if(!k)
return;
int32_t low, mid, high;
double tolerance;
enum keygroup::type ntype;
enum keygroup::type ctype;
runemufn([&ctype, k]() { ctype = k->get_parameters().ktype; });
std::string amode = tostdstring(a_type->GetValue());
if(amode == AMODE_AXIS_PAIR)
ntype = keygroup::KT_AXIS_PAIR;
if(amode == AMODE_AXIS_PAIR_INVERSE)
ntype = keygroup::KT_AXIS_PAIR_INVERSE;
if(amode == AMODE_DISABLED)
ntype = keygroup::KT_DISABLED;
if(amode == AMODE_PRESSURE_0M)
ntype = keygroup::KT_PRESSURE_0M;
if(amode == AMODE_PRESSURE_0P)
ntype = keygroup::KT_PRESSURE_0P;
if(amode == AMODE_PRESSURE_M0)
ntype = keygroup::KT_PRESSURE_M0;
if(amode == AMODE_PRESSURE_MP)
ntype = keygroup::KT_PRESSURE_MP;
if(amode == AMODE_PRESSURE_PM)
ntype = keygroup::KT_PRESSURE_PM;
if(amode == AMODE_PRESSURE_P0)
ntype = keygroup::KT_PRESSURE_P0;
try {
low = boost::lexical_cast<int32_t>(tostdstring(a_low->GetValue()));
mid = boost::lexical_cast<int32_t>(tostdstring(a_mid->GetValue()));
high = boost::lexical_cast<int32_t>(tostdstring(a_high->GetValue()));
tolerance = boost::lexical_cast<double>(tostdstring(a_tolerance->GetValue()));
} catch(...) {
return;
}
if(low < -32768 || low > 32767 || low > mid)
return;
if(mid < -32768 || mid > 32767 || mid > high)
return;
if(high < -32768 || high > 32767)
return;
if(tolerance <= 0 || tolerance >= 1)
return;
runemufn([k, ctype, ntype, low, mid, high, tolerance]() {
if(ctype != ntype)
k->change_type(ntype);
k->change_calibration(low, mid, high, tolerance);
});
}
wxeditor_axes::wxeditor_axes(wxWindow* parent)
: wxDialog(parent, wxID_ANY, wxT("lsnes: Edit axes"), wxDefaultPosition, wxSize(-1, -1))
{
std::set<std::string> axisnames;
runemufn([&axisnames]() { axisnames = keygroup::get_axis_set(); });
Centre();
wxFlexGridSizer* top_s = new wxFlexGridSizer(2, 1, 0, 0);
SetSizer(top_s);
wxFlexGridSizer* t_s = new wxFlexGridSizer(axisnames.size() + 1, 6, 0, 0);
t_s->Add(new wxStaticText(this, wxID_ANY, wxT("Name")), 0, wxGROW);
t_s->Add(new wxStaticText(this, wxID_ANY, wxT("Type")), 0, wxGROW);
t_s->Add(new wxStaticText(this, wxID_ANY, wxT("Low")), 0, wxGROW);
t_s->Add(new wxStaticText(this, wxID_ANY, wxT("Mid")), 0, wxGROW);
t_s->Add(new wxStaticText(this, wxID_ANY, wxT("High")), 0, wxGROW);
t_s->Add(new wxStaticText(this, wxID_ANY, wxT("Tolerance")), 0, wxGROW);
for(auto i : axisnames)
axes.push_back(new wxeditor_axes_axis(t_s, this, i));
top_s->Add(t_s);
wxBoxSizer* pbutton_s = new wxBoxSizer(wxHORIZONTAL);
pbutton_s->AddStretchSpacer();
pbutton_s->Add(okbutton = new wxButton(this, wxID_OK, wxT("OK")), 0, wxGROW);
pbutton_s->Add(cancel = new wxButton(this, wxID_CANCEL, wxT("Cancel")), 0, wxGROW);
okbutton->Connect(wxEVT_COMMAND_BUTTON_CLICKED,
wxCommandEventHandler(wxeditor_axes::on_ok), NULL, this);
cancel->Connect(wxEVT_COMMAND_BUTTON_CLICKED,
wxCommandEventHandler(wxeditor_axes::on_cancel), NULL, this);
top_s->Add(pbutton_s, 0, wxGROW);
t_s->SetSizeHints(this);
top_s->SetSizeHints(this);
Fit();
}
wxeditor_axes::~wxeditor_axes()
{
for(auto i : axes)
delete i;
}
bool wxeditor_axes::has_axes()
{
return (axes.size() != 0);
}
bool wxeditor_axes::ShouldPreventAppExit() const
{
return false;
}
void wxeditor_axes::on_value_change(wxCommandEvent& e)
{
bool all_ok = true;
for(auto i : axes)
all_ok = all_ok && i->is_ok();
okbutton->Enable(all_ok);
}
void wxeditor_axes::on_cancel(wxCommandEvent& e)
{
EndModal(wxID_CANCEL);
}
void wxeditor_axes::on_ok(wxCommandEvent& e)
{
for(auto i : axes)
i->apply();
EndModal(wxID_OK);
}
void wxeditor_axes_display(wxWindow* parent)
{
modal_pause_holder hld;
wxDialog* editor;
try {
editor = new wxeditor_axes(parent);
if(dynamic_cast<wxeditor_axes*>(editor)->has_axes())
editor->ShowModal();
else {
wxMessageBox(_T("You don't have joysticks to configure!"), _T("Warning"), wxICON_WARNING |
wxOK);
}
} catch(...) {
}
editor->Destroy();
}

View file

@ -1,112 +0,0 @@
#include "platform/wxwidgets/platform.hpp"
#include "core/settings.hpp"
#include "library/string.hpp"
#include <wx/wx.h>
#include <wx/event.h>
#include <wx/control.h>
#include <wx/combobox.h>
#include <vector>
#include <string>
#include <boost/lexical_cast.hpp>
#include <sstream>
#define FIRMWAREPATH "firmwarepath"
#define ROMPATH "rompath"
#define MOVIEPATH "moviepath"
class wxeditor_paths : public wxDialog
{
public:
wxeditor_paths(wxWindow* parent);
~wxeditor_paths();
bool ShouldPreventAppExit() const;
void on_cancel(wxCommandEvent& e);
void on_ok(wxCommandEvent& e);
private:
wxButton* okbutton;
wxButton* cancel;
wxTextCtrl* rompath;
wxTextCtrl* moviepath;
wxTextCtrl* firmwarepath;
};
wxeditor_paths::wxeditor_paths(wxWindow* parent)
: wxDialog(parent, wxID_ANY, wxT("lsnes: Paths"), wxDefaultPosition, wxSize(-1, -1))
{
std::string cur_rompath, cur_moviepath, cur_firmwarepath;
runemufn([&cur_firmwarepath, &cur_moviepath, &cur_rompath]() {
cur_firmwarepath = setting::get(FIRMWAREPATH);
cur_rompath = setting::get(ROMPATH);
cur_moviepath = setting::get(MOVIEPATH);
});
Centre();
wxFlexGridSizer* top_s = new wxFlexGridSizer(2, 1, 0, 0);
SetSizer(top_s);
wxFlexGridSizer* t_s = new wxFlexGridSizer(3, 2, 0, 0);
t_s->Add(new wxStaticText(this, wxID_ANY, wxT("ROMs:")), 0, wxGROW);
t_s->Add(rompath = new wxTextCtrl(this, wxID_ANY, towxstring(cur_rompath), wxDefaultPosition, wxSize(400, -1)),
1, wxGROW);
t_s->Add(new wxStaticText(this, wxID_ANY, wxT("Movies:")), 0, wxGROW);
t_s->Add(moviepath = new wxTextCtrl(this, wxID_ANY, towxstring(cur_moviepath), wxDefaultPosition,
wxSize(400, -1)), 1, wxGROW);
t_s->Add(new wxStaticText(this, wxID_ANY, wxT("Firmware:")), 0, wxGROW);
t_s->Add(firmwarepath = new wxTextCtrl(this, wxID_ANY, towxstring(cur_firmwarepath), wxDefaultPosition,
wxSize(400, -1)), 1, wxGROW);
top_s->Add(t_s);
wxBoxSizer* pbutton_s = new wxBoxSizer(wxHORIZONTAL);
pbutton_s->AddStretchSpacer();
pbutton_s->Add(okbutton = new wxButton(this, wxID_OK, wxT("OK")), 0, wxGROW);
pbutton_s->Add(cancel = new wxButton(this, wxID_CANCEL, wxT("Cancel")), 0, wxGROW);
okbutton->Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(wxeditor_paths::on_ok), NULL, this);
cancel->Connect(wxEVT_COMMAND_BUTTON_CLICKED,
wxCommandEventHandler(wxeditor_paths::on_cancel), NULL, this);
top_s->Add(pbutton_s, 0, wxGROW);
t_s->SetSizeHints(this);
top_s->SetSizeHints(this);
Fit();
}
wxeditor_paths::~wxeditor_paths()
{
}
bool wxeditor_paths::ShouldPreventAppExit() const
{
return false;
}
void wxeditor_paths::on_cancel(wxCommandEvent& e)
{
EndModal(wxID_CANCEL);
}
void wxeditor_paths::on_ok(wxCommandEvent& e)
{
std::string cur_rompath = tostdstring(rompath->GetValue());
std::string cur_moviepath = tostdstring(moviepath->GetValue());
std::string cur_firmwarepath = tostdstring(firmwarepath->GetValue());
runemufn([cur_firmwarepath, cur_moviepath, cur_rompath]() {
setting::set(ROMPATH, cur_rompath);
setting::set(MOVIEPATH, cur_moviepath);
setting::set(FIRMWAREPATH, cur_firmwarepath);
});
EndModal(wxID_OK);
}
void wxeditor_paths_display(wxWindow* parent)
{
modal_pause_holder hld;
wxDialog* editor;
try {
editor = new wxeditor_paths(parent);
editor->ShowModal();
} catch(...) {
}
editor->Destroy();
}

View file

@ -25,16 +25,13 @@ const char* algo_choices[] = {"Fast Bilinear", "Bilinear", "Bicubic", "Experimen
class wxeditor_screen : public wxDialog
{
public:
wxeditor_screen(wxWindow* parent, double& _horiz, double& _vert, int& _flags);
wxeditor_screen(wxWindow* parent);
~wxeditor_screen();
bool ShouldPreventAppExit() const;
void on_value_change(wxCommandEvent& e);
void on_cancel(wxCommandEvent& e);
void on_ok(wxCommandEvent& e);
private:
double& horiz;
double& vert;
int& flags;
wxButton* okbutton;
wxButton* cancel;
wxTextCtrl* horizbox;
@ -42,9 +39,8 @@ private:
wxComboBox* algo;
};
wxeditor_screen::wxeditor_screen(wxWindow* parent, double& _horiz, double& _vert, int& _flags)
: wxDialog(parent, wxID_ANY, wxT("lsnes: Screen scaling"), wxDefaultPosition, wxSize(-1, -1)),
horiz(_horiz), vert(_vert), flags(_flags)
wxeditor_screen::wxeditor_screen(wxWindow* parent)
: wxDialog(parent, wxID_ANY, wxT("lsnes: Screen scaling"), wxDefaultPosition, wxSize(-1, -1))
{
std::set<std::string> axisnames;
std::string h_x, v_x;
@ -54,11 +50,11 @@ wxeditor_screen::wxeditor_screen(wxWindow* parent, double& _horiz, double& _vert
for(size_t i = 0; i < sizeof(_algo_choices) / sizeof(_algo_choices[0]); i++)
_algo_choices[i] = towxstring(algo_choices[i]);
h_x = (stringfmt() << _horiz).str();
v_x = (stringfmt() << _vert).str();
h_x = (stringfmt() << horizontal_scale_factor).str();
v_x = (stringfmt() << vertical_scale_factor).str();
algoidx = 0;
for(size_t i = 0; i < sizeof(algo_choices) / sizeof(_algo_choices[0]); i++)
if(_flags & (1 << i)) {
if(scaling_flags & (1 << i)) {
algoidx = i;
break;
}
@ -164,20 +160,18 @@ void wxeditor_screen::on_ok(wxCommandEvent& e)
return;
}
horiz = hscale;
vert = vscale;
flags = newflags;
horizontal_scale_factor = hscale;
vertical_scale_factor = vscale;
scaling_flags = newflags;
EndModal(wxID_OK);
}
void wxeditor_screen_display(wxWindow* parent, double& horiz, double& vert, int& flags)
void wxeditor_screen_display(wxWindow* parent)
{
modal_pause_holder hld;
wxDialog* editor;
try {
editor = new wxeditor_screen(parent, horiz, vert, flags);
editor = new wxeditor_screen(parent);
editor->ShowModal();
} catch(...) {
}

View file

@ -64,7 +64,6 @@ enum
wxID_EDIT_AUTHORS,
wxID_AUTOHOLD_FIRST,
wxID_AUTOHOLD_LAST = wxID_AUTOHOLD_FIRST + 1023,
wxID_EDIT_AXES,
wxID_EDIT_SETTINGS,
wxID_EDIT_KEYBINDINGS,
wxID_EDIT_ALIAS,
@ -82,7 +81,6 @@ enum
wxID_SET_SPEED,
wxID_SET_VOLUME,
wxID_SET_SCREEN,
wxID_SET_PATHS,
wxID_SPEED_5,
wxID_SPEED_10,
wxID_SPEED_17,
@ -95,15 +93,17 @@ enum
wxID_SPEED_200,
wxID_SPEED_300,
wxID_SPEED_TURBO,
wxID_LOAD_LIBRARY
wxID_LOAD_LIBRARY,
wxID_SETTINGS,
};
double horizontal_scale_factor = 1.0;
double vertical_scale_factor = 1.0;
int scaling_flags = SWS_POINT;
namespace
{
double horizontal_multiplier = 1.0;
double vertical_multiplier = 1.0;
int libswscale_flags = SWS_POINT;
std::string last_volume = "0dB";
unsigned char* screen_buffer;
uint32_t old_width;
@ -615,18 +615,18 @@ void wxwin_mainwindow::panel::on_paint(wxPaintEvent& e)
uint8_t* dstp[1];
int dsts[1];
wxPaintDC dc(this);
uint32_t tw = main_screen.width * horizontal_multiplier + 0.5;
uint32_t th = main_screen.height * vertical_multiplier + 0.5;
if(!screen_buffer || tw != old_width || th != old_height || libswscale_flags != old_flags) {
uint32_t tw = main_screen.width * horizontal_scale_factor + 0.5;
uint32_t th = main_screen.height * vertical_scale_factor + 0.5;
if(!screen_buffer || tw != old_width || th != old_height || scaling_flags != old_flags) {
if(screen_buffer)
delete[] screen_buffer;
old_height = th;
old_width = tw;
old_flags = libswscale_flags;
old_flags = scaling_flags;
uint32_t w = main_screen.width;
uint32_t h = main_screen.height;
if(w && h)
ctx = sws_getCachedContext(ctx, w, h, PIX_FMT_RGBA, tw, th, PIX_FMT_BGR24, libswscale_flags,
ctx = sws_getCachedContext(ctx, w, h, PIX_FMT_RGBA, tw, th, PIX_FMT_BGR24, scaling_flags,
NULL, NULL, NULL);
tw = max(tw, static_cast<uint32_t>(128));
th = max(th, static_cast<uint32_t>(112));
@ -685,31 +685,12 @@ wxwin_mainwindow::wxwin_mainwindow()
SetMenuBar(menubar);
menu_start(wxT("lsnes"));
menu_entry_check(wxID_READONLY_MODE, wxT("Readonly mode"));
menu_check(wxID_READONLY_MODE, is_readonly_mode());
menu_entry(wxID_EDIT_AUTHORS, wxT("Edit game name && authors..."));
menu_entry_check(wxID_SHOW_STATUS, wxT("Show/Hide status panel"));
menu_check(wxID_SHOW_STATUS, true);
menu_start_sub(wxT("Speed"));
menu_entry(wxID_SPEED_5, wxT("1/20x"));
menu_entry(wxID_SPEED_10, wxT("1/10x"));
menu_entry(wxID_SPEED_17, wxT("1/6x"));
menu_entry(wxID_SPEED_20, wxT("1/5x"));
menu_entry(wxID_SPEED_25, wxT("1/4x"));
menu_entry(wxID_SPEED_33, wxT("1/3x"));
menu_entry(wxID_SPEED_50, wxT("1/2x"));
menu_entry(wxID_SPEED_100, wxT("1x"));
menu_entry(wxID_SPEED_150, wxT("1.5x"));
menu_entry(wxID_SPEED_200, wxT("2x"));
menu_entry(wxID_SPEED_300, wxT("3x"));
menu_entry(wxID_SPEED_TURBO, wxT("Turbo"));
menu_entry(wxID_SET_SPEED, wxT("Set..."));
menu_end_sub();
if(load_library_supported) {
menu_entry(wxID_LOAD_LIBRARY, towxstring(std::string("Load ") + library_is_called));
}
menu_special_sub(wxT("Dump video"), reinterpret_cast<dumper_menu*>(dmenu = new dumper_menu(this,
wxID_DUMP_FIRST, wxID_DUMP_LAST)));
menu_entry(wxID_SETTINGS, wxT("Configure emulator..."));
if(platform::sound_initialized()) {
menu_separator();
menu_entry_check(wxID_AUDIO_ENABLED, wxT("Sounds enabled"));
@ -749,7 +730,22 @@ wxwin_mainwindow::wxwin_mainwindow()
//Autohold menu: (ACOS)
menu_special(wxT("Autohold"), reinterpret_cast<autohold_menu*>(ahmenu = new autohold_menu(this)));
blistener->set_autohold_menu(reinterpret_cast<autohold_menu*>(ahmenu));
//Scripting menu: (ACOS)ERU
menu_start(wxT("Speed"));
menu_entry(wxID_SPEED_5, wxT("1/20x"));
menu_entry(wxID_SPEED_10, wxT("1/10x"));
menu_entry(wxID_SPEED_17, wxT("1/6x"));
menu_entry(wxID_SPEED_20, wxT("1/5x"));
menu_entry(wxID_SPEED_25, wxT("1/4x"));
menu_entry(wxID_SPEED_33, wxT("1/3x"));
menu_entry(wxID_SPEED_50, wxT("1/2x"));
menu_entry(wxID_SPEED_100, wxT("1x"));
menu_entry(wxID_SPEED_150, wxT("1.5x"));
menu_entry(wxID_SPEED_200, wxT("2x"));
menu_entry(wxID_SPEED_300, wxT("3x"));
menu_entry(wxID_SPEED_TURBO, wxT("Turbo"));
menu_entry(wxID_SET_SPEED, wxT("Set..."));
menu_start(wxT("Scripting"));
menu_entry(wxID_RUN_SCRIPT, wxT("Run script..."));
if(lua_supported) {
@ -764,16 +760,22 @@ wxwin_mainwindow::wxwin_mainwindow()
menu_entry(wxID_SAVE_MEMORYWATCH, wxT("Save memory watch..."));
menu_separator();
menu_entry(wxID_MEMORY_SEARCH, wxT("Memory Search..."));
//Settings menu: (ACFOS)
menu_start(wxT("Movie"));
menu_entry_check(wxID_READONLY_MODE, wxT("Readonly mode"));
menu_check(wxID_READONLY_MODE, is_readonly_mode());
menu_entry(wxID_EDIT_AUTHORS, wxT("Edit game name && authors..."));
menu_special(wxT("Capture"), reinterpret_cast<dumper_menu*>(dmenu = new dumper_menu(this,
wxID_DUMP_FIRST, wxID_DUMP_LAST)));
menu_start(wxT("Settings"));
menu_entry(wxID_EDIT_AXES, wxT("Configure axes..."));
menu_entry(wxID_EDIT_SETTINGS, wxT("Configure settings..."));
menu_entry(wxID_EDIT_KEYBINDINGS, wxT("Configure keybindings..."));
menu_entry(wxID_EDIT_ALIAS, wxT("Configure aliases..."));
menu_entry(wxID_EDIT_JUKEBOX, wxT("Configure jukebox..."));
menu_separator();
menu_entry(wxID_SET_SCREEN, wxT("Set screen scaling..."));
menu_entry(wxID_SET_PATHS, wxT("Set paths..."));
menu_entry(wxID_EDIT_HOTKEYS, wxT("Configure hotkeys..."));
}
@ -914,9 +916,6 @@ void wxwin_mainwindow::handle_menu_click_cancelable(wxCommandEvent& e)
update_movie_state();
});
return;
case wxID_EDIT_AXES:
wxeditor_axes_display(this);
return;
case wxID_EDIT_AUTHORS:
wxeditor_authors_display(this);
return;
@ -1103,6 +1102,7 @@ void wxwin_mainwindow::handle_menu_click_cancelable(wxCommandEvent& e)
runemufn([&bad, &value]() { try { setting::set("targetfps", value); } catch(...) { bad = true; } });
if(bad)
wxMessageBox(wxT("Invalid speed"), _T("Error"), wxICON_EXCLAMATION | wxOK, this);
return
}
case wxID_SET_VOLUME: {
std::string value;
@ -1122,12 +1122,10 @@ void wxwin_mainwindow::handle_menu_click_cancelable(wxCommandEvent& e)
}
last_volume = value;
runemufn([parsed]() { platform::global_volume = parsed; });
return;
}
case wxID_SET_SCREEN:
wxeditor_screen_display(this, horizontal_multiplier, vertical_multiplier, libswscale_flags);
return;
case wxID_SET_PATHS:
wxeditor_paths_display(this);
wxeditor_screen_display(this);
return;
case wxID_SPEED_5:
set_speed(5);
@ -1168,6 +1166,10 @@ void wxwin_mainwindow::handle_menu_click_cancelable(wxCommandEvent& e)
case wxID_LOAD_LIBRARY: {
std::string name = std::string("load ") + library_is_called;
load_library(pick_file(this, name, "."));
break;
}
case wxID_SETTINGS:
wxsetingsdialog_display(this);
break;
};
}

View file

@ -0,0 +1,452 @@
#include "platform/wxwidgets/platform.hpp"
#include "core/settings.hpp"
#include "library/string.hpp"
#include <wx/wx.h>
#include <wx/notebook.h>
#include <wx/event.h>
#include <wx/control.h>
#include <wx/combobox.h>
#include <vector>
#include <string>
#include <boost/lexical_cast.hpp>
#include <sstream>
extern "C"
{
#ifndef UINT64_C
#define UINT64_C(val) val##ULL
#endif
#include <libswscale/swscale.h>
}
#define AMODE_DISABLED "Disabled"
#define AMODE_AXIS_PAIR "Axis"
#define AMODE_AXIS_PAIR_INVERSE "Axis (inverted)"
#define AMODE_PRESSURE_M0 "Pressure - to 0"
#define AMODE_PRESSURE_MP "Pressure - to +"
#define AMODE_PRESSURE_0M "Pressure 0 to -"
#define AMODE_PRESSURE_0P "Pressure 0 to +"
#define AMODE_PRESSURE_PM "Pressure + to -"
#define AMODE_PRESSURE_P0 "Pressure + to 0"
#define FIRMWAREPATH "firmwarepath"
#define ROMPATH "rompath"
#define MOVIEPATH "moviepath"
const char* scalealgo_choices[] = {"Fast Bilinear", "Bilinear", "Bicubic", "Experimential", "Point", "Area",
"Bicubic-Linear", "Gauss", "Sinc", "Lanczos", "Spline"};
class wxeditor_esettings_joystick_aconfig : public wxDialog
{
public:
wxeditor_esettings_joystick_aconfig(wxWindow* parent, const std::string& _aname);
~wxeditor_esettings_joystick_aconfig();
void on_ok(wxCommandEvent& e);
void on_cancel(wxCommandEvent& e);
private:
std::string aname;
wxComboBox* type;
wxTextCtrl* low;
wxTextCtrl* mid;
wxTextCtrl* hi;
wxTextCtrl* tol;
wxButton* okbutton;
wxButton* cancel;
};
wxeditor_esettings_joystick_aconfig::wxeditor_esettings_joystick_aconfig(wxWindow* parent, const std::string& _aname)
: wxDialog(parent, -1, towxstring("Configure axis " + _aname))
{
wxString choices[9];
int didx = 1;
choices[0] = wxT(AMODE_DISABLED);
choices[1] = wxT(AMODE_AXIS_PAIR);
choices[2] = wxT(AMODE_AXIS_PAIR_INVERSE);
choices[3] = wxT(AMODE_PRESSURE_M0);
choices[4] = wxT(AMODE_PRESSURE_MP);
choices[5] = wxT(AMODE_PRESSURE_0M);
choices[6] = wxT(AMODE_PRESSURE_0P);
choices[7] = wxT(AMODE_PRESSURE_PM);
choices[8] = wxT(AMODE_PRESSURE_P0);
aname = _aname;
keygroup::parameters params;
runemufn([aname, &params]() {
auto k = keygroup::lookup_by_name(aname);
if(k)
params = k->get_parameters();
});
switch(params.ktype) {
case keygroup::KT_DISABLED: didx = 0; break;
case keygroup::KT_AXIS_PAIR: didx = 1; break;
case keygroup::KT_AXIS_PAIR_INVERSE: didx = 2; break;
case keygroup::KT_PRESSURE_M0: didx = 3; break;
case keygroup::KT_PRESSURE_MP: didx = 4; break;
case keygroup::KT_PRESSURE_0M: didx = 5; break;
case keygroup::KT_PRESSURE_0P: didx = 6; break;
case keygroup::KT_PRESSURE_PM: didx = 7; break;
case keygroup::KT_PRESSURE_P0: didx = 8; break;
};
Centre();
wxSizer* top_s = new wxBoxSizer(wxVERTICAL);
SetSizer(top_s);
wxFlexGridSizer* t_s = new wxFlexGridSizer(5, 2, 0, 0);
t_s->Add(new wxStaticText(this, -1, wxT("Type: ")), 0, wxGROW);
t_s->Add(type = new wxComboBox(this, wxID_ANY, choices[didx], wxDefaultPosition, wxDefaultSize,
9, choices, wxCB_READONLY), 1, wxGROW);
t_s->Add(new wxStaticText(this, -1, wxT("Low: ")), 0, wxGROW);
t_s->Add(low = new wxTextCtrl(this, -1, towxstring((stringfmt() << params.cal_left).str()), wxDefaultPosition,
wxSize(100, -1)), 1, wxGROW);
t_s->Add(new wxStaticText(this, -1, wxT("Middle: ")), 0, wxGROW);
t_s->Add(mid = new wxTextCtrl(this, -1, towxstring((stringfmt() << params.cal_center).str()),
wxDefaultPosition, wxSize(100, -1)), 1, wxGROW);
t_s->Add(new wxStaticText(this, -1, wxT("High: ")), 0, wxGROW);
t_s->Add(hi = new wxTextCtrl(this, -1, towxstring((stringfmt() << params.cal_right).str()),
wxDefaultPosition, wxSize(100, -1)), 1, wxGROW);
t_s->Add(new wxStaticText(this, -1, wxT("Tolerance: ")), 0, wxGROW);
t_s->Add(tol = new wxTextCtrl(this, -1, towxstring((stringfmt() << params.cal_tolerance).str()),
wxDefaultPosition, wxSize(100, -1)), 1, wxGROW);
top_s->Add(t_s);
wxBoxSizer* pbutton_s = new wxBoxSizer(wxHORIZONTAL);
pbutton_s->AddStretchSpacer();
pbutton_s->Add(okbutton = new wxButton(this, wxID_OK, wxT("OK")), 0, wxGROW);
pbutton_s->Add(cancel = new wxButton(this, wxID_CANCEL, wxT("Cancel")), 0, wxGROW);
okbutton->Connect(wxEVT_COMMAND_BUTTON_CLICKED,
wxCommandEventHandler(wxeditor_esettings_joystick_aconfig::on_ok), NULL, this);
cancel->Connect(wxEVT_COMMAND_BUTTON_CLICKED,
wxCommandEventHandler(wxeditor_esettings_joystick_aconfig::on_cancel), NULL, this);
top_s->Add(pbutton_s, 0, wxGROW);
t_s->SetSizeHints(this);
top_s->SetSizeHints(this);
Fit();
}
wxeditor_esettings_joystick_aconfig::~wxeditor_esettings_joystick_aconfig()
{
}
void wxeditor_esettings_joystick_aconfig::on_ok(wxCommandEvent& e)
{
std::string _type = tostdstring(type->GetValue());
std::string _low = tostdstring(low->GetValue());
std::string _mid = tostdstring(mid->GetValue());
std::string _hi = tostdstring(hi->GetValue());
std::string _tol = tostdstring(tol->GetValue());
enum keygroup::type _ctype = keygroup::KT_DISABLED;
enum keygroup::type _ntype = keygroup::KT_AXIS_PAIR;
int32_t nlow, nmid, nhi;
double ntol;
keygroup* k;
runemufn([&k, aname, &_ctype]() {
k = keygroup::lookup_by_name(aname);
if(k)
_ctype = k->get_parameters().ktype;
});
if(!k) {
//Axis gone away?
EndModal(wxID_OK);
return;
}
const char* bad_what = NULL;
try {
bad_what = "Bad axis type";
if(_type == AMODE_AXIS_PAIR) _ntype = keygroup::KT_AXIS_PAIR;
else if(_type == AMODE_AXIS_PAIR_INVERSE) _ntype = keygroup::KT_AXIS_PAIR_INVERSE;
else if(_type == AMODE_DISABLED) _ntype = keygroup::KT_DISABLED;
else if(_type == AMODE_PRESSURE_0M) _ntype = keygroup::KT_PRESSURE_0M;
else if(_type == AMODE_PRESSURE_0P) _ntype = keygroup::KT_PRESSURE_0P;
else if(_type == AMODE_PRESSURE_M0) _ntype = keygroup::KT_PRESSURE_M0;
else if(_type == AMODE_PRESSURE_MP) _ntype = keygroup::KT_PRESSURE_MP;
else if(_type == AMODE_PRESSURE_P0) _ntype = keygroup::KT_PRESSURE_P0;
else if(_type == AMODE_PRESSURE_PM) _ntype = keygroup::KT_PRESSURE_PM;
else
throw 42;
bad_what = "Bad low calibration value (range is -32768 - 32767)";
nlow = boost::lexical_cast<int32_t>(_low);
if(nlow < -32768 || nlow > 32767)
throw 42;
bad_what = "Bad middle calibration value (range is -32768 - 32767)";
nmid = boost::lexical_cast<int32_t>(_mid);
if(nmid < -32768 || nmid > 32767)
throw 42;
bad_what = "Bad high calibration value (range is -32768 - 32767)";
nhi = boost::lexical_cast<int32_t>(_hi);
if(nhi < -32768 || nhi > 32767)
throw 42;
bad_what = "Bad tolerance (range is 0 - 1)";
ntol = boost::lexical_cast<double>(_tol);
if(ntol <= 0 || ntol >= 1)
throw 42;
} catch(...) {
wxMessageBox(towxstring(bad_what), _T("Error"), wxICON_EXCLAMATION | wxOK);
return;
}
runemufn([&k, _ctype, _ntype, nlow, nmid, nhi, ntol]() {
if(_ctype != _ntype)
k->change_type(_ntype);
k->change_calibration(nlow, nmid, nhi, ntol);
});
EndModal(wxID_OK);
}
void wxeditor_esettings_joystick_aconfig::on_cancel(wxCommandEvent& e)
{
EndModal(wxID_CANCEL);
}
class wxeditor_esettings_joystick : public wxPanel
{
public:
wxeditor_esettings_joystick(wxWindow* parent);
~wxeditor_esettings_joystick();
void on_configure(wxCommandEvent& e);
private:
void refresh();
wxSizer* jgrid;
std::map<std::string, wxButton*> buttons;
std::map<int, std::string> ids;
int last_id;
};
namespace
{
std::string formattype(keygroup::type t)
{
if(t == keygroup::KT_AXIS_PAIR) return AMODE_AXIS_PAIR;
else if(t == keygroup::KT_AXIS_PAIR_INVERSE) return AMODE_AXIS_PAIR_INVERSE;
else if(t == keygroup::KT_PRESSURE_0M) return AMODE_PRESSURE_0M;
else if(t == keygroup::KT_PRESSURE_0P) return AMODE_PRESSURE_0P;
else if(t == keygroup::KT_PRESSURE_M0) return AMODE_PRESSURE_M0;
else if(t == keygroup::KT_PRESSURE_MP) return AMODE_PRESSURE_MP;
else if(t == keygroup::KT_PRESSURE_P0) return AMODE_PRESSURE_P0;
else if(t == keygroup::KT_PRESSURE_PM) return AMODE_PRESSURE_PM;
else return "Unknown";
}
std::string formatsettings(const std::string& name, const keygroup::parameters& s)
{
return (stringfmt() << name << ": " << formattype(s.ktype) << " low:" << s.cal_left << " mid:"
<< s.cal_center << " high:" << s.cal_right << " tolerance:" << s.cal_tolerance).str();
}
}
wxeditor_esettings_joystick::wxeditor_esettings_joystick(wxWindow* parent)
: wxPanel(parent, -1)
{
last_id = wxID_HIGHEST + 1;
SetSizer(jgrid = new wxBoxSizer(wxVERTICAL));
refresh();
jgrid->SetSizeHints(this);
Fit();
}
wxeditor_esettings_joystick::~wxeditor_esettings_joystick()
{
}
void wxeditor_esettings_joystick::on_configure(wxCommandEvent& e)
{
if(!ids.count(e.GetId()))
return;
wxDialog* d = new wxeditor_esettings_joystick_aconfig(this, ids[e.GetId()]);
d->ShowModal();
d->Destroy();
refresh();
}
void wxeditor_esettings_joystick::refresh()
{
//Collect the new settings.
std::map<std::string, keygroup::parameters> x;
runemufn([&x]() {
auto axisnames = keygroup::get_axis_set();
for(auto i : axisnames) {
keygroup* k = keygroup::lookup_by_name(i);
if(k)
x[i] = k->get_parameters();
}
});
for(auto i : x) {
if(buttons.count(i.first)) {
//Okay, this already exists. Update.
buttons[i.first]->SetLabel(towxstring(formatsettings(i.first, i.second)));
if(!buttons[i.first]->IsShown()) {
jgrid->Add(buttons[i.first], 1, wxGROW);
buttons[i.first]->Show();
}
} else {
//New button.
ids[last_id] = i.first;
buttons[i.first] = new wxButton(this, last_id++, towxstring(formatsettings(i.first,
i.second)));
buttons[i.first]->Connect(wxEVT_COMMAND_BUTTON_CLICKED,
wxCommandEventHandler(wxeditor_esettings_joystick::on_configure), NULL, this);
jgrid->Add(buttons[i.first], 1, wxGROW);
}
}
for(auto i : buttons) {
if(!x.count(i.first)) {
//Removed button.
i.second->Hide();
jgrid->Detach(i.second);
}
}
jgrid->Layout();
Fit();
}
class wxeditor_esettings_paths : public wxPanel
{
public:
wxeditor_esettings_paths(wxWindow* parent);
~wxeditor_esettings_paths();
void on_configure(wxCommandEvent& e);
private:
void refresh();
wxStaticText* rompath;
wxStaticText* firmpath;
wxStaticText* savepath;
wxFlexGridSizer* top_s;
};
wxeditor_esettings_paths::wxeditor_esettings_paths(wxWindow* parent)
: wxPanel(parent, -1)
{
wxButton* tmp;
top_s = new wxFlexGridSizer(3, 3, 0, 0);
SetSizer(top_s);
top_s->Add(new wxStaticText(this, -1, wxT("ROM path")), 0, wxGROW);
top_s->Add(rompath = new wxStaticText(this, -1, wxT("")), 1, wxGROW);
top_s->Add(tmp = new wxButton(this, wxID_HIGHEST + 1, wxT("Change...")), 0, wxGROW);
tmp->Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(wxeditor_esettings_paths::on_configure), NULL,
this);
top_s->Add(new wxStaticText(this, -1, wxT("Firmware path")), 0, wxGROW);
top_s->Add(firmpath = new wxStaticText(this, -1, wxT("")), 1, wxGROW);
top_s->Add(tmp = new wxButton(this, wxID_HIGHEST + 2, wxT("Change...")), 0, wxGROW);
tmp->Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(wxeditor_esettings_paths::on_configure), NULL,
this);
top_s->Add(new wxStaticText(this, -1, wxT("Save path")), 0, wxGROW);
top_s->Add(savepath = new wxStaticText(this, -1, wxT("")), 1, wxGROW);
top_s->Add(tmp = new wxButton(this, wxID_HIGHEST + 3, wxT("Change...")), 0, wxGROW);
tmp->Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(wxeditor_esettings_paths::on_configure), NULL,
this);
refresh();
top_s->SetSizeHints(this);
Fit();
}
wxeditor_esettings_paths::~wxeditor_esettings_paths()
{
}
void wxeditor_esettings_paths::on_configure(wxCommandEvent& e)
{
std::string name;
if(e.GetId() == wxID_HIGHEST + 1)
name = ROMPATH;
else if(e.GetId() == wxID_HIGHEST + 2)
name = FIRMWAREPATH;
else if(e.GetId() == wxID_HIGHEST + 3)
name = MOVIEPATH;
else
return;
std::string val;
runemufn([&val, name]() { val = setting::get(name); });
try {
val = pick_text(this, "Change path to", "Enter new path:", val);
} catch(...) {
refresh();
return;
}
runemufn([val, name]() { setting::set(name, val); });
refresh();
}
void wxeditor_esettings_paths::refresh()
{
std::string rpath, fpath, spath;
runemufn([&rpath, &fpath, &spath]() {
fpath = setting::get(FIRMWAREPATH);
rpath = setting::get(ROMPATH);
spath = setting::get(MOVIEPATH);
});
rompath->SetLabel(towxstring(rpath));
firmpath->SetLabel(towxstring(fpath));
savepath->SetLabel(towxstring(spath));
top_s->Layout();
Fit();
}
class wxeditor_esettings : public wxDialog
{
public:
wxeditor_esettings(wxWindow* parent);
~wxeditor_esettings();
bool ShouldPreventAppExit() const;
void on_close(wxCommandEvent& e);
private:
wxWindow* joystick_window;
wxNotebook* tabset;
wxButton* closebutton;
};
wxeditor_esettings::wxeditor_esettings(wxWindow* parent)
: wxDialog(parent, wxID_ANY, wxT("lsnes: Configure emulator"), wxDefaultPosition, wxSize(-1, -1))
{
Centre();
wxSizer* top_s = new wxBoxSizer(wxVERTICAL);
SetSizer(top_s);
tabset = new wxNotebook(this, -1, wxDefaultPosition, wxDefaultSize, wxNB_TOP);
tabset->AddPage(new wxeditor_esettings_joystick(tabset), wxT("Joysticks"));
tabset->AddPage(new wxeditor_esettings_paths(tabset), wxT("Paths"));
top_s->Add(tabset, 1, wxGROW);
wxBoxSizer* pbutton_s = new wxBoxSizer(wxHORIZONTAL);
pbutton_s->AddStretchSpacer();
pbutton_s->Add(closebutton = new wxButton(this, wxID_ANY, wxT("Close")), 0, wxGROW);
closebutton->Connect(wxEVT_COMMAND_BUTTON_CLICKED,
wxCommandEventHandler(wxeditor_esettings::on_close), NULL, this);
top_s->Add(pbutton_s, 0, wxGROW);
top_s->SetSizeHints(this);
Fit();
}
wxeditor_esettings::~wxeditor_esettings()
{
}
bool wxeditor_esettings::ShouldPreventAppExit() const
{
return false;
}
void wxeditor_esettings::on_close(wxCommandEvent& e)
{
EndModal(wxID_OK);
}
void wxsetingsdialog_display(wxWindow* parent)
{
modal_pause_holder hld;
wxDialog* editor;
try {
editor = new wxeditor_esettings(parent);
editor->ShowModal();
} catch(...) {
}
editor->Destroy();
}

View file

@ -102,6 +102,20 @@ namespace
return *_dumper;
}
std::string format_details(unsigned detail)
{
std::string r;
if((detail & adv_dumper::target_type_mask) == adv_dumper::target_type_file)
r = r + "TARGET_FILE";
else if((detail & adv_dumper::target_type_mask) == adv_dumper::target_type_prefix)
r = r + "TARGET_PREFIX";
else if((detail & adv_dumper::target_type_mask) == adv_dumper::target_type_special)
r = r + "TARGET_SPECIAL";
else
r = r + "TARGET_UNKNOWN";
return r;
}
adv_dumper& get_dumper(const std::vector<std::string>& cmdline, std::string& mode, std::string& prefix,
uint64_t& length)
{
@ -171,18 +185,17 @@ namespace
adv_dumper& _dumper = locate_dumper(dumper);
std::set<std::string> modes = _dumper.list_submodes();
if(modes.empty()) {
if(_dumper.wants_prefix(""))
std::cout << "No modes available for " << dumper << " (multi)" << std::endl;
else
std::cout << "No modes available for " << dumper << " (single)" << std::endl;
unsigned d = _dumper.mode_details("");
std::cout << "No modes available for " << dumper << " (" << format_details(d) << ")"
<< std::endl;
exit(0);
}
std::cout << "Modes available for " << dumper << ":" << std::endl;
for(auto i : modes)
if(_dumper.wants_prefix(i))
std::cout << i << "\tmulti\t" << _dumper.modename(i) << std::endl;
else
std::cout << i << "\tsingle\t" << _dumper.modename(i) << std::endl;
for(auto i : modes) {
unsigned d = _dumper.mode_details(i);
std::cout << i << "\t" << _dumper.modename(i) << "\t(" << format_details(d) << ")"
<< std::endl;
}
exit(0);
}
adv_dumper& _dumper = locate_dumper(dumper);

View file

@ -280,9 +280,9 @@ namespace
return x;
}
bool wants_prefix(const std::string& mode) throw()
unsigned mode_details(const std::string& mode) throw()
{
return true;
return target_type_prefix;
}
std::string name() throw(std::bad_alloc)

View file

@ -2,6 +2,7 @@
#include "core/dispatch.hpp"
#include "core/settings.hpp"
#include "library/serialization.hpp"
#include "video/tcp.hpp"
#include <iomanip>
#include <cassert>
@ -18,16 +19,27 @@ namespace
{
numeric_setting clevel("jmd-compression", 0, 9, 7);
void deleter_fn(void* f)
{
delete reinterpret_cast<std::ofstream*>(f);
}
class jmd_avsnoop : public information_dispatch
{
public:
jmd_avsnoop(const std::string& filename) throw(std::bad_alloc)
jmd_avsnoop(const std::string& filename, bool tcp_flag) throw(std::bad_alloc)
: information_dispatch("dump-jmd")
{
enable_send_sound();
complevel = clevel;
jmd.open(filename.c_str(), std::ios::out | std::ios::binary);
if(!jmd)
if(tcp_flag) {
jmd = &(socket_address(filename).connect());
deleter = socket_address::deleter();
} else {
jmd = new std::ofstream(filename.c_str(), std::ios::out | std::ios::binary);
deleter = deleter_fn;
}
if(!*jmd)
throw std::runtime_error("Can't open output JMD file.");
last_written_ts = 0;
//Write the segment tables.
@ -50,8 +62,8 @@ namespace
/* Dummy channel header. */
0x00, 0x03, 0x00, 0x03, 0x00, 0x02, 'd', 'u'
};
jmd.write(header, sizeof(header));
if(!jmd)
jmd->write(header, sizeof(header));
if(!*jmd)
throw std::runtime_error("Can't write JMD header and segment table");
have_dumped_frame = false;
audio_w = 0;
@ -103,18 +115,22 @@ namespace
void on_dump_end()
{
if(!jmd)
return;
flush_buffers(true);
if(last_written_ts > maxtc) {
jmd.close();
deleter(jmd);
jmd = NULL;
return;
}
char dummypacket[8] = {0x00, 0x03};
write32ube(dummypacket + 2, maxtc - last_written_ts);
last_written_ts = maxtc;
jmd.write(dummypacket, sizeof(dummypacket));
if(!jmd)
jmd->write(dummypacket, sizeof(dummypacket));
if(!*jmd)
throw std::runtime_error("Can't write JMD ending dummy packet");
jmd.close();
deleter(jmd);
jmd = NULL;
}
void on_gameinfo(const struct gameinfo_struct& gi)
@ -278,12 +294,12 @@ namespace
videopacketh[7 + lneed++] = 0x80 | ((datasize >> shift) & 0x7F);
videopacketh[7 + lneed++] = (datasize & 0x7F);
jmd.write(videopacketh, 7 + lneed);
if(!jmd)
jmd->write(videopacketh, 7 + lneed);
if(!*jmd)
throw std::runtime_error("Can't write JMD video packet header");
if(datasize > 0)
jmd.write(&f.data[0], datasize);
if(!jmd)
jmd->write(&f.data[0], datasize);
if(!*jmd)
throw std::runtime_error("Can't write JMD video packet body");
}
@ -295,12 +311,13 @@ namespace
last_written_ts = s.ts;
write16sbe(soundpacket + 8, s.l);
write16sbe(soundpacket + 10, s.r);
jmd.write(soundpacket, sizeof(soundpacket));
if(!jmd)
jmd->write(soundpacket, sizeof(soundpacket));
if(!*jmd)
throw std::runtime_error("Can't write JMD sound packet");
}
std::ofstream jmd;
std::ostream* jmd;
void (*deleter)(void* f);
uint64_t last_written_ts;
unsigned complevel;
};
@ -315,12 +332,14 @@ namespace
std::set<std::string> list_submodes() throw(std::bad_alloc)
{
std::set<std::string> x;
x.insert("file");
x.insert("tcp");
return x;
}
bool wants_prefix(const std::string& mode) throw()
unsigned mode_details(const std::string& mode) throw()
{
return false;
return (mode == "tcp") ? target_type_special : target_type_file;
}
std::string name() throw(std::bad_alloc)
@ -330,7 +349,7 @@ namespace
std::string modename(const std::string& mode) throw(std::bad_alloc)
{
return "";
return (mode == "tcp") ? "over TCP/IP" : "to file";
}
bool busy()
@ -342,11 +361,11 @@ namespace
std::runtime_error)
{
if(prefix == "")
throw std::runtime_error("Expected filename");
throw std::runtime_error("Expected target");
if(vid_dumper)
throw std::runtime_error("JMD dumping already in progress");
try {
vid_dumper = new jmd_avsnoop(prefix);
vid_dumper = new jmd_avsnoop(prefix, mode == "tcp");
} catch(std::bad_alloc& e) {
throw;
} catch(std::exception& e) {

View file

@ -1,5 +1,6 @@
#include "core/advdumper.hpp"
#include "core/dispatch.hpp"
#include "video/tcp.hpp"
#include "library/serialization.hpp"
#include <iomanip>
@ -10,29 +11,6 @@
#include <sstream>
#include <fstream>
#include <zlib.h>
#ifndef NO_TCP_SOCKETS
#include <boost/iostreams/categories.hpp>
#include <boost/iostreams/copy.hpp>
#include <boost/iostreams/stream.hpp>
#include <boost/iostreams/stream_buffer.hpp>
#include <boost/iostreams/filter/symmetric.hpp>
#include <boost/iostreams/filter/zlib.hpp>
#include <boost/iostreams/filtering_stream.hpp>
#include <boost/iostreams/device/back_inserter.hpp>
#include <unistd.h>
#if defined(_WIN32) || defined(_WIN64)
//Why the fuck does windows have nonstandard socket API???
#define _WIN32_WINNT 0x0501
#include <winsock2.h>
#include <ws2tcpip.h>
struct sockaddr_un { int sun_family; char sun_path[108]; };
#else
#include <sys/socket.h>
#include <netdb.h>
#include <sys/un.h>
#endif
#include <sys/types.h>
#endif
#define IS_RGB(m) (((m) + ((m) >> 3)) & 2)
#define IS_64(m) (m % 5 < 2)
@ -42,194 +20,6 @@ struct sockaddr_un { int sun_family; char sun_path[108]; };
namespace
{
#ifndef NO_TCP_SOCKETS
//Increment port number by 1.
void mung_sockaddr(struct sockaddr* addr, socklen_t addrlen)
{
switch(addr->sa_family) {
case AF_INET: { //IPv4
struct sockaddr_in* _addr = (struct sockaddr_in*)addr;
_addr->sin_port = htons(htons(_addr->sin_port) + 1);
break;
}
case AF_INET6: { //IPv6
struct sockaddr_in6* _addr = (struct sockaddr_in6*)addr;
_addr->sin6_port = htons(htons(_addr->sin6_port) + 1);
break;
}
case AF_UNIX: { //Unix domain sockets.
struct sockaddr_un* _addr = (struct sockaddr_un*)addr;
const char* b1 = (char*)_addr;
const char* b2 = (char*)&_addr->sun_path;
size_t maxpath = addrlen - (b2 - b1);
for(size_t i = 0; i < maxpath; i++)
if(i && !_addr->sun_path[i]) {
maxpath = i;
break;
}
if(!maxpath)
throw std::runtime_error("Eh, empty unix domain socket path?");
_addr->sun_path[maxpath - 1]++;
break;
}
default:
throw std::runtime_error("This address family is not supported, sorry.");
}
}
int compat_connect(int fd, struct sockaddr* addr, socklen_t addrlen)
{
#if defined(_WIN32) || defined(_WIN64)
return connect(fd, addr, addrlen) ? -1 : 0;
#else
return connect(fd, addr, addrlen);
#endif
}
std::pair<int, int> establish_connections(struct addrinfo* i)
{
struct sockaddr* addr = i->ai_addr;
socklen_t addrlen = i->ai_addrlen;
int a = socket(i->ai_family, i->ai_socktype, i->ai_protocol);
if(a < 0) {
int err = errno;
throw std::runtime_error(std::string("socket: ") + strerror(err));
}
int b = socket(i->ai_family, i->ai_socktype, i->ai_protocol);
if(b < 0) {
int err = errno;
close(a);
throw std::runtime_error(std::string("socket: ") + strerror(err));
}
if(compat_connect(a, addr, addrlen) < 0) {
int err = errno;
close(a);
close(b);
throw std::runtime_error(std::string("connect (video): ") + strerror(err));
}
mung_sockaddr(addr, addrlen);
if(compat_connect(b, addr, addrlen) < 0) {
int err = errno;
close(a);
close(b);
throw std::runtime_error(std::string("connect (audio): ") + strerror(err));
}
std::cerr << "Routing video to socket " << a << std::endl;
std::cerr << "Routing audio to socket " << b << std::endl;
return std::make_pair(a, b);
}
std::pair<int, int> get_sockets(const std::string& name)
{
struct addrinfo hints;
struct addrinfo* ainfo;
bool real = false;
int r;
std::string node, service, tmp = name;
size_t s;
struct sockaddr_un uaddr;
if(name[0] == '/' || name[0] == '@') {
//Fake a unix-domain.
if(name.length() >= sizeof(sockaddr_un) - offsetof(sockaddr_un, sun_path) - 1)
throw std::runtime_error("Path too long for filesystem socket");
size_t namelen = offsetof(struct sockaddr_un, sun_path) + name.length();
uaddr.sun_family = AF_UNIX;
strcpy(uaddr.sun_path, name.c_str());
if(name[0] == '@')
uaddr.sun_path[0] = 0; //Mark as abstract namespace socket.
ainfo = &hints;
ainfo->ai_flags = 0;
ainfo->ai_family = AF_UNIX;
ainfo->ai_socktype = SOCK_STREAM;
ainfo->ai_protocol = 0;
ainfo->ai_addrlen = (name[0] == '@') ? namelen : sizeof(sockaddr_un),
ainfo->ai_addr = reinterpret_cast<sockaddr*>(&uaddr);
ainfo->ai_canonname = NULL;
ainfo->ai_next = NULL;
goto establish;
}
//Split into address and port.
s = tmp.find_last_of(":");
if(s >= tmp.length())
throw std::runtime_error("Port number has to be specified");
node = tmp.substr(0, s);
service = tmp.substr(s + 1);
memset(&hints, 0, sizeof(hints));
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
#ifdef AI_V4MAPPED
hints.ai_flags = AI_V4MAPPED;
#endif
#ifdef AI_ADDRCONFIG
hints.ai_flags = AI_ADDRCONFIG;
#endif
real = true;
r = getaddrinfo(node.c_str(), service.c_str(), &hints, &ainfo);
if(r < 0)
throw std::runtime_error(std::string("getaddrinfo: ") + gai_strerror(r));
establish:
auto x = establish_connections(ainfo);
if(real)
freeaddrinfo(ainfo);
return x;
}
class socket_output
{
public:
typedef char char_type;
typedef boost::iostreams::sink_tag category;
socket_output(int _fd)
: fd(_fd)
{
}
void close()
{
::close(fd);
}
std::streamsize write(const char* s, std::streamsize n)
{
size_t w = n;
while(n > 0) {
ssize_t r = ::send(fd, s, n, 0);
if(r >= 0) {
s += r;
n -= r;
} else { //Error.
int err = errno;
messages << "Socket write error: " << strerror(err) << std::endl;
break;
}
}
return w;
}
protected:
int fd;
};
bool tcp_dump_supported = true;
#else
std::pair<int, int> get_sockets(const std::string& name)
{
throw std::runtime_error("Dumping over TCP/IP not supported");
}
class socket_output
{
public:
typedef char char_type;
typedef boost::iostreams::sink_tag category;
socket_output(int _fd) {}
void close() {}
std::streamsize write(const char* s, std::streamsize n) { return n; }
};
bool tcp_dump_supported = false;
#endif
unsigned strhash(const std::string& str)
{
unsigned h = 0;
@ -238,6 +28,11 @@ establish:
return h;
}
void deleter_fn(void* f)
{
delete reinterpret_cast<std::ofstream*>(f);
}
class raw_avsnoop : public information_dispatch
{
public:
@ -246,12 +41,22 @@ establish:
{
enable_send_sound();
if(socket_mode) {
std::pair<int, int> socks = get_sockets(prefix);
video = new boost::iostreams::stream<socket_output>(socks.first);
audio = new boost::iostreams::stream<socket_output>(socks.second);
socket_address videoaddr = socket_address(prefix);
socket_address audioaddr = videoaddr.next();
deleter = socket_address::deleter();
video = audio = NULL;
try {
video = &videoaddr.connect();
audio = &audioaddr.connect();
} catch(...) {
deleter(video);
deleter(audio);
throw;
}
} else {
video = new std::ofstream(prefix + ".video", std::ios::out | std::ios::binary);
audio = new std::ofstream(prefix + ".audio", std::ios::out | std::ios::binary);
deleter = deleter_fn;
}
if(!*video || !*audio)
throw std::runtime_error("Can't open output files");
@ -262,8 +67,10 @@ establish:
~raw_avsnoop() throw()
{
delete video;
delete audio;
if(video)
deleter(video);
if(audio)
deleter(audio);
}
void on_frame(struct lcscreen& _frame, uint32_t fps_n, uint32_t fps_d)
@ -306,8 +113,8 @@ establish:
void on_dump_end()
{
delete video;
delete audio;
deleter(video);
deleter(audio);
video = NULL;
audio = NULL;
}
@ -319,6 +126,7 @@ establish:
private:
std::ostream* audio;
std::ostream* video;
void (*deleter)(void* f);
bool have_dumped_frame;
struct screen<false> dscr;
struct screen<true> dscr2;
@ -336,7 +144,7 @@ establish:
std::set<std::string> list_submodes() throw(std::bad_alloc)
{
std::set<std::string> x;
for(size_t i = 0; i < (tcp_dump_supported ? 2 : 1); i++)
for(size_t i = 0; i < (socket_address::supported() ? 2 : 1); i++)
for(size_t j = 0; j < 2; j++)
for(size_t k = 0; k < 2; k++)
x.insert(std::string("") + (i ? "tcp" : "") + (j ? "bgr" : "rgb")
@ -344,9 +152,9 @@ establish:
return x;
}
bool wants_prefix(const std::string& mode) throw()
unsigned mode_details(const std::string& mode) throw()
{
return true;
return IS_TCP(strhash(mode)) ? target_type_special : target_type_prefix;
}
std::string name() throw(std::bad_alloc)

View file

@ -4,6 +4,7 @@
#include "core/dispatch.hpp"
#include "interface/core.hpp"
#include "library/serialization.hpp"
#include "video/tcp.hpp"
#include <iomanip>
#include <cassert>
@ -22,25 +23,38 @@
namespace
{
void deleter_fn(void* f)
{
delete reinterpret_cast<std::ofstream*>(f);
}
class sdmp_avsnoop : public information_dispatch
{
public:
sdmp_avsnoop(const std::string& prefix, bool ssflag) throw(std::bad_alloc)
sdmp_avsnoop(const std::string& prefix, const std::string& mode) throw(std::bad_alloc,
std::runtime_error)
: information_dispatch("dump-sdmp")
{
enable_send_sound();
oprefix = prefix;
sdump_ss = ssflag;
sdump_ss = (mode != "ms");
ssize = 0;
next_seq = 0;
sdump_iopen = false;
dumped_pic = false;
if(mode == "tcp") {
out = &(socket_address(prefix).connect());
deleter = socket_address::deleter();
} else {
out = NULL;
deleter = deleter_fn;
}
}
~sdmp_avsnoop() throw()
{
try {
if(sdump_iopen)
out.close();
if(out)
deleter(out);
} catch(...) {
}
}
@ -53,10 +67,10 @@ namespace
flags |= (overscan ? SDUMP_FLAG_OVERSCAN : 0);
flags |= (region == VIDEO_REGION_PAL ? SDUMP_FLAG_PAL : 0);
unsigned char tbuffer[2049];
if(!sdump_iopen || (ssize > CUTOFF && !sdump_ss)) {
if(!out || (ssize > CUTOFF && !sdump_ss)) {
std::cerr << "Starting new segment" << std::endl;
if(sdump_iopen)
out.close();
if(out)
deleter(out);
std::ostringstream str;
if(sdump_ss)
str << oprefix;
@ -64,10 +78,9 @@ namespace
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)
out = new std::ofstream(str2.c_str(), std::ios::out | std::ios::binary);
if(!*out)
throw std::runtime_error("Failed to open '" + str2 + "'");
sdump_iopen = true;
write32ube(tbuffer, 0x53444D50U);
write32ube(tbuffer + 4, emucore_get_video_rate().first);
write32ube(tbuffer + 8, emucore_get_audio_rate().first);
@ -76,35 +89,36 @@ namespace
throw std::runtime_error("Failed to write header to '" + str2 + "'");
ssize = 12;
}
dumped_pic = true;
tbuffer[0] = flags;
for(unsigned i = 0; i < 512; i++) {
for(unsigned j = 0; j < 512; j++)
write32ube(tbuffer + (4 * j + 1), raw[512 * i + j]);
out.write(reinterpret_cast<char*>(tbuffer + (i ? 1 : 0)), i ? 2048 : 2049);
out->write(reinterpret_cast<char*>(tbuffer + (i ? 1 : 0)), i ? 2048 : 2049);
}
if(!out)
if(!*out)
throw std::runtime_error("Failed to write frame");
ssize += 1048577;
}
void on_sample(short l, short r)
{
if(!sdump_iopen)
if(!out || !dumped_pic)
return;
unsigned char pkt[5];
pkt[0] = 16;
write16sbe(pkt + 1, l);
write16sbe(pkt + 3, r);
out.write(reinterpret_cast<char*>(pkt), 5);
if(!out)
out->write(reinterpret_cast<char*>(pkt), 5);
if(!*out)
throw std::runtime_error("Failed to write sample");
ssize += 5;
}
void on_dump_end()
{
if(sdump_iopen)
out.close();
deleter(out);
out = NULL;
}
bool get_dumper_flag() throw()
@ -114,10 +128,11 @@ namespace
private:
std::string oprefix;
bool sdump_ss;
bool dumped_pic;
uint64_t ssize;
uint64_t next_seq;
bool sdump_iopen;
std::ofstream out;
void (*deleter)(void* f);
std::ostream* out;
};
sdmp_avsnoop* vid_dumper;
@ -132,12 +147,19 @@ namespace
std::set<std::string> x;
x.insert("ss");
x.insert("ms");
x.insert("tcp");
return x;
}
bool wants_prefix(const std::string& mode) throw()
unsigned mode_details(const std::string& mode) throw()
{
return (mode != "ss");
if(mode == "ss")
return target_type_file;
if(mode == "ms")
return target_type_prefix;
if(mode == "tcp")
return target_type_special;
return target_type_mask;
}
std::string name() throw(std::bad_alloc)
@ -147,7 +169,13 @@ namespace
std::string modename(const std::string& mode) throw(std::bad_alloc)
{
return (mode == "ss" ? "Single-Segment" : "Multi-Segment");
if(mode == "ss")
return "Single-Segment";
if(mode == "ms")
return "Multi-Segment";
if(mode == "tcp")
return "over TCP/IP";
return "What?";
}
bool busy()
@ -158,16 +186,12 @@ namespace
void start(const std::string& mode, const std::string& prefix) throw(std::bad_alloc,
std::runtime_error)
{
if(prefix == "") {
if(mode == "ss")
throw std::runtime_error("Expected filename");
else
throw std::runtime_error("Expected prefix");
}
if(prefix == "")
throw std::runtime_error("Expected target");
if(vid_dumper)
throw std::runtime_error("SDMP Dump already in progress");
try {
vid_dumper = new sdmp_avsnoop(prefix, mode == "ss");
vid_dumper = new sdmp_avsnoop(prefix, mode);
} catch(std::bad_alloc& e) {
throw;
} catch(std::exception& e) {
@ -175,10 +199,7 @@ namespace
x << "Error starting SDMP dump: " << e.what();
throw std::runtime_error(x.str());
}
if(mode == "ss")
messages << "Dumping SDMP (SS) to " << prefix << std::endl;
else
messages << "Dumping SDMP to " << prefix << std::endl;
messages << "Dumping SDMP (" << mode << ") to " << prefix << std::endl;
information_dispatch::do_dumper_update();
}

251
src/video/tcp.cpp Normal file
View file

@ -0,0 +1,251 @@
#include "video/tcp.hpp"
#ifdef NO_TCP_SOCKETS
namespace
{
void deleter_fn(void* f)
{
}
}
socket_address::socket_address(const std::string& spec)
{
throw std::runtime_error("TCP/IP support not compiled in");
}
socket_address socket_address::next()
{
}
std::ostream& socket_address::connect()
{
throw std::runtime_error("TCP/IP support not compiled in");
}
bool socket_address::supported()
{
return false;
}
#else
#include <boost/iostreams/categories.hpp>
#include <boost/iostreams/copy.hpp>
#include <boost/iostreams/stream.hpp>
#include <boost/iostreams/stream_buffer.hpp>
#include <boost/iostreams/filter/symmetric.hpp>
#include <boost/iostreams/filter/zlib.hpp>
#include <boost/iostreams/filtering_stream.hpp>
#include <boost/iostreams/device/back_inserter.hpp>
#include <unistd.h>
#if defined(_WIN32) || defined(_WIN64)
//Why the fuck does windows have nonstandard socket API???
#define _WIN32_WINNT 0x0501
#include <winsock2.h>
#include <ws2tcpip.h>
struct sockaddr_un { int sun_family; char sun_path[108]; };
#else
#include <sys/socket.h>
#include <netdb.h>
#include <sys/un.h>
#endif
#include <sys/types.h>
namespace
{
class socket_output
{
public:
typedef char char_type;
typedef struct : public boost::iostreams::sink_tag, boost::iostreams::closable_tag {} category;
socket_output(int _fd)
: fd(_fd)
{
broken = false;
}
void close()
{
::close(fd);
}
std::streamsize write(const char* s, std::streamsize n)
{
if(broken)
return n;
size_t w = n;
while(n > 0) {
ssize_t r = ::send(fd, s, n, 0);
if(r >= 0) {
s += r;
n -= r;
} else if(errno == EPIPE) {
std::cerr << "The other end of socket went away" << std::endl;
broken = true;
n = 0;
break;
} else { //Error.
int err = errno;
std::cerr << "Socket write error: " << strerror(err) << std::endl;
n = 0;
break;
}
}
return w;
}
protected:
int fd;
bool broken;
};
void deleter_fn(void* f)
{
delete reinterpret_cast<boost::iostreams::stream<socket_output>*>(f);
}
}
socket_address::socket_address(const std::string& name)
{
struct addrinfo hints;
struct addrinfo* ainfo;
bool real = false;
int r;
std::string node, service, tmp = name;
size_t s;
struct sockaddr_un uaddr;
if(name[0] == '/' || name[0] == '@') {
//Fake a unix-domain.
if(name.length() >= sizeof(sockaddr_un) - offsetof(sockaddr_un, sun_path) - 1)
throw std::runtime_error("Path too long for filesystem socket");
size_t namelen = offsetof(struct sockaddr_un, sun_path) + name.length();
uaddr.sun_family = AF_UNIX;
strcpy(uaddr.sun_path, name.c_str());
if(name[0] == '@')
uaddr.sun_path[0] = 0; //Mark as abstract namespace socket.
family = AF_UNIX;
socktype = SOCK_STREAM;
protocol = 0;
memory.resize((name[0] == '@') ? namelen : sizeof(sockaddr_un));
memcpy(&memory[0], &uaddr, memory.size());
return;
}
//Split into address and port.
s = tmp.find_last_of(":");
if(s >= tmp.length())
throw std::runtime_error("Port number has to be specified");
node = tmp.substr(0, s);
service = tmp.substr(s + 1);
memset(&hints, 0, sizeof(hints));
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
#ifdef AI_V4MAPPED
hints.ai_flags = AI_V4MAPPED;
#endif
#ifdef AI_ADDRCONFIG
hints.ai_flags = AI_ADDRCONFIG;
#endif
real = true;
r = getaddrinfo(node.c_str(), service.c_str(), &hints, &ainfo);
if(r < 0)
throw std::runtime_error(std::string("getaddrinfo: ") + gai_strerror(r));
establish:
family = ainfo->ai_family;
socktype = ainfo->ai_socktype;
protocol = ainfo->ai_protocol;
try {
memory.resize(ainfo->ai_addrlen);
memcpy(&memory[0], ainfo->ai_addr, ainfo->ai_addrlen);
} catch(...) {
freeaddrinfo(ainfo);
throw;
}
freeaddrinfo(ainfo);
}
socket_address socket_address::next()
{
std::vector<char> newaddr = memory;
struct sockaddr* addr = reinterpret_cast<struct sockaddr*>(&newaddr[0]);
socklen_t addrlen = memory.size();
switch(addr->sa_family) {
case AF_INET: { //IPv4
struct sockaddr_in* _addr = (struct sockaddr_in*)addr;
_addr->sin_port = htons(htons(_addr->sin_port) + 1);
break;
}
case AF_INET6: { //IPv6
struct sockaddr_in6* _addr = (struct sockaddr_in6*)addr;
_addr->sin6_port = htons(htons(_addr->sin6_port) + 1);
break;
}
case AF_UNIX: { //Unix domain sockets.
struct sockaddr_un* _addr = (struct sockaddr_un*)addr;
const char* b1 = (char*)_addr;
const char* b2 = (char*)&_addr->sun_path;
size_t maxpath = addrlen - (b2 - b1);
for(size_t i = 0; i < maxpath; i++)
if(i && !_addr->sun_path[i]) {
maxpath = i;
break;
}
if(!maxpath)
throw std::runtime_error("Eh, empty unix domain socket path?");
_addr->sun_path[maxpath - 1]++;
break;
}
default:
throw std::runtime_error("This address family is not supported, sorry.");
}
socket_address n(family, socktype, protocol);
n.memory = newaddr;
return n;
}
std::ostream& socket_address::connect()
{
int a = socket(family, socktype, protocol);
if(a < 0) {
int err = errno;
throw std::runtime_error(std::string("socket: ") + strerror(err));
}
int r;
struct sockaddr* addr = reinterpret_cast<struct sockaddr*>(&memory[0]);
socklen_t addrlen = memory.size();
#if defined(_WIN32) || defined(_WIN64)
r = ::connect(a, addr, addrlen) ? -1 : 0;
#else
r = ::connect(a, addr, addrlen);
#endif
if(r < 0) {
int err = errno;
::close(a);
throw std::runtime_error(std::string("connect: ") + strerror(err));
}
try {
return *new boost::iostreams::stream<socket_output>(a);
} catch(...) {
::close(a);
throw;
}
}
bool socket_address::supported()
{
return true;
}
#endif
deleter_fn_t socket_address::deleter()
{
return deleter_fn;
}
socket_address::socket_address(int f, int st, int p)
{
family = f;
socktype = st;
protocol = p;
}