Update to v088r14 release.

byuu says:

Changelog:
- added NSS DIP switch settings window (when loading NSS carts with
  appropriate manifest.xml file)
- added video shader selection (they go in ~/.config/bsnes/Video
  Shaders/ now)
- added driver selection
- added timing settings (not only allows video/audio settings, also has
  code to dynamically compute the values for you ... and it actually
  works pretty good!)
- moved "None" controller device to bottom of list (it is the least
  likely to be used, after all)
- added Interface::path() to support MSU1, USART, Link
- input and hotkey mappings remember list position after assignment
- and more!

target-ethos now has all of the functionality of target-ui, and more.
Final code size for the port is 101.2KB (ethos) vs 167.6KB (ui).
A ~67% reduction in code size, yet it does even more! And you can add or
remove an entire system with only three lines of code (Makefile include,
header include, interface append.)

The only problem left is that the BS-X BIOS won't load the BS Zelda no
Densetsu file.
I can't figure out why it's not working, would appreciate any
assistance, but otherwise I'm probably just going to leave it broken for
v089, sorry.

So the show stoppers for a new release at this point are:
- fix laevateinn to compile with the new interface changes (shouldn't be
  too hard, it'll still use the old, direct interface.)
- clean up Emulator::Interface as much as possible (trim down
  Information, mediaRequest should use an alternate struct designed to
  load firmware / slots separately)
- enhance purify to strip SNES ROM headers, and it really needs a GUI
  interface
- it would be highly desirable to make a launcher that can create
  a cartridge folder from an existing ROM set (* ethos will need to
  accept command-line arguments for this.)
- probably need to remember which controller was selected in each port
  for each system across runs
- need to fix the cursor for Super Scope / Justifier games (move from
  19-bit to 32-bit colors broke it)
- have to refactor that cache.(hv)offset thing to fix ASP
This commit is contained in:
Tim Allen 2012-05-07 09:27:42 +10:00
parent 3cb04b101b
commit cb97d98ad2
146 changed files with 604 additions and 5106 deletions

View File

@ -3,7 +3,7 @@
namespace Emulator {
static const char Name[] = "bsnes";
static const char Version[] = "088.13";
static const char Version[] = "088.14";
static const char Author[] = "byuu";
static const char License[] = "GPLv3";
}

View File

@ -59,6 +59,8 @@ struct Interface {
function<void (int16_t, int16_t)> audioSample;
function<int16_t (unsigned, unsigned, unsigned)> inputPoll;
function<void (Media)> mediaRequest;
function<unsigned (const XML::Node&)> dipSettings;
function<string (unsigned)> path;
} callback;
//callback bindings (provided by user interface)
@ -84,6 +86,20 @@ struct Interface {
if(callback.mediaRequest) return callback.mediaRequest(media);
}
virtual unsigned dipSettings(const XML::Node &node) {
if(callback.dipSettings) return callback.dipSettings(node);
return 0u;
}
virtual string path(unsigned group) {
if(callback.path) return callback.path(group);
return "";
}
//information
virtual double videoFrequency() = 0;
virtual double audioFrequency() = 0;
//media interface
virtual bool loaded() { return false; }
virtual string sha256() { return ""; }

View File

@ -4,6 +4,14 @@ namespace Famicom {
Interface *interface = nullptr;
double Interface::videoFrequency() {
return 21477272.0 / (262.0 * 1364.0 - 4.0);
}
double Interface::audioFrequency() {
return 21477272.0 / 12.0;
}
bool Interface::loaded() {
return cartridge.loaded();
}

View File

@ -15,6 +15,9 @@ struct ID {
};
struct Interface : Emulator::Interface {
double videoFrequency();
double audioFrequency();
bool loaded();
string sha256();
void load(unsigned id, const stream &stream, const string &markup = "");

View File

@ -4,6 +4,14 @@ namespace GameBoy {
Interface *interface = nullptr;
double Interface::videoFrequency() {
return 4194304.0 / (154.0 * 456.0);
}
double Interface::audioFrequency() {
return 4194304.0;
}
bool Interface::loaded() {
return cartridge.loaded();
}

View File

@ -22,6 +22,9 @@ struct Interface : Emulator::Interface {
virtual void lcdScanline() {}
virtual void joypWrite(bool p15, bool p14) {}
double videoFrequency();
double audioFrequency();
bool loaded();
string sha256();
void load(unsigned id, const stream &stream, const string &markup = "");

View File

@ -4,6 +4,14 @@ namespace GameBoyAdvance {
Interface *interface = nullptr;
double Interface::videoFrequency() {
return 16777216.0 / (228.0 * 1232.0);
}
double Interface::audioFrequency() {
return 16777216.0 / 512.0;
}
bool Interface::loaded() {
return cartridge.loaded();
}

View File

@ -17,6 +17,9 @@ struct ID {
};
struct Interface : Emulator::Interface {
double videoFrequency();
double audioFrequency();
bool loaded();
void load(unsigned id, const stream &stream, const string &markup = "");
void save(unsigned id, const stream &stream);

View File

@ -17,6 +17,8 @@
namespace nall {
struct directory {
static bool create(const string &pathname, unsigned permissions = 0755);
static bool remove(const string &pathname);
static bool exists(const string &pathname);
static lstring folders(const string &pathname, const string &pattern = "*");
static lstring files(const string &pathname, const string &pattern = "*");
@ -24,6 +26,14 @@ struct directory {
};
#if defined(PLATFORM_WINDOWS)
inline bool directory::create(const string &pathname, unsigned permissions) {
return _wmkdir(utf16_t(pathname)) == 0;
}
inline bool directory::remove(const string &pathname) {
return _wrmdir(utf16_t(pathname)) == 0;
}
inline bool directory::exists(const string &pathname) {
DWORD result = GetFileAttributes(utf16_t(pathname));
if(result == INVALID_FILE_ATTRIBUTES) return false;
@ -94,6 +104,14 @@ struct directory {
return folders;
}
#else
inline bool directory::create(const string &pathname, unsigned permissions) {
return mkdir(pathname, permissions) == 0;
}
inline bool directory::remove(const string &pathname) {
return rmdir(pathname) == 0;
}
inline bool directory::exists(const string &pathname) {
DIR *dp = opendir(pathname);
if(!dp) return false;

View File

@ -22,6 +22,24 @@ namespace nall {
enum class index : unsigned { absolute, relative };
enum class time : unsigned { create, modify, access };
static bool remove(const string &filename) {
return unlink(filename) == 0;
}
static bool truncate(const string &filename, unsigned size) {
#if !defined(_WIN32)
return truncate(filename, size) == 0;
#else
bool result = false;
FILE *fp = fopen(filename, "rb+");
if(fp) {
result = _chsize(fileno(fp), size) == 0;
fclose(fp);
}
return result;
#endif
}
static vector<uint8_t> read(const string &filename) {
vector<uint8_t> memory;
file fp;

View File

@ -60,10 +60,7 @@
#if defined(_WIN32)
#define getcwd _getcwd
#define ftruncate _chsize
#define mkdir(n, m) _wmkdir(nall::utf16_t(n))
#define putenv _putenv
#define rmdir _rmdir
#define vsnprintf _vsnprintf
inline void usleep(unsigned milliseconds) { Sleep(milliseconds / 1000); }
#endif

View File

@ -562,6 +562,10 @@ void RadioItem::setText(const string &text) {
return p.setText(text);
}
string RadioItem::text() {
return state.text;
}
RadioItem::RadioItem():
state(*new State),
base_from_member<pRadioItem&>(*new pRadioItem(*this)),
@ -850,6 +854,14 @@ void ComboBox::setSelection(unsigned row) {
return p.setSelection(row);
}
string ComboBox::text() {
return state.text(selection());
}
string ComboBox::text(unsigned row) {
return state.text(row);
}
ComboBox::ComboBox():
state(*new State),
base_from_member<pComboBox&>(*new pComboBox(*this)),

View File

@ -289,6 +289,7 @@ struct RadioItem : private nall::base_from_member<pRadioItem&>, Action {
bool checked();
void setChecked();
void setText(const nall::string &text);
nall::string text();
RadioItem();
~RadioItem();
@ -403,6 +404,8 @@ struct ComboBox : private nall::base_from_member<pComboBox&>, Widget {
void reset();
unsigned selection();
void setSelection(unsigned row);
nall::string text();
nall::string text(unsigned row);
ComboBox();
~ComboBox();

View File

@ -9,7 +9,6 @@ namespace SuperFamicom {
Cartridge cartridge;
void Cartridge::load(const string &markup, const stream &stream) {
information.markup = markup;
rom.copy(stream);
region = Region::NTSC;

View File

@ -68,14 +68,6 @@ struct Cartridge : property<Cartridge> {
};
linear_vector<Mapping> mapping;
struct Information {
string markup;
struct NSS {
lstring setting;
lstring option[16];
} nss;
} information;
void load(const string &markup, const stream &stream);
void unload();

View File

@ -2,7 +2,6 @@
void Cartridge::parse_markup(const char *markup) {
mapping.reset();
information.nss.setting.reset();
XML::Document document(markup);
auto &cartridge = document["cartridge"];
@ -89,20 +88,8 @@ void Cartridge::parse_markup_ram(XML::Node &root) {
void Cartridge::parse_markup_nss(XML::Node &root) {
if(root.exists() == false) return;
has_nss_dip = true;
for(auto &node : root) {
if(node.name != "setting") continue;
unsigned number = information.nss.setting.size();
if(number >= 16) break; //more than 16 DIP switches is not physically possible
information.nss.option[number].reset();
information.nss.setting.append(node["name"].data);
for(auto &leaf : node) {
if(leaf.name != "option") continue;
string name = leaf["name"].data;
unsigned value = numeral(leaf["value"].data);
information.nss.option[number].append({ hex<4>(value), ":", name });
}
}
nss.dip = interface->dipSettings(root);
}
void Cartridge::parse_markup_icd2(XML::Node &root) {
@ -467,7 +454,7 @@ void Cartridge::parse_markup_obc1(XML::Node &root) {
void Cartridge::parse_markup_msu1(XML::Node &root) {
if(root.exists() == false) {
has_msu1 = file::exists(interface->path((unsigned)Cartridge::Slot::Base, "msu1.rom"));
has_msu1 = file::exists({interface->path(0), "msu1.rom"});
if(has_msu1) {
Mapping m({ &MSU1::mmio_read, &msu1 }, { &MSU1::mmio_write, &msu1 });
m.banklo = 0x00, m.bankhi = 0x3f, m.addrlo = 0x2000, m.addrhi = 0x2007;

View File

@ -22,10 +22,7 @@ void Link::init() {
void Link::load() {
if(opened()) close();
string basename = interface->path((unsigned)Cartridge::Slot::Base, "");
string name = program != "" ? program : notdir(basename);
string path = dir(basename);
if(open(name, path)) {
if(open("link.so", interface->path(0))) {
link_power = sym("link_power");
link_reset = sym("link_reset");
link_run = sym("link_run" );

View File

@ -52,7 +52,7 @@ void MSU1::init() {
void MSU1::load() {
if(datafile.open()) datafile.close();
datafile.open(interface->path((unsigned)Cartridge::Slot::Base, "msu1.rom"), file::mode::read);
datafile.open({interface->path(0), "msu1.rom"}, file::mode::read);
}
void MSU1::unload() {
@ -112,7 +112,7 @@ void MSU1::mmio_write(unsigned addr, uint8 data) {
case 4: mmio.audio_track = (mmio.audio_track & 0xff00) | (data << 0);
case 5: mmio.audio_track = (mmio.audio_track & 0x00ff) | (data << 8);
if(audiofile.open()) audiofile.close();
if(audiofile.open(interface->path((unsigned)Cartridge::Slot::Base, { "track-", (unsigned)mmio.audio_track, ".pcm" }), file::mode::read)) {
if(audiofile.open({interface->path(0), "track-", mmio.audio_track, ".pcm"}, file::mode::read)) {
uint32 header = audiofile.readm(4);
if(header != 0x4d535531) { //verify 'MSU1' header
audiofile.close();

View File

@ -16,12 +16,12 @@ void MSU1::serialize(serializer &s) {
s.integer(mmio.audio_play);
if(datafile.open()) datafile.close();
if(datafile.open(interface->path((unsigned)Cartridge::Slot::Base, "msu1.rom"), file::mode::read)) {
if(datafile.open({interface->path(0), "msu1.rom"}, file::mode::read)) {
datafile.seek(mmio.data_offset);
}
if(audiofile.open()) audiofile.close();
if(audiofile.open(interface->path((unsigned)Cartridge::Slot::Base, { "track-", (unsigned)mmio.audio_track, ".pcm" }), file::mode::read)) {
if(audiofile.open({interface->path(0), "track-", mmio.audio_track, ".pcm"}, file::mode::read)) {
audiofile.seek(mmio.audio_offset);
}
}

View File

@ -6,10 +6,10 @@ namespace SuperFamicom {
NSS nss;
void NSS::init() {
dip = 0x0000;
}
void NSS::load() {
dip = 0x0000;
bus.map(Bus::MapMode::Direct, 0x00, 0x3f, 0x4100, 0x4101, { &NSS::read, this }, { &NSS::write, this });
bus.map(Bus::MapMode::Direct, 0x80, 0xbf, 0x4100, 0x4101, { &NSS::read, this }, { &NSS::write, this });
}

View File

@ -1,5 +1,6 @@
class NSS {
public:
struct NSS {
uint16 dip;
void init();
void load();
void unload();
@ -9,9 +10,6 @@ public:
void set_dip(uint16 dip);
uint8 read(unsigned addr);
void write(unsigned addr, uint8 data);
private:
uint16 dip;
};
extern NSS nss;

View File

@ -122,7 +122,7 @@ USART::USART(bool port) : Controller(port) {
txlength = 0;
txdata = 0;
string filename = interface->path((unsigned)Cartridge::Slot::Base, "usart.so");
string filename = {interface->path(0), "usart.so"};
if(open_absolute(filename)) {
init = sym("usart_init");
main = sym("usart_main");

View File

@ -4,6 +4,17 @@ namespace SuperFamicom {
Interface *interface = nullptr;
double Interface::videoFrequency() {
switch(system.region()) { default:
case System::Region::NTSC: return system.cpu_frequency() / (262.0 * 1364.0 - 4.0);
case System::Region::PAL: return system.cpu_frequency() / (312.0 * 1364.0);
}
}
double Interface::audioFrequency() {
return system.apu_frequency() / 768.0;
}
bool Interface::loaded() {
return cartridge.loaded();
}
@ -236,12 +247,7 @@ Interface::Interface() {
media.append({ID::ROM, "Sufami Turbo", "sfc", "program.rom", "st" });
{
Device device{0, ID::Port1 | ID::Port2, "None"};
this->device.append(device);
}
{
Device device{1, ID::Port1 | ID::Port2, "Controller"};
Device device{0, ID::Port1 | ID::Port2, "Controller"};
device.input.append({ 0, 0, "B" });
device.input.append({ 1, 0, "Y" });
device.input.append({ 2, 0, "Select"});
@ -259,7 +265,7 @@ Interface::Interface() {
}
{
Device device{2, ID::Port1 | ID::Port2, "Multitap"};
Device device{1, ID::Port1 | ID::Port2, "Multitap"};
for(unsigned p = 1, n = 0; p <= 4; p++, n += 12) {
device.input.append({n + 0, 0, {"Port ", p, " - ", "B" }});
device.input.append({n + 1, 0, {"Port ", p, " - ", "Y" }});
@ -280,7 +286,7 @@ Interface::Interface() {
}
{
Device device{3, ID::Port1 | ID::Port2, "Mouse"};
Device device{2, ID::Port1 | ID::Port2, "Mouse"};
device.input.append({0, 1, "X-axis"});
device.input.append({1, 1, "Y-axis"});
device.input.append({2, 0, "Left" });
@ -290,7 +296,7 @@ Interface::Interface() {
}
{
Device device{4, ID::Port2, "Super Scope"};
Device device{3, ID::Port2, "Super Scope"};
device.input.append({0, 1, "X-axis" });
device.input.append({1, 1, "Y-axis" });
device.input.append({2, 0, "Trigger"});
@ -302,7 +308,7 @@ Interface::Interface() {
}
{
Device device{5, ID::Port2, "Justifier"};
Device device{4, ID::Port2, "Justifier"};
device.input.append({0, 1, "X-axis" });
device.input.append({1, 1, "Y-axis" });
device.input.append({2, 0, "Trigger"});
@ -312,7 +318,7 @@ Interface::Interface() {
}
{
Device device{6, ID::Port2, "Justifiers"};
Device device{5, ID::Port2, "Justifiers"};
device.input.append({0, 1, "Port 1 - X-axis" });
device.input.append({1, 1, "Port 1 - Y-axis" });
device.input.append({2, 0, "Port 1 - Trigger"});
@ -327,7 +333,12 @@ Interface::Interface() {
}
{
Device device{7, ID::Port1, "Serial USART"};
Device device{6, ID::Port1, "Serial USART"};
this->device.append(device);
}
{
Device device{7, ID::Port1 | ID::Port2, "None"};
this->device.append(device);
}

View File

@ -33,8 +33,8 @@ struct ID {
};
struct Interface : Emulator::Interface {
virtual string path(unsigned slot, const string &hint) { return ""; }
virtual void message(const string &text) {}
double videoFrequency();
double audioFrequency();
bool loaded();
string sha256();

View File

@ -1,6 +1,5 @@
struct Input {
enum class Device : unsigned {
None,
Joypad,
Multitap,
Mouse,
@ -8,6 +7,7 @@ struct Input {
Justifier,
Justifiers,
USART,
None,
};
enum class JoypadID : unsigned {

View File

@ -94,6 +94,15 @@ void System::term() {
}
void System::load() {
region = config.region;
expansion = config.expansion_port;
if(region == Region::Autodetect) {
region = (cartridge.region() == Cartridge::Region::NTSC ? Region::NTSC : Region::PAL);
}
cpu_frequency = region() == Region::NTSC ? config.cpu.ntsc_frequency : config.cpu.pal_frequency;
apu_frequency = region() == Region::NTSC ? config.smp.ntsc_frequency : config.smp.pal_frequency;
audio.coprocessor_enable(false);
bus.map_reset();
@ -151,15 +160,6 @@ void System::unload() {
void System::power() {
random.seed((unsigned)time(0));
region = config.region;
expansion = config.expansion_port;
if(region == Region::Autodetect) {
region = (cartridge.region() == Cartridge::Region::NTSC ? Region::NTSC : Region::PAL);
}
cpu_frequency = region() == Region::NTSC ? config.cpu.ntsc_frequency : config.cpu.pal_frequency;
apu_frequency = region() == Region::NTSC ? config.smp.ntsc_frequency : config.smp.pal_frequency;
cpu.power();
smp.power();
dsp.power();

View File

@ -17,6 +17,8 @@ void Application::bootstrap() {
system->callback.audioSample = {&Interface::audioSample, interface};
system->callback.inputPoll = {&Interface::inputPoll, interface};
system->callback.mediaRequest = {&Interface::mediaRequest, interface};
system->callback.dipSettings = {&Interface::dipSettings, interface};
system->callback.path = {&Interface::path, interface};
for(auto &firmware : system->firmware) {
filestream fs{application->path({firmware.name, ".", firmware.type, "/", firmware.path})};

View File

@ -2,7 +2,9 @@
Configuration *config = nullptr;
Configuration::Configuration() {
append(video.driver = ruby::video.default_driver(), "Video::Driver");
append(video.synchronize = false, "Video::Synchronize");
append(video.shader = "Blur", "Video::Shader");
append(video.scaleMode = 0, "Video::ScaleMode");
append(video.aspectCorrection = true, "Video::AspectCorrection");
append(video.maskOverscan = false, "Video::MaskOverscan");
@ -11,15 +13,18 @@ Configuration::Configuration() {
append(video.saturation = 100, "Video::Saturation");
append(video.gamma = 150, "Video::Gamma");
append(video.luminance = 100, "Video::Luminance");
append(audio.driver = ruby::audio.default_driver(), "Audio::Driver");
append(audio.synchronize = true, "Audio::Synchronize");
append(audio.frequency = 48000, "Audio::Frequency");
append(audio.latency = 60, "Audio::Latency");
append(audio.resampler = 2, "Audio::Resampler");
append(audio.volume = 100, "Audio::Volume");
append(audio.mute = false, "Audio::Mute");
append(input.driver = ruby::input.default_driver(), "Input::Driver");
append(input.focusPause = false, "Input::Focus::Pause");
append(input.focusAllow = false, "Input::Focus::AllowInput");
append(timing.video = 60.0, "Timing::Video");
append(timing.audio = 48000.0, "Timing::Audio");
load();
}

View File

@ -1,6 +1,8 @@
struct Configuration : configuration {
struct Video {
string driver;
bool synchronize;
string shader;
unsigned scaleMode;
bool aspectCorrection;
bool maskOverscan;
@ -12,6 +14,7 @@ struct Configuration : configuration {
} video;
struct Audio {
string driver;
bool synchronize;
unsigned frequency;
unsigned latency;
@ -21,10 +24,16 @@ struct Configuration : configuration {
} audio;
struct Input {
string driver;
bool focusPause;
bool focusAllow;
} input;
struct Timing {
double video;
double audio;
} timing;
void load();
void save();
Configuration();

View File

@ -5,8 +5,7 @@ Application *application = nullptr;
DSP dspaudio;
Emulator::Interface& system() {
struct application_interface_null{};
if(application->active == nullptr) throw application_interface_null();
if(application->active == nullptr) throw;
return *application->active;
}
@ -47,7 +46,7 @@ Application::Application(int argc, char **argv) {
} else {
userpath.append(".config/ethos/");
}
mkdir(userpath, 0755);
directory::create(userpath);
bootstrap();
active = nullptr;
@ -64,20 +63,23 @@ Application::Application(int argc, char **argv) {
monospaceFont = "Liberation Mono, 8";
}
video.driver("OpenGL");
audio.driver("ALSA");
input.driver("SDL");
config = new Configuration;
video.driver(config->video.driver);
audio.driver(config->audio.driver);
input.driver(config->input.driver);
utility = new Utility;
inputManager = new InputManager;
windowManager = new WindowManager;
browser = new Browser;
presentation = new Presentation;
dipSwitches = new DipSwitches;
videoSettings = new VideoSettings;
audioSettings = new AudioSettings;
inputSettings = new InputSettings;
hotkeySettings = new HotkeySettings;
timingSettings = new TimingSettings;
driverSettings = new DriverSettings;
settings = new Settings;
cheatDatabase = new CheatDatabase;
cheatEditor = new CheatEditor;
@ -89,19 +91,20 @@ Application::Application(int argc, char **argv) {
if(!video.cap(Video::Depth) || !video.set(Video::Depth, depth = 30u)) {
video.set(Video::Depth, depth = 24u);
}
video.init();
if(video.init() == false) { video.driver("None"); video.init(); }
audio.set(Audio::Handle, presentation->viewport.handle());
audio.init();
if(audio.init() == false) { audio.driver("None"); audio.init(); }
input.set(Input::Handle, presentation->viewport.handle());
input.init();
if(input.init() == false) { input.driver("None"); input.init(); }
dspaudio.setPrecision(16);
dspaudio.setBalance(0.0);
dspaudio.setFrequency(96000);
utility->synchronizeRuby();
utility->updateShader();
while(quit == false) {
OS::processEvents();

View File

@ -0,0 +1,68 @@
DipSwitches *dipSwitches = nullptr;
DipSwitch::DipSwitch() {
append(name, {100, 0}, 5);
append(value, {~0, 0});
}
DipSwitches::DipSwitches() {
setTitle("DIP Switches");
layout.setMargin(5);
accept.setText("Accept");
append(layout);
for(auto &dip : this->dip) layout.append(dip, {~0, 0}, 5);
layout.append(controlLayout, {~0, 0});
controlLayout.append(spacer, {~0, 0});
controlLayout.append(accept, {80, 0});
setGeometry({128, 128, 250, layout.minimumGeometry().height});
onClose = accept.onActivate = [&] { quit = true; };
}
unsigned DipSwitches::run(const XML::Node &node) {
audio.clear();
setModal(true);
quit = false;
for(auto &dip : this->dip) {
dip.name.setEnabled(false);
dip.name.setText("(empty)");
dip.value.setEnabled(false);
dip.value.reset();
dip.values.reset();
}
unsigned index = 0;
for(auto &setting : node) {
if(setting.name != "setting") continue;
dip[index].name.setEnabled();
dip[index].name.setText(setting["name"].data);
dip[index].value.setEnabled();
for(auto &option : setting) {
if(option.name != "option") continue;
dip[index].value.append(option["name"].data);
dip[index].values.append(fixedpoint::parse(option["value"].data));
}
if(++index >= Slots) break;
}
setVisible();
accept.setFocused();
while(quit == false) {
OS::processEvents();
}
setModal(false);
setVisible(false);
unsigned result = 0;
for(auto &dip : this->dip) {
if(dip.value.enabled() == false) continue;
result |= dip.values[dip.value.selection()];
}
return result;
}

View File

@ -1,23 +1,25 @@
struct DipSwitch : HorizontalLayout {
Label name;
ComboBox value;
vector<unsigned> values;
DipSwitch();
};
struct DipSwitches : Window {
VerticalLayout layout;
DipSwitch dip[8];
HorizontalLayout controlLayout;
Widget spacer;
Button acceptButton;
enum : unsigned { Slots = 8 };
void load();
void accept();
VerticalLayout layout;
DipSwitch dip[Slots];
HorizontalLayout controlLayout;
Widget spacer;
Button accept;
unsigned run(const XML::Node &node);
DipSwitches();
private:
unsigned values[8][16];
bool quit;
};
extern DipSwitches *dipSwitches;

View File

@ -1,3 +1,4 @@
#include "../ethos.hpp"
#include "browser.cpp"
#include "presentation.cpp"
#include "dip-switches.cpp"

View File

@ -1,2 +1,3 @@
#include "browser.hpp"
#include "presentation.hpp"
#include "dip-switches.hpp"

View File

@ -9,6 +9,15 @@ void Presentation::synchronize() {
}
}
shaderNone.setChecked();
if(config->video.shader == "None") shaderNone.setChecked();
if(config->video.shader == "Blur") shaderBlur.setChecked();
for(auto &shader : shaderList) {
string name = notdir(nall::basename(config->video.shader));
if(auto position = name.position(".")) name[position()] = 0;
if(name == shader->text()) shader->setChecked();
}
switch(config->video.scaleMode) {
case 0: centerVideo.setChecked(); break;
case 1: scaleVideo.setChecked(); break;
@ -29,6 +38,7 @@ void Presentation::setSystemName(const string &name) {
Presentation::Presentation() : active(nullptr) {
bootstrap();
loadShaders();
setGeometry({1024, 600, 720, 480});
windowManager->append(this, "Presentation");
@ -46,6 +56,9 @@ Presentation::Presentation() : active(nullptr) {
RadioItem::group(centerVideo, scaleVideo, stretchVideo);
aspectCorrection.setText("Aspect Correction");
maskOverscan.setText("Mask Overscan");
shaderMenu.setText("Shader");
shaderNone.setText("None");
shaderBlur.setText("Blur");
synchronizeVideo.setText("Synchronize Video");
synchronizeAudio.setText("Synchronize Audio");
muteAudio.setText("Mute Audio");
@ -69,6 +82,10 @@ Presentation::Presentation() : active(nullptr) {
append(settingsMenu);
settingsMenu.append(videoMenu);
videoMenu.append(centerVideo, scaleVideo, stretchVideo, *new Separator, aspectCorrection, maskOverscan);
settingsMenu.append(shaderMenu);
shaderMenu.append(shaderNone, shaderBlur);
if(shaderList.size() > 0) shaderMenu.append(*new Separator);
for(auto &shader : shaderList) shaderMenu.append(*shader);
settingsMenu.append(*new Separator);
settingsMenu.append(synchronizeVideo, synchronizeAudio, muteAudio);
settingsMenu.append(*new Separator);
@ -87,6 +104,8 @@ Presentation::Presentation() : active(nullptr) {
onSize = [&] { utility->resize(); };
onClose = [&] { application->quit = true; };
shaderNone.onActivate = [&] { config->video.shader = "None"; utility->updateShader(); };
shaderBlur.onActivate = [&] { config->video.shader = "Blur"; utility->updateShader(); };
centerVideo.onActivate = [&] { config->video.scaleMode = 0; utility->resize(); };
scaleVideo.onActivate = [&] { config->video.scaleMode = 1; utility->resize(); };
stretchVideo.onActivate = [&] { config->video.scaleMode = 2; utility->resize(); };
@ -135,7 +154,7 @@ void Presentation::bootstrap() {
for(auto &device : port.device) {
auto iDevice = new RadioItem;
iDevice->setText(device.name);
iDevice->onActivate = [=] { utility->connect(portNumber, deviceNumber); };
iDevice->onActivate = [=] { utility->connect(portNumber, device.id); };
iPort->group.append(*iDevice);
iPort->device.append(iDevice);
deviceNumber++;
@ -169,3 +188,25 @@ void Presentation::bootstrap() {
emulatorList.append(iEmulator);
}
}
void Presentation::loadShaders() {
string pathname = application->path("Video Shaders/");
lstring files = directory::files(pathname);
for(auto &filename : files) {
auto shader = new RadioItem;
string name = filename;
if(auto position = name.position(".")) name[position()] = 0;
shader->setText(name);
shader->onActivate = [=] {
config->video.shader = {pathname, filename};
utility->updateShader();
};
shaderList.append(shader);
}
set<RadioItem&> group;
group.append(shaderNone);
group.append(shaderBlur);
for(auto &shader : shaderList) group.append(*shader);
RadioItem::group(group);
}

View File

@ -30,6 +30,10 @@ struct Presentation : Window {
RadioItem stretchVideo;
CheckItem aspectCorrection;
CheckItem maskOverscan;
Menu shaderMenu;
RadioItem shaderNone;
RadioItem shaderBlur;
vector<RadioItem*> shaderList;
CheckItem synchronizeVideo;
CheckItem synchronizeAudio;
CheckItem muteAudio;
@ -46,6 +50,7 @@ struct Presentation : Window {
void synchronize();
void setSystemName(const string &name);
void loadShaders();
void bootstrap();
Presentation();

View File

@ -99,3 +99,11 @@ int16_t Interface::inputPoll(unsigned port, unsigned device, unsigned input) {
void Interface::mediaRequest(Emulator::Interface::Media media) {
utility->loadMedia(media);
}
unsigned Interface::dipSettings(const XML::Node &node) {
return dipSwitches->run(node);
}
string Interface::path(unsigned group) {
return utility->path(group);
}

View File

@ -4,6 +4,8 @@ struct Interface {
void audioSample(int16_t lsample, int16_t rsample);
int16_t inputPoll(unsigned port, unsigned device, unsigned input);
void mediaRequest(Emulator::Interface::Media media);
unsigned dipSettings(const XML::Node &node);
string path(unsigned group);
};
extern Interface *interface;

View File

@ -0,0 +1,42 @@
DriverSettings *driverSettings = nullptr;
DriverSettings::DriverSettings() {
title.setFont(application->titleFont);
title.setText("Driver Configuration");
videoLabel.setText("Video:");
audioLabel.setText("Audio:");
inputLabel.setText("Input:");
lstring list;
list.split(";", video.driver_list());
for(unsigned n = 0; n < list.size(); n++) {
videoDriver.append(list[n]);
if(list[n] == config->video.driver) videoDriver.setSelection(n);
}
list.split(";", audio.driver_list());
for(unsigned n = 0; n < list.size(); n++) {
audioDriver.append(list[n]);
if(list[n] == config->audio.driver) audioDriver.setSelection(n);
}
list.split(";", input.driver_list());
for(unsigned n = 0; n < list.size(); n++) {
inputDriver.append(list[n]);
if(list[n] == config->input.driver) inputDriver.setSelection(n);
}
append(title, {~0, 0}, 5);
append(driverLayout, {~0, 0});
driverLayout.append(videoLabel, {0, 0}, 5);
driverLayout.append(videoDriver, {~0, 0}, 5);
driverLayout.append(audioLabel, {0, 0}, 5);
driverLayout.append(audioDriver, {~0, 0}, 5);
driverLayout.append(inputLabel, {0, 0}, 5);
driverLayout.append(inputDriver, {~0, 0});
videoDriver.onChange = [&] { config->video.driver = videoDriver.text(); };
audioDriver.onChange = [&] { config->audio.driver = audioDriver.text(); };
inputDriver.onChange = [&] { config->input.driver = inputDriver.text(); };
}

View File

@ -0,0 +1,14 @@
struct DriverSettings : SettingsLayout {
Label title;
HorizontalLayout driverLayout;
Label videoLabel;
ComboBox videoDriver;
Label audioLabel;
ComboBox audioDriver;
Label inputLabel;
ComboBox inputDriver;
DriverSettings();
};
extern DriverSettings *driverSettings;

View File

@ -18,6 +18,7 @@ HotkeySettings::HotkeySettings() : activeInput(nullptr) {
inputList.onActivate = {&HotkeySettings::assignInput, this};
eraseButton.onActivate = {&HotkeySettings::eraseInput, this};
for(auto &hotkey : inputManager->hotkeyMap) inputList.append("", "");
refresh();
}
@ -26,13 +27,13 @@ void HotkeySettings::synchronize() {
}
void HotkeySettings::refresh() {
inputList.reset();
unsigned index = 0;
for(auto &hotkey : inputManager->hotkeyMap) {
string mapping = hotkey->mapping;
mapping.replace("KB0::", "");
mapping.replace("MS0::", "Mouse::");
mapping.replace(",", " and ");
inputList.append(hotkey->name, mapping);
inputList.modify(index++, hotkey->name, mapping);
}
synchronize();
}

View File

@ -114,6 +114,13 @@ void InputSettings::portChanged() {
void InputSettings::deviceChanged() {
inputList.reset();
for(unsigned number : activeDevice().order) inputList.append("", "");
inputChanged();
synchronize();
}
void InputSettings::inputChanged() {
unsigned index = 0;
for(unsigned number : activeDevice().order) {
auto &input = activeDevice().input[number];
auto abstract = inputManager->inputMap(input.guid);
@ -121,9 +128,8 @@ void InputSettings::deviceChanged() {
mapping.replace("KB0::", "");
mapping.replace("MS0::", "Mouse::");
mapping.replace(",", " or ");
inputList.append(input.name, mapping);
inputList.modify(index++, input.name, mapping);
}
synchronize();
}
void InputSettings::resetInput() {
@ -176,7 +182,7 @@ void InputSettings::inputEvent(unsigned scancode, int16_t value, bool allowMouse
if(activeInput->bind(scancode, value) == false) return;
activeInput = nullptr;
deviceChanged();
inputChanged();
settings->setStatusText("");
settings->layout.setEnabled(true);
setEnabled(true);

View File

@ -24,6 +24,7 @@ struct InputSettings : SettingsLayout {
void systemChanged();
void portChanged();
void deviceChanged();
void inputChanged();
void resetInput();
void eraseInput();
void assignInput();

View File

@ -3,6 +3,8 @@
#include "audio.cpp"
#include "input.cpp"
#include "hotkey.cpp"
#include "timing.cpp"
#include "driver.cpp"
Settings *settings = nullptr;
void SettingsLayout::append(Sizable &sizable, const Size &size, unsigned spacing) {
@ -28,6 +30,8 @@ Settings::Settings() {
panelList.append("Audio");
panelList.append("Input");
panelList.append("Hotkeys");
panelList.append("Timing");
panelList.append("Driver");
append(layout);
layout.append(panelList, {120, ~0}, 5);
@ -35,6 +39,8 @@ Settings::Settings() {
append(*audioSettings);
append(*inputSettings);
append(*hotkeySettings);
append(*timingSettings);
append(*driverSettings);
panelList.onChange = {&Settings::panelChanged, this};
@ -43,10 +49,13 @@ Settings::Settings() {
}
void Settings::panelChanged() {
setStatusText("");
videoSettings->setVisible(false);
audioSettings->setVisible(false);
inputSettings->setVisible(false);
hotkeySettings->setVisible(false);
timingSettings->setVisible(false);
driverSettings->setVisible(false);
if(panelList.selected() == false) return;
switch(panelList.selection()) {
@ -54,5 +63,7 @@ void Settings::panelChanged() {
case 1: return audioSettings->setVisible();
case 2: return inputSettings->setVisible();
case 3: return hotkeySettings->setVisible();
case 4: return timingSettings->setVisible();
case 5: return driverSettings->setVisible();
}
}

View File

@ -10,6 +10,8 @@ struct SettingsLayout : HorizontalLayout {
#include "audio.hpp"
#include "input.hpp"
#include "hotkey.hpp"
#include "timing.hpp"
#include "driver.hpp"
struct Settings : Window {
HorizontalLayout layout;

View File

@ -0,0 +1,130 @@
TimingSettings *timingSettings = nullptr;
TimingAdjustment::TimingAdjustment() {
assign.setEnabled(false);
assign.setText("Assign");
analyze.setText("Analyze");
stop.setEnabled(false);
stop.setText("Stop");
append(name, {40, 0});
append(value, {100, 0}, 5);
append(assign, {80, 0}, 5);
append(spacer, {~0, 0});
append(analyze, {80, 0}, 5);
append(stop, {80, 0});
}
TimingSettings::TimingSettings() {
title.setFont(application->titleFont);
title.setText("Audiovisual Synchronization");
videoAdjust.name.setText("Video:");
videoAdjust.value.setText({config->timing.video});
audioAdjust.name.setText("Audio:");
audioAdjust.value.setText({config->timing.audio});
append(title, {~0, 0}, 5);
append(videoAdjust, {~0, 0}, 5);
append(audioAdjust, {~0, 0}, 5);
videoAdjust.value.onChange = [&] { videoAdjust.assign.setEnabled(true); };
audioAdjust.value.onChange = [&] { audioAdjust.assign.setEnabled(true); };
videoAdjust.assign.onActivate = [&] {
config->timing.video = atof(videoAdjust.value.text());
videoAdjust.value.setText({config->timing.video});
videoAdjust.assign.setEnabled(false);
utility->synchronizeDSP();
};
audioAdjust.assign.onActivate = [&] {
config->timing.audio = atof(audioAdjust.value.text());
audioAdjust.value.setText({config->timing.audio});
audioAdjust.assign.setEnabled(false);
utility->synchronizeDSP();
};
videoAdjust.analyze.onActivate = {&TimingSettings::analyzeVideoFrequency, this};
audioAdjust.analyze.onActivate = {&TimingSettings::analyzeAudioFrequency, this};
videoAdjust.stop.onActivate = audioAdjust.stop.onActivate = [&] { analysis.stop = true; };
}
void TimingSettings::analyzeVideoFrequency() {
video.set(Video::Synchronize, true);
audio.set(Audio::Synchronize, false);
videoAdjust.stop.setEnabled(true);
analyzeStart();
do {
uint32_t *output;
unsigned pitch;
if(video.lock(output, pitch, 16, 16)) {
pitch >>= 2;
for(unsigned y = 0; y < 16; y++) memset(output + y * pitch, 0, 4 * 16);
video.unlock();
video.refresh();
}
} while(analyzeTick("Video"));
analyzeStop();
}
void TimingSettings::analyzeAudioFrequency() {
video.set(Video::Synchronize, false);
audio.set(Audio::Synchronize, true);
audioAdjust.stop.setEnabled(true);
analyzeStart();
do {
audio.sample(0, 0);
} while(analyzeTick("Audio"));
analyzeStop();
}
void TimingSettings::analyzeStart() {
audio.clear();
settings->setModal(true);
settings->panelList.setEnabled(false);
videoAdjust.analyze.setEnabled(false);
audioAdjust.analyze.setEnabled(false);
settings->setStatusText("Initiailizing ...");
OS::processEvents();
analysis.stop = false;
analysis.seconds = 0;
analysis.counter = 0;
analysis.sample.reset();
analysis.systemTime = time(0);
}
bool TimingSettings::analyzeTick(const string &type) {
analysis.counter++;
time_t systemTime = time(0);
if(systemTime > analysis.systemTime) {
analysis.systemTime = systemTime;
OS::processEvents();
if(analysis.seconds < 3) {
analysis.seconds++;
} else {
analysis.sample.append(analysis.counter);
uintmax_t sum = 0;
for(auto &point : analysis.sample) sum += point;
settings->setStatusText({
type, " sample rate: ", (double)sum / analysis.sample.size(), "hz",
" (", analysis.sample.size(), " sample points)"
});
}
analysis.counter = 0;
}
return !analysis.stop;
}
void TimingSettings::analyzeStop() {
video.set(Video::Synchronize, config->video.synchronize);
audio.set(Audio::Synchronize, config->audio.synchronize);
settings->panelList.setEnabled(true);
videoAdjust.analyze.setEnabled(true);
audioAdjust.analyze.setEnabled(true);
videoAdjust.stop.setEnabled(false);
audioAdjust.stop.setEnabled(false);
settings->setModal(false);
}

View File

@ -0,0 +1,36 @@
struct TimingAdjustment : HorizontalLayout {
Label name;
LineEdit value;
Button assign;
Widget spacer;
Button analyze;
Button stop;
TimingAdjustment();
};
struct TimingSettings : SettingsLayout {
Label title;
TimingAdjustment videoAdjust;
TimingAdjustment audioAdjust;
void analyzeVideoFrequency();
void analyzeAudioFrequency();
void analyzeStart();
bool analyzeTick(const string &type);
void analyzeStop();
TimingSettings();
private:
struct Analysis {
bool stop;
unsigned seconds;
unsigned counter;
vector<unsigned> sample;
time_t systemTime;
} analysis;
};
extern TimingSettings *timingSettings;

View File

@ -153,7 +153,7 @@ bool CheatEditor::save(const string &filename) {
}
if(lastSave == -1) {
unlink(filename);
file::remove(filename);
return true;
}

View File

@ -97,7 +97,7 @@ bool StateManager::save(const string &filename, unsigned revision) {
bool hasSave = false;
for(auto &slot : this->slot) hasSave |= slot.capacity() > 0;
if(hasSave == false) {
unlink(filename);
file::remove(filename);
return true;
}

View File

@ -67,7 +67,7 @@ void Utility::saveMemory() {
system().save(memory.id, fs);
}
mkdir(string{pathname[0], "bsnes/"}, 0755);
directory::create({pathname[0], "bsnes/"});
cheatEditor->save({pathname[0], "cheats.xml"});
stateManager->save({pathname[0], "bsnes/states.bsa"}, 1);
}
@ -100,7 +100,7 @@ void Utility::load() {
loadMemory();
system().updatePalette();
dspaudio.setFrequency(system().information.frequency);
synchronizeDSP();
resize();
cheatEditor->synchronize();
@ -118,15 +118,18 @@ void Utility::unload() {
stateManager->reset();
setInterface(nullptr);
presentation->setTitle({Emulator::Name, " v", Emulator::Version});
video.clear();
audio.clear();
presentation->setTitle({Emulator::Name, " v", Emulator::Version});
cheatEditor->synchronize();
stateManager->synchronize();
}
void Utility::saveState(unsigned slot) {
if(application->active == nullptr) return;
serializer s = system().serialize();
if(s.size() == 0) return;
mkdir(string{pathname[0], "bsnes/"}, 0755);
directory::create({pathname[0], "bsnes/"});
if(file::write({pathname[0], "bsnes/state-", slot, ".bsa"}, s.data(), s.size()) == false);
showMessage({"Save to slot ", slot});
}
@ -140,6 +143,20 @@ void Utility::loadState(unsigned slot) {
showMessage({"Loaded from slot ", slot});
}
void Utility::synchronizeDSP() {
if(application->active == nullptr) return;
if(config->video.synchronize == false) {
return dspaudio.setFrequency(system().audioFrequency());
}
double inputRatio = system().audioFrequency() / system().videoFrequency();
double outputRatio = config->timing.audio / config->timing.video;
double frequency = inputRatio / outputRatio * config->audio.frequency;
dspaudio.setFrequency(frequency);
}
void Utility::synchronizeRuby() {
video.set(Video::Synchronize, config->video.synchronize);
audio.set(Audio::Synchronize, config->audio.synchronize);
@ -155,6 +172,22 @@ void Utility::synchronizeRuby() {
dspaudio.setVolume(config->audio.mute ? 0.0 : config->audio.volume * 0.01);
}
void Utility::updateShader() {
if(config->video.shader == "None") {
video.set(Video::Shader, (const char*)"");
video.set(Video::Filter, 0u);
return;
}
if(config->video.shader == "Blur") {
video.set(Video::Shader, (const char*)"");
video.set(Video::Filter, 1u);
return;
}
string data;
data.readfile(config->video.shader);
video.set(Video::Shader, (const char*)data);
}
void Utility::resize(bool resizeWindow) {
if(application->active == nullptr) return;
Geometry geometry = presentation->geometry();

View File

@ -15,7 +15,9 @@ struct Utility {
void saveState(unsigned slot);
void loadState(unsigned slot);
void synchronizeDSP();
void synchronizeRuby();
void updateShader();
void resize(bool resizeWindow = false);
void toggleFullScreen();

View File

@ -1,91 +0,0 @@
processors := arm gsu hg51b lr35902 r6502 r65816 spc700 upd96050
include processor/Makefile
include $(fc)/Makefile
include $(sfc)/Makefile
include $(gb)/Makefile
include $(gba)/Makefile
name := bsnes
ui_objects := ui-main ui-config ui-interface ui-input ui-utility
ui_objects += ui-window ui-general ui-settings ui-tools
ui_objects += phoenix ruby
ui_objects += $(if $(call streq,$(platform),win),resource)
# platform
ifeq ($(platform),x)
ruby := video.glx video.xv video.sdl
ruby += audio.alsa audio.openal audio.oss audio.pulseaudio audio.pulseaudiosimple audio.ao
ruby += input.sdl input.x
else ifeq ($(platform),osx)
ruby :=
ruby += audio.openal
ruby += input.carbon
else ifeq ($(platform),win)
ruby := video.direct3d video.wgl video.directdraw video.gdi
ruby += audio.directsound audio.xaudio2
ruby += input.rawinput input.directinput
endif
# phoenix
include phoenix/Makefile
link += $(phoenixlink)
# ruby
include ruby/Makefile
link += $(rubylink)
# rules
objects := $(ui_objects) $(objects)
objects := $(patsubst %,obj/%.o,$(objects))
obj/ui-main.o: $(ui)/main.cpp $(call rwildcard,$(ui)/)
obj/ui-config.o: $(ui)/config/config.cpp $(call rwildcard,$(ui)/)
obj/ui-interface.o: $(ui)/interface/interface.cpp $(call rwildcard,$(ui)/)
obj/ui-input.o: $(ui)/input/input.cpp $(call rwildcard,$(ui)/)
obj/ui-utility.o: $(ui)/utility/utility.cpp $(call rwildcard,$(ui)/)
obj/ui-window.o: $(ui)/window/window.cpp $(call rwildcard,$(ui)/)
obj/ui-general.o: $(ui)/general/general.cpp $(call rwildcard,$(ui)/)
obj/ui-settings.o: $(ui)/settings/settings.cpp $(call rwildcard,$(ui)/)
obj/ui-tools.o: $(ui)/tools/tools.cpp $(call rwildcard,$(ui)/)
obj/ruby.o: ruby/ruby.cpp $(call rwildcard,ruby/*)
$(call compile,$(rubyflags))
obj/phoenix.o: phoenix/phoenix.cpp $(call rwildcard,phoenix/*)
$(call compile,$(phoenixflags))
obj/resource.o: $(ui)/resource.rc
# windres --target=pe-i386 $(ui)/resource.rc obj/resource.o
windres $(ui)/resource.rc obj/resource.o
# targets
build: $(objects)
ifeq ($(platform),osx)
test -d ../$(name).app || mkdir -p ../$(name).app/Contents/MacOS
$(strip $(cpp) -o ../$(name).app/Contents/MacOS/$(name) $(objects) $(link))
else
$(strip $(cpp) -o out/$(name) $(objects) $(link))
endif
install:
ifeq ($(USER),root)
@echo Please do not run make install as root.
@echo The installer needs to know your home directory to install important files.
else ifeq ($(platform),x)
sudo install -D -m 755 out/$(name) $(DESTDIR)$(prefix)/bin/$(name)
sudo install -D -m 644 data/$(name).png $(DESTDIR)$(prefix)/share/pixmaps/$(name).png
sudo install -D -m 644 data/$(name).desktop $(DESTDIR)$(prefix)/share/applications/$(name).desktop
mkdir -p ~/.config/$(name)
cp -R profile/* ~/.config/$(name)
cp data/cheats.xml ~/.config/$(name)/cheats.xml
chmod -R 777 ~/.config/$(name)
endif
uninstall:
ifeq ($(platform),x)
sudo rm $(DESTDIR)$(prefix)/bin/$(name)
sudo rm $(DESTDIR)$(prefix)/share/pixmaps/$(name).png
sudo rm $(DESTDIR)$(prefix)/share/applications/$(name).desktop
endif

View File

@ -1,66 +0,0 @@
#include <fc/fc.hpp>
#include <sfc/sfc.hpp>
#include <gb/gb.hpp>
#include <gba/gba.hpp>
namespace FC = Famicom;
namespace SFC = SuperFamicom;
namespace GB = GameBoy;
namespace GBA = GameBoyAdvance;
#include <nall/compositor.hpp>
#include <nall/config.hpp>
#include <nall/directory.hpp>
#include <nall/dsp.hpp>
#include <nall/file.hpp>
#include <nall/filemap.hpp>
#include <nall/input.hpp>
#include <nall/bps/patch.hpp>
#include <nall/stream/file.hpp>
#include <nall/stream/memory.hpp>
#include <nall/stream/vector.hpp>
#include <nall/nes/cartridge.hpp>
#include <nall/snes/cartridge.hpp>
#include <nall/gb/cartridge.hpp>
#include <nall/gba/cartridge.hpp>
using namespace nall;
#include <phoenix/phoenix.hpp>
using namespace phoenix;
#include <ruby/ruby.hpp>
using namespace ruby;
#include "config/config.hpp"
#include "interface/interface.hpp"
#include "input/input.hpp"
#include "utility/utility.hpp"
#include "window/window.hpp"
#include "general/general.hpp"
#include "settings/settings.hpp"
#include "tools/tools.hpp"
struct Application {
bool quit;
bool pause;
bool autopause;
bool compositionEnable;
unsigned depth;
string basepath;
string userpath;
string path(const string &filename);
string title;
string normalFont;
string boldFont;
string titleFont;
string monospaceFont;
void run();
Application(int argc, char **argv);
~Application();
};
extern Application *application;
extern nall::DSP dspaudio;

View File

@ -1,52 +0,0 @@
#include "../base.hpp"
Config *config = nullptr;
Config::Config() {
append(video.driver = "", "Video::Driver");
append(video.filter = "None", "Video::Filter");
append(video.shader = "Blur", "Video::Shader");
append(video.synchronize = true, "Video::Synchronize");
append(video.correctAspectRatio = true, "Video::CorrectAspectRatio");
append(video.maskOverscan = false, "Video::MaskOverscan");
append(video.maskOverscanHorizontal = 8, "Video::MaskOverscanHorizontal");
append(video.maskOverscanVertical = 8, "Video::MaskOverscanVertical");
append(video.brightness = 100, "Video::Brightness");
append(video.contrast = 100, "Video::Contrast");
append(video.gamma = 50, "Video::Gamma");
append(video.fullScreenMode = 0, "Video::FullScreenMode");
append(video.startFullScreen = false, "Video::StartFullScreen");
append(video.compositionMode = 0, "Video::CompositionMode");
append(audio.driver = "", "Audio::Driver");
append(audio.synchronize = true, "Audio::Synchronize");
append(audio.mute = false, "Audio::Mute");
append(audio.volume = 100, "Audio::Volume");
append(audio.latency = 60, "Audio::Latency");
append(audio.resampler = "sinc", "Audio::Resampler");
append(audio.frequency = 48000, "Audio::Frequency::Native");
append(audio.frequencyNES = 1789772, "Audio::Frequency::NES");
append(audio.frequencySNES = 32000, "Audio::Frequency::SNES");
append(audio.frequencyGB = 4194304, "Audio::Frequency::GB");
append(audio.frequencyGBA = 32768, "Audio::Frequency::GBA");
append(input.driver = "", "Input::Driver");
append(input.focusPolicy = 1, "Input::FocusPolicy");
append(nes.controllerPort1Device = 1, "Famicom::Controller::Port1");
append(nes.controllerPort2Device = 0, "Famicom::Controller::Port2");
append(snes.controllerPort1Device = 1, "SuperFamciom::Controller::Port1");
append(snes.controllerPort2Device = 0, "SuperFamicom::Controller::Port2");
load(application->path("settings.cfg"));
save(application->path("settings.cfg"));
}
Config::~Config() {
save(application->path("settings.cfg"));
}

View File

@ -1,59 +0,0 @@
struct Config : public configuration {
struct Video {
string driver;
string filter;
string shader;
bool synchronize;
bool correctAspectRatio;
bool maskOverscan;
unsigned maskOverscanHorizontal;
unsigned maskOverscanVertical;
unsigned brightness;
unsigned contrast;
unsigned gamma;
unsigned fullScreenMode;
unsigned compositionMode;
bool startFullScreen;
} video;
struct Audio {
string driver;
bool synchronize;
bool mute;
unsigned volume;
unsigned latency;
string resampler;
unsigned frequency;
unsigned frequencyNES;
unsigned frequencySNES;
unsigned frequencyGB;
unsigned frequencyGBA;
} audio;
struct Input {
string driver;
unsigned focusPolicy;
} input;
struct NES {
unsigned controllerPort1Device;
unsigned controllerPort2Device;
} nes;
struct SNES {
unsigned controllerPort1Device;
unsigned controllerPort2Device;
} snes;
Config();
~Config();
};
extern Config *config;

View File

@ -1,69 +0,0 @@
DipSwitches *dipSwitches = nullptr;
DipSwitch::DipSwitch() {
append(name, {~0, 0}, 5);
append(value, {~0, 0}, 0);
}
DipSwitches::DipSwitches() {
setTitle("DIP Switches");
layout.setMargin(5);
acceptButton.setText("Accept");
append(layout);
for(unsigned n = 0; n < 8; n++)
layout.append(dip[n], {~0, 0}, 5);
layout.append(controlLayout, {~0, 0}, 5);
controlLayout.append(spacer, {~0, 0}, 0);
controlLayout.append(acceptButton, { 0, 0}, 0);
setGeometry({128, 128, 400, layout.minimumGeometry().height});
windowManager->append(this, "DipSwitches");
acceptButton.onActivate = { &DipSwitches::accept, this };
}
void DipSwitches::load() {
if(interface->mode() != Interface::Mode::SFC || SFC::cartridge.has_nss_dip() == false) return;
application->pause = true;
auto info = SFC::cartridge.information.nss;
unsigned count = info.setting.size();
for(unsigned n = 0; n < min(8, count); n++) {
dip[n].setEnabled(true);
dip[n].name.setText(info.setting[n]);
dip[n].value.reset();
for(unsigned z = 0; z < min(16, info.option[n].size()); z++) {
lstring part;
part.split<1>(":", info.option[n][z]);
values[n][z] = hex(part[0]);
dip[n].value.append(part[1]);
}
}
for(unsigned n = count; n < 8; n++) {
dip[n].setEnabled(false);
dip[n].name.setText("(unused)");
dip[n].value.reset();
dip[n].value.append("(unused)");
}
acceptButton.setFocused();
setVisible();
}
void DipSwitches::accept() {
auto info = SFC::cartridge.information.nss;
unsigned count = info.setting.size();
unsigned result = 0x0000;
for(unsigned n = 0; n < min(8, count); n++) {
result |= values[n][dip[n].value.selection()];
}
setVisible(false);
SFC::nss.set_dip(result);
application->pause = false;
}

View File

@ -1,141 +0,0 @@
FileBrowser *fileBrowser = nullptr;
FileBrowser::FileBrowser() {
setGeometry({ 128, 128, 640, 400 });
windowManager->append(this, "FileBrowser");
layout.setMargin(5);
pathBrowse.setText("Browse ...");
pathUp.setText("..");
openButton.setText("Open");
append(layout);
layout.append(pathLayout, { ~0, 0 }, 5);
pathLayout.append(pathEdit, { ~0, 0 }, 5);
pathLayout.append(pathBrowse, { 0, 0 }, 5);
pathLayout.append(pathUp, { 0, 0 });
layout.append(fileList, { ~0, ~0 }, 5);
layout.append(controlLayout, { ~0, 0 });
controlLayout.append(filterLabel, { ~0, 0 }, 5);
controlLayout.append(openButton, { 80, 0 });
pathEdit.onActivate = [&] {
string path = pathEdit.text();
path.transform("\\", "/");
if(path.endswith("/") == false) path.append("/");
setPath(path);
};
pathBrowse.onActivate = [&] {
string path = DialogWindow::folderSelect(*this, mode->path);
if(path != "") setPath(path);
};
pathUp.onActivate = [&] {
if(mode->path == "/") return;
string path = mode->path;
path.rtrim<1>("/");
path = dir(path);
setPath(path);
};
fileList.onChange = { &FileBrowser::synchronize, this };
fileList.onActivate = openButton.onActivate = { &FileBrowser::fileListActivate, this };
filterModes.append({ "Default", "", { "*" } });
filterModes.append({ "Famicom", "", { "*.fc", "*.nes" } });
filterModes.append({ "SuperFamicom", "", { "*.sfc" } });
filterModes.append({ "GameBoy", "", { "*.gb", "*.gbb" } });
filterModes.append({ "GameBoyColor", "", { "*.gbc", "*.gbb" } });
filterModes.append({ "GameBoyAdvance", "", { "*.gba" } });
filterModes.append({ "Satellaview", "", { "*.bs" } });
filterModes.append({ "SufamiTurbo", "", { "*.st" } });
mode = &filterModes[Mode::Default];
for(auto &mode : filterModes) config.attach(mode.path, mode.name);
config.load(application->path("paths.cfg"));
config.save(application->path("paths.cfg"));
synchronize();
}
void FileBrowser::synchronize() {
openButton.setEnabled(fileList.selected());
}
FileBrowser::~FileBrowser() {
config.save(application->path("paths.cfg"));
}
void FileBrowser::open(const string &title, unsigned requestedMode, function<void (string)> requestedCallback) {
callback = requestedCallback;
if(mode == &filterModes[requestedMode]) {
setVisible();
fileList.setFocused();
return;
}
mode = &filterModes[requestedMode];
setTitle(title);
setPath(mode->path);
string filterText = "Files of type: ";
for(auto &filter : mode->filter) filterText.append(filter, ", ");
filterText.trim<1>(", ");
filterLabel.setText(filterText);
setVisible();
fileList.setFocused();
}
void FileBrowser::setPath(const string &path) {
mode->path = path;
if(mode->path == "") mode->path = application->basepath;
pathEdit.setText(mode->path);
fileList.reset();
fileNameList.reset();
lstring contentsList = directory::contents(path);
for(auto &fileName : contentsList) {
if(fileName.endswith("/")) {
fileNameList.append(fileName);
} else for(auto &filter : mode->filter) {
if(fileName.wildcard(filter)) {
fileNameList.append(fileName);
break;
}
}
}
for(auto &fileName : fileNameList) fileList.append(fileName);
fileList.setSelection(0);
fileList.setFocused();
synchronize();
}
void FileBrowser::fileListActivate() {
unsigned selection = fileList.selection();
string fileName = fileNameList[selection];
if(fileName.endswith("/")) {
if(loadFolder({ mode->path, fileName })) return;
return setPath({ mode->path, fileName });
}
loadFile({ mode->path, fileName });
}
bool FileBrowser::loadFolder(const string &requestedPath) {
bool accept = false;
string path = requestedPath;
path.rtrim<1>("/");
for(auto &filter : mode->filter) {
if(path.wildcard(filter)) accept = true;
}
if(accept == false) return false;
loadFile(requestedPath);
return true;
}
void FileBrowser::loadFile(const string &filename) {
setVisible(false);
if(callback) callback(filename);
}

View File

@ -1,37 +0,0 @@
struct FileBrowser : Window {
VerticalLayout layout;
HorizontalLayout pathLayout;
LineEdit pathEdit;
Button pathBrowse;
Button pathUp;
ListView fileList;
HorizontalLayout controlLayout;
Label filterLabel;
Button openButton;
struct Mode { enum : unsigned { Default, NES, SNES, GameBoy, GameBoyColor, GameBoyAdvance, Satellaview, SufamiTurbo }; };
void open(const string &title, unsigned mode, function<void (string)> callback);
FileBrowser();
~FileBrowser();
private:
configuration config;
struct FilterMode {
string name;
string path;
lstring filter;
} *mode;
vector<FilterMode> filterModes;
lstring fileNameList;
function<void (string)> callback;
void synchronize();
void setPath(const string &path);
void fileListActivate();
bool loadFolder(const string &path);
void loadFile(const string &filename);
};
extern FileBrowser *fileBrowser;

View File

@ -1,5 +0,0 @@
#include "../base.hpp"
#include "main-window.cpp"
#include "file-browser.cpp"
#include "dip-switches.cpp"
#include "information-window.cpp"

View File

@ -1,4 +0,0 @@
#include "main-window.hpp"
#include "file-browser.hpp"
#include "dip-switches.hpp"
#include "information-window.hpp"

View File

@ -1,43 +0,0 @@
InformationWindow *informationWindow = nullptr;
InformationWindow::InformationWindow() {
setTitle("Information");
layout.setMargin(5);
markup.setFont(application->monospaceFont);
markupXML.setChecked();
markupXML.setText("Show original XML markup");
append(layout);
layout.append(markup, {~0, ~0}, 5);
layout.append(markupXML, {~0, 0});
setGeometry({128, 128, 600, 360});
windowManager->append(this, "InformationWindow");
markupXML.onToggle = { &InformationWindow::update, this };
}
void InformationWindow::update() {
string markupData = interface->markup();
if(markupXML.checked()) {
markup.setText(markupData);
return;
}
XML::Document document(markupData);
markupData = "";
parse(document, markupData, 0);
markup.setText(markupData);
}
void InformationWindow::parse(XML::Node &header, string &output, unsigned depth) {
for(auto &node : header) {
if(node.name.beginswith("?")) continue;
for(unsigned n = 0; n < depth; n++) output.append(" ");
output.append(node.name);
if(node.children.size() == 0 && !node.data.empty()) output.append(": ", node.data);
output.append("\n");
parse(node, output, depth + 1);
}
}

View File

@ -1,11 +0,0 @@
struct InformationWindow : Window {
VerticalLayout layout;
TextEdit markup;
CheckBox markupXML;
void update();
void parse(XML::Node &node, string &output, unsigned depth);
InformationWindow();
};
extern InformationWindow *informationWindow;

View File

@ -1,446 +0,0 @@
MainWindow *mainWindow = nullptr;
MainWindow::MainWindow() {
setTitle(application->title);
setGeometry({ 256, 256, 626, 480 });
setBackgroundColor({ 0, 0, 0 });
windowManager->append(this, "MainWindow");
cartridgeMenu.setText("&Load");
cartridgeLoadNES.setText("&Famicom ...");
cartridgeLoadSNES.setText("&Super Famicom ...");
cartridgeLoadGameBoy.setText("&Game Boy ...");
cartridgeLoadGameBoyColor.setText("Game Boy &Color ...");
cartridgeLoadGameBoyAdvance.setText("Game Boy &Advance ...");
cartridgeLoadSuperGameBoy.setText("Super Game Boy ...");
cartridgeLoadSatellaview.setText("BS-X Satellaview ...");
cartridgeLoadSufamiTurbo.setText("Sufami Turbo ...");
nesMenu.setText("&Famicom");
nesPower.setText("&Power Cycle");
nesReset.setText("&Reset");
nesPort1.setText("Controller Port &1");
nesPort1Device[0].setText("None");
nesPort1Device[1].setText("Gamepad");
RadioItem::group(nesPort1Device[0], nesPort1Device[1]);
nesPort1Device[config->nes.controllerPort1Device].setChecked();
nesPort2.setText("Controller Port &2");
nesPort2Device[0].setText("None");
nesPort2Device[1].setText("Gamepad");
RadioItem::group(nesPort2Device[0], nesPort2Device[1]);
nesPort2Device[config->nes.controllerPort2Device].setChecked();
nesCartridgeUnload.setText("&Unload Cartridge");
snesMenu.setText("&Super Famicom");
snesPower.setText("&Power Cycle");
snesReset.setText("&Reset");
snesPort1.setText("Controller Port &1");
snesPort1Device[0].setText("None");
snesPort1Device[1].setText("Gamepad");
snesPort1Device[2].setText("Multitap");
snesPort1Device[3].setText("Mouse");
snesPort1Device[4].setText("Serial USART");
RadioItem::group(snesPort1Device[0], snesPort1Device[1], snesPort1Device[2], snesPort1Device[3],
snesPort1Device[4]);
snesPort1Device[config->snes.controllerPort1Device].setChecked();
snesPort2.setText("Controller Port &2");
snesPort2Device[0].setText("None");
snesPort2Device[1].setText("Gamepad");
snesPort2Device[2].setText("Multitap");
snesPort2Device[3].setText("Mouse");
snesPort2Device[4].setText("Super Scope");
snesPort2Device[5].setText("Justifier");
snesPort2Device[6].setText("Dual Justifiers");
snesPort2Device[7].setText("Serial USART");
RadioItem::group(snesPort2Device[0], snesPort2Device[1], snesPort2Device[2], snesPort2Device[3],
snesPort2Device[4], snesPort2Device[5], snesPort2Device[6], snesPort2Device[7]);
snesPort2Device[config->snes.controllerPort2Device].setChecked();
snesCartridgeUnload.setText("&Unload Cartridge");
gameBoyMenu.setText("&Game Boy");
gameBoyPower.setText("&Power Cycle");
gameBoyCartridgeUnload.setText("&Unload Cartridge");
gameBoyAdvanceMenu.setText("&Game Boy Advance");
gameBoyAdvancePower.setText("&Power Cycle");
gameBoyAdvanceCartridgeUnload.setText("&Unload Cartridge");
settingsMenu.setText("S&ettings");
settingsVideoFilter.setText("Video &Filter");
settingsVideoFilterNone.setText("None");
setupVideoFilters();
settingsVideoShader.setText("Video &Shader");
settingsVideoShaderNone.setText("None");
settingsVideoShaderBlur.setText("Blur");
setupVideoShaders();
settingsSynchronizeVideo.setText("Synchronize &Video");
settingsSynchronizeVideo.setChecked(config->video.synchronize);
settingsSynchronizeAudio.setText("Synchronize &Audio");
settingsSynchronizeAudio.setChecked(config->audio.synchronize);
settingsCorrectAspectRatio.setText("Correct Aspect &Ratio");
settingsCorrectAspectRatio.setChecked(config->video.correctAspectRatio);
settingsMaskOverscan.setText("Mask &Overscan");
settingsMaskOverscan.setChecked(config->video.maskOverscan);
settingsMuteAudio.setText("&Mute Audio");
settingsMuteAudio.setChecked(config->audio.mute);
settingsConfiguration.setText("&Configuration ...");
toolsMenu.setText("&Tools");
toolsStateSave.setText("&Save State");
toolsStateSave1.setText("Slot &1");
toolsStateSave2.setText("Slot &2");
toolsStateSave3.setText("Slot &3");
toolsStateSave4.setText("Slot &4");
toolsStateSave5.setText("Slot &5");
toolsStateLoad.setText("&Load State");
toolsStateLoad1.setText("Slot &1");
toolsStateLoad2.setText("Slot &2");
toolsStateLoad3.setText("Slot &3");
toolsStateLoad4.setText("Slot &4");
toolsStateLoad5.setText("Slot &5");
toolsInformationWindow.setText("&Information ...");
toolsShrinkWindow.setText("Shrink &Window");
toolsCheatEditor.setText("&Cheat Editor ...");
toolsStateManager.setText("State &Manager ...");
append(cartridgeMenu);
cartridgeMenu.append(cartridgeLoadNES);
cartridgeMenu.append(cartridgeLoadSNES);
cartridgeMenu.append(cartridgeLoadGameBoy);
cartridgeMenu.append(cartridgeLoadGameBoyColor);
cartridgeMenu.append(cartridgeLoadGameBoyAdvance);
cartridgeMenu.append(cartridgeSeparator);
cartridgeMenu.append(cartridgeLoadSuperGameBoy);
cartridgeMenu.append(cartridgeLoadSatellaview);
cartridgeMenu.append(cartridgeLoadSufamiTurbo);
append(nesMenu);
nesMenu.append(nesPower);
nesMenu.append(nesReset);
nesMenu.append(nesSeparator1);
nesMenu.append(nesPort1);
nesPort1.append(nesPort1Device[0]);
nesPort1.append(nesPort1Device[1]);
nesMenu.append(nesPort2);
nesPort2.append(nesPort2Device[0]);
nesPort2.append(nesPort2Device[1]);
nesMenu.append(nesSeparator2);
nesMenu.append(nesCartridgeUnload);
append(snesMenu);
snesMenu.append(snesPower);
snesMenu.append(snesReset);
snesMenu.append(snesSeparator1);
snesMenu.append(snesPort1);
for(auto &item : snesPort1Device) snesPort1.append(item);
snesMenu.append(snesPort2);
for(auto &item : snesPort2Device) snesPort2.append(item);
snesMenu.append(snesSeparator2);
snesMenu.append(snesCartridgeUnload);
append(gameBoyMenu);
gameBoyMenu.append(gameBoyPower);
gameBoyMenu.append(gameBoySeparator);
gameBoyMenu.append(gameBoyCartridgeUnload);
append(gameBoyAdvanceMenu);
gameBoyAdvanceMenu.append(gameBoyAdvancePower);
gameBoyAdvanceMenu.append(gameBoyAdvanceSeparator);
gameBoyAdvanceMenu.append(gameBoyAdvanceCartridgeUnload);
append(settingsMenu);
settingsMenu.append(settingsVideoFilter);
settingsVideoFilter.append(settingsVideoFilterNone);
if(videoFilterName.size())
settingsVideoFilter.append(settingsVideoFilterSeparator);
for(unsigned n = 0; n < videoFilterName.size(); n++)
settingsVideoFilter.append(settingsVideoFilterList[n]);
settingsMenu.append(settingsVideoShader);
settingsVideoShader.append(settingsVideoShaderNone);
settingsVideoShader.append(settingsVideoShaderBlur);
if(videoShaderName.size())
settingsVideoShader.append(settingsVideoShaderSeparator);
for(unsigned n = 0; n < videoShaderName.size(); n++)
settingsVideoShader.append(settingsVideoShaderList[n]);
settingsMenu.append(settingsSeparator1);
settingsMenu.append(settingsSynchronizeVideo);
settingsMenu.append(settingsSynchronizeAudio);
settingsMenu.append(settingsSeparator2);
settingsMenu.append(settingsCorrectAspectRatio);
settingsMenu.append(settingsMaskOverscan);
settingsMenu.append(settingsMuteAudio);
settingsMenu.append(settingsSeparator3);
settingsMenu.append(settingsConfiguration);
append(toolsMenu);
toolsMenu.append(toolsStateSave);
toolsStateSave.append(toolsStateSave1);
toolsStateSave.append(toolsStateSave2);
toolsStateSave.append(toolsStateSave3);
toolsStateSave.append(toolsStateSave4);
toolsStateSave.append(toolsStateSave5);
toolsMenu.append(toolsStateLoad);
toolsStateLoad.append(toolsStateLoad1);
toolsStateLoad.append(toolsStateLoad2);
toolsStateLoad.append(toolsStateLoad3);
toolsStateLoad.append(toolsStateLoad4);
toolsStateLoad.append(toolsStateLoad5);
toolsMenu.append(toolsSeparator);
toolsMenu.append(toolsInformationWindow);
toolsMenu.append(toolsShrinkWindow);
toolsMenu.append(toolsCheatEditor);
toolsMenu.append(toolsStateManager);
setMenuVisible();
setStatusText("No cartridge loaded");
setStatusVisible();
layout.append(viewport, { 0, 0, 512, 480 });
append(layout);
onClose = [&] { application->quit = true; };
onSize = [&] { utility->resizeMainWindow(); };
cartridgeLoadNES.onActivate = [&] {
fileBrowser->open("Load Cartridge - Famicom", FileBrowser::Mode::NES, [](string filename) {
interface->nes.loadCartridge(filename);
});
};
cartridgeLoadSNES.onActivate = [&] {
fileBrowser->open("Load Cartridge - Super Famicom", FileBrowser::Mode::SNES, [](string filename) {
string filedata;
filedata.readfile({dir(filename),"manifest.xml"});
XML::Document document(filedata);
if(document["cartridge"]["bsx"]["slot"].exists()
&& MessageWindow::question(*mainWindow, "Load BS-X Satellaview data pack?") == MessageWindow::Response::Yes) {
mainWindow->filename = filename;
fileBrowser->open("Load Cartridge - BS-X Satellaview", FileBrowser::Mode::Satellaview, [](string filename) {
interface->snes.loadSatellaviewSlottedCartridge(mainWindow->filename, filename);
});
} else {
interface->snes.loadCartridge(filename);
}
});
};
cartridgeLoadGameBoy.onActivate = [&] {
fileBrowser->open("Load Cartridge - Game Boy", FileBrowser::Mode::GameBoy, [](string filename) {
interface->gb.loadCartridge(GB::System::Revision::GameBoy, filename);
});
};
cartridgeLoadGameBoyColor.onActivate = [&] {
fileBrowser->open("Load Cartridge - Game Boy Color", FileBrowser::Mode::GameBoyColor, [](string filename) {
interface->gb.loadCartridge(GB::System::Revision::GameBoyColor, filename);
});
};
cartridgeLoadGameBoyAdvance.onActivate = [&] {
fileBrowser->open("Load Cartridge - Game Boy Advance", FileBrowser::Mode::GameBoyAdvance, [](string filename) {
interface->gba.loadCartridge(filename);
});
};
cartridgeLoadSuperGameBoy.onActivate = [&] {
fileBrowser->open("Load Cartridge - Super Game Boy", FileBrowser::Mode::GameBoy, [](string filename) {
interface->snes.loadSuperGameBoyCartridge(application->path("Super Game Boy.sfc/"), filename);
});
};
cartridgeLoadSatellaview.onActivate = [&] {
fileBrowser->open("Load Cartridge - BS-X Satellaview", FileBrowser::Mode::Satellaview, [](string filename) {
interface->snes.loadSatellaviewCartridge(application->path("BS-X Satellaview.sfc/"), filename);
});
};
cartridgeLoadSufamiTurbo.onActivate = [&] {
fileBrowser->open("Load Cartridge - Sufami Turbo", FileBrowser::Mode::SufamiTurbo, [](string filename) {
string filedata;
filedata.readfile({dir(filename),"manifest.xml"});
XML::Document document(filedata);
if(document["cartridge"]["linkable"].data == "true"
&& MessageWindow::question(*mainWindow, "Load linkable cartridge?") == MessageWindow::Response::Yes) {
mainWindow->filename = filename;
fileBrowser->open("Load Cartridge - Sufami Turbo", FileBrowser::Mode::SufamiTurbo, [](string filename) {
if(mainWindow->filename == filename) {
MessageWindow::critical(*mainWindow, "It is physically impossible to have the same cartridge in two slots at the same time.");
} else {
interface->snes.loadSufamiTurboCartridge(application->path("Sufami Turbo.sfc/"), mainWindow->filename, filename);
}
});
} else {
interface->snes.loadSufamiTurboCartridge(application->path("Sufami Turbo.sfc/"), filename, "");
}
});
};
nesPower.onActivate = { &Interface::power, interface };
nesReset.onActivate = { &Interface::reset, interface };
nesPort1Device[0].onActivate = [&] { interface->setController(0, 0); };
nesPort1Device[1].onActivate = [&] { interface->setController(0, 1); };
nesPort2Device[0].onActivate = [&] { interface->setController(1, 0); };
nesPort2Device[1].onActivate = [&] { interface->setController(1, 1); };
nesCartridgeUnload.onActivate = { &Interface::unloadCartridge, interface };
snesPower.onActivate = { &Interface::power, interface };
snesReset.onActivate = { &Interface::reset, interface };
snesPort1Device[0].onActivate = [&] { interface->setController(0, 0); };
snesPort1Device[1].onActivate = [&] { interface->setController(0, 1); };
snesPort1Device[2].onActivate = [&] { interface->setController(0, 2); };
snesPort1Device[3].onActivate = [&] { interface->setController(0, 3); };
snesPort1Device[4].onActivate = [&] { interface->setController(0, 4); };
snesPort2Device[0].onActivate = [&] { interface->setController(1, 0); };
snesPort2Device[1].onActivate = [&] { interface->setController(1, 1); };
snesPort2Device[2].onActivate = [&] { interface->setController(1, 2); };
snesPort2Device[3].onActivate = [&] { interface->setController(1, 3); };
snesPort2Device[4].onActivate = [&] { interface->setController(1, 4); };
snesPort2Device[5].onActivate = [&] { interface->setController(1, 5); };
snesPort2Device[6].onActivate = [&] { interface->setController(1, 6); };
snesPort2Device[7].onActivate = [&] { interface->setController(1, 7); };
snesCartridgeUnload.onActivate = { &Interface::unloadCartridge, interface };
gameBoyPower.onActivate = { &Interface::power, interface };
gameBoyCartridgeUnload.onActivate = { &Interface::unloadCartridge, interface };
gameBoyAdvancePower.onActivate = { &Interface::power, interface };
gameBoyAdvanceCartridgeUnload.onActivate = { &Interface::unloadCartridge, interface };
settingsVideoFilterNone.onActivate = [&] {
config->video.filter = "None";
utility->bindVideoFilter();
};
settingsVideoShaderNone.onActivate = [&] {
config->video.shader = "None";
utility->bindVideoShader();
};
settingsVideoShaderBlur.onActivate = [&] {
config->video.shader = "Blur";
utility->bindVideoShader();
};
settingsSynchronizeVideo.onToggle = [&] {
config->video.synchronize = settingsSynchronizeVideo.checked();
video.set(Video::Synchronize, config->video.synchronize);
};
settingsSynchronizeAudio.onToggle = [&] {
config->audio.synchronize = settingsSynchronizeAudio.checked();
audio.set(Audio::Synchronize, config->audio.synchronize);
};
settingsCorrectAspectRatio.onToggle = [&] {
config->video.correctAspectRatio = settingsCorrectAspectRatio.checked();
utility->resizeMainWindow();
};
settingsMaskOverscan.onToggle = [&] {
config->video.maskOverscan = settingsMaskOverscan.checked();
};
settingsMuteAudio.onToggle = [&] {
config->audio.mute = settingsMuteAudio.checked();
dspaudio.setVolume(config->audio.mute == false ? (double)config->audio.volume / 100.0 : 0.0);
};
settingsConfiguration.onActivate = [&] { settingsWindow->setVisible(); };
toolsStateSave1.onActivate = [&] { interface->saveState(1); };
toolsStateSave2.onActivate = [&] { interface->saveState(2); };
toolsStateSave3.onActivate = [&] { interface->saveState(3); };
toolsStateSave4.onActivate = [&] { interface->saveState(4); };
toolsStateSave5.onActivate = [&] { interface->saveState(5); };
toolsStateLoad1.onActivate = [&] { interface->loadState(1); };
toolsStateLoad2.onActivate = [&] { interface->loadState(2); };
toolsStateLoad3.onActivate = [&] { interface->loadState(3); };
toolsStateLoad4.onActivate = [&] { interface->loadState(4); };
toolsStateLoad5.onActivate = [&] { interface->loadState(5); };
toolsInformationWindow.onActivate = [&] { informationWindow->setVisible(); };
toolsShrinkWindow.onActivate = [&] { utility->resizeMainWindow(true); };
toolsCheatEditor.onActivate = [&] { cheatEditor->setVisible(); };
toolsStateManager.onActivate = [&] { stateManager->setVisible(); };
synchronize();
}
void MainWindow::synchronize() {
bool enable = interface->cartridgeLoaded();
toolsStateSave.setEnabled(enable);
toolsStateLoad.setEnabled(enable);
toolsInformationWindow.setEnabled(enable);
}
void MainWindow::setupVideoFilters() {
string path = { application->basepath, "filters/" };
lstring files = directory::files(path, "*.filter");
if(files.size() == 0) {
path = { application->userpath, "filters/" };
files = directory::files(path, "*.filter");
}
set<RadioItem&> group;
settingsVideoFilterList = new RadioItem[files.size()];
for(unsigned n = 0; n < files.size(); n++) {
string name = files[n];
videoFilterName.append({ path, name });
if(auto position = name.position(".filter")) name[position()] = 0;
settingsVideoFilterList[n].setText(name);
settingsVideoFilterList[n].onActivate = [&, n] {
config->video.filter = videoFilterName[n];
utility->bindVideoFilter();
};
}
group.append(settingsVideoFilterNone);
for(unsigned n = 0; n < files.size(); n++) group.append(settingsVideoFilterList[n]);
RadioItem::group(group);
if(config->video.filter == "None") settingsVideoFilterNone.setChecked();
for(unsigned n = 0; n < files.size(); n++)
if(config->video.filter == videoFilterName[n]) settingsVideoFilterList[n].setChecked();
}
void MainWindow::setupVideoShaders() {
string path = { application->basepath, "shaders/" };
lstring files = directory::files(path, { "*.", config->video.driver, ".shader" });
if(files.size() == 0) {
path = { application->userpath, "shaders/" };
files = directory::files(path, { "*.", config->video.driver, ".shader" });
}
set<RadioItem&> group;
settingsVideoShaderList = new RadioItem[files.size()];
for(unsigned n = 0; n < files.size(); n++) {
string name = files[n];
videoShaderName.append({ path, name });
if(auto position = name.position(string{ ".", config->video.driver, ".shader" })) name[position()] = 0;
settingsVideoShaderList[n].setText(name);
settingsVideoShaderList[n].onActivate = [&, n] {
config->video.shader = videoShaderName[n];
utility->bindVideoShader();
};
}
group.append(settingsVideoShaderNone);
group.append(settingsVideoShaderBlur);
for(unsigned n = 0; n < files.size(); n++) group.append(settingsVideoShaderList[n]);
RadioItem::group(group);
if(config->video.shader == "None") settingsVideoShaderNone.setChecked();
if(config->video.shader == "Blur") settingsVideoShaderBlur.setChecked();
for(unsigned n = 0; n < files.size(); n++)
if(config->video.shader == videoShaderName[n]) settingsVideoShaderList[n].setChecked();
}

View File

@ -1,99 +0,0 @@
struct MainWindow : Window {
FixedLayout layout;
Viewport viewport;
Menu cartridgeMenu;
Item cartridgeLoadSNES;
Item cartridgeLoadNES;
Item cartridgeLoadGameBoy;
Item cartridgeLoadGameBoyColor;
Item cartridgeLoadGameBoyAdvance;
Separator cartridgeSeparator;
Item cartridgeLoadSuperGameBoy;
Item cartridgeLoadSatellaview;
Item cartridgeLoadSufamiTurbo;
Menu nesMenu;
Item nesPower;
Item nesReset;
Separator nesSeparator1;
Menu nesPort1;
RadioItem nesPort1Device[2];
Menu nesPort2;
RadioItem nesPort2Device[2];
Separator nesSeparator2;
Item nesCartridgeUnload;
Menu snesMenu;
Item snesPower;
Item snesReset;
Separator snesSeparator1;
Menu snesPort1;
RadioItem snesPort1Device[5];
Menu snesPort2;
RadioItem snesPort2Device[8];
Separator snesSeparator2;
Item snesCartridgeUnload;
Menu gameBoyMenu;
Item gameBoyPower;
Separator gameBoySeparator;
Item gameBoyCartridgeUnload;
Menu gameBoyAdvanceMenu;
Item gameBoyAdvancePower;
Separator gameBoyAdvanceSeparator;
Item gameBoyAdvanceCartridgeUnload;
Menu settingsMenu;
Menu settingsVideoFilter;
RadioItem settingsVideoFilterNone;
Separator settingsVideoFilterSeparator;
RadioItem *settingsVideoFilterList;
Menu settingsVideoShader;
RadioItem settingsVideoShaderNone;
RadioItem settingsVideoShaderBlur;
Separator settingsVideoShaderSeparator;
RadioItem *settingsVideoShaderList;
Separator settingsSeparator1;
CheckItem settingsSynchronizeVideo;
CheckItem settingsSynchronizeAudio;
Separator settingsSeparator2;
CheckItem settingsCorrectAspectRatio;
CheckItem settingsMaskOverscan;
CheckItem settingsMuteAudio;
Separator settingsSeparator3;
Item settingsConfiguration;
Menu toolsMenu;
Menu toolsStateSave;
Item toolsStateSave1;
Item toolsStateSave2;
Item toolsStateSave3;
Item toolsStateSave4;
Item toolsStateSave5;
Menu toolsStateLoad;
Item toolsStateLoad1;
Item toolsStateLoad2;
Item toolsStateLoad3;
Item toolsStateLoad4;
Item toolsStateLoad5;
Separator toolsSeparator;
Item toolsInformationWindow;
Item toolsShrinkWindow;
Item toolsCheatEditor;
Item toolsStateManager;
void synchronize();
MainWindow();
private:
lstring videoFilterName;
lstring videoShaderName;
string filename;
void setupVideoFilters();
void setupVideoShaders();
};
extern MainWindow *mainWindow;

View File

@ -1,53 +0,0 @@
int16_t GbController::poll(unsigned n) {
switch(n) {
case 0: return up.poll() & !down.poll();
case 1: return down.poll() & !up.poll();
case 2: return left.poll() & !right.poll();
case 3: return right.poll() & !left.poll();
case 4: return b.poll() | bTurbo.poll();
case 5: return a.poll() | aTurbo.poll();
case 6: return select.poll();
case 7: return start.poll();
}
return 0;
}
GbController::GbController() {
name = "Controller";
up.name = "Up";
down.name = "Down";
left.name = "Left";
right.name = "Right";
b.name = "B";
a.name = "A";
select.name = "Select";
start.name = "Start";
bTurbo.name = "Turbo B";
aTurbo.name = "Turbo A";
up.mapping = "KB0::Up";
down.mapping = "KB0::Down";
left.mapping = "KB0::Left";
right.mapping = "KB0::Right";
b.mapping = "KB0::Z";
a.mapping = "KB0::X";
select.mapping = "KB0::Apostrophe";
start.mapping = "KB0::Return";
append(up, down, left, right, b, a, select, start, bTurbo, aTurbo);
}
//
GbDevice::GbDevice() {
name = "Device";
append(controller);
}
//
GbInput::GbInput() {
name = "Game Boy";
append(device);
}

Some files were not shown because too many files have changed in this diff Show More