Update to v089r08 release.

byuu says:

Changelog:
- Super Game Boy, BS-X Satellaview and Sufami Turbo cartridges all load
  manifests that specify their file names, and they all work
- Sufami Turbo can now properly handle carts without RAM, or empty slots
  entirely
- Emulator::Interface structures no longer specify any file names, ever
- exposed "capability.(cheats,states)" now. So far, this just means the
  GBA doesn't show the cheat editor, since it doesn't support cheat
  codes yet
- as such, state manager and cheat editor windows auto-hide (may be
  a tiny bit inconvenient, but it makes not having to sync them or deal
  with input when no cart is loaded easier)
- added "AbsoluteInput" type, which returns mouse coordinates from
  -32767,-32767 (top left) to +32767,+32767 (bottom right) or
  -32768,-32768 (offscreen)

AbsoluteInput is just something I'm toying with. Idea is to support eg
Super Scope or Justifier, or possibly some future Famicom controllers
that are absolute-indexed. The coordinates are scaled, so the bigger
your window, the more precise they are. But obviously you can't get more
precise than the emulated system, so 1x scale will behave the same
anyway. I haven't hooked it up yet, need to mess with the idea of custom
cursors via phoenix for that first. Also not sure if it will feel
smoother or not ... if you resize the window, your mouse will seem to
move slower. Still, not having to capture the mouse for SS/JS may be
nicer yet. But we'll see ... just experimenting for now.
This commit is contained in:
Tim Allen 2012-05-28 09:50:50 +10:00
parent 9a8a54c75e
commit 189e707594
25 changed files with 236 additions and 165 deletions

View File

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

View File

@ -11,6 +11,10 @@ struct Interface {
bool overscan;
double aspectRatio;
bool resettable;
struct Capability {
bool states;
bool cheats;
} capability;
} information;
struct Media {
@ -18,7 +22,6 @@ struct Interface {
string name;
string type;
string path;
string extension;
};
vector<Media> firmware;
@ -30,7 +33,7 @@ struct Interface {
string name;
struct Input {
unsigned id;
unsigned type; //0 = digital, 1 = analog
unsigned type; //0 = digital, 1 = analog (relative), 2 = analog (absolute)
string name;
unsigned guid;
};
@ -46,7 +49,7 @@ struct Interface {
vector<Port> port;
struct Bind {
virtual void loadRequest(unsigned, const string&, const string&, const string&) {}
virtual void loadRequest(unsigned, const string&, const string&) {}
virtual void loadRequest(unsigned, const string&) {}
virtual void saveRequest(unsigned, const string&) {}
virtual uint32_t videoColor(unsigned, uint16_t, uint16_t, uint16_t) { return 0u; }
@ -58,7 +61,7 @@ struct Interface {
} *bind;
//callback bindings (provided by user interface)
void loadRequest(unsigned id, const string &name, const string &type, const string &path) { return bind->loadRequest(id, name, type, path); }
void loadRequest(unsigned id, const string &name, const string &type) { return bind->loadRequest(id, name, type); }
void loadRequest(unsigned id, const string &path) { return bind->loadRequest(id, path); }
void saveRequest(unsigned id, const string &path) { return bind->saveRequest(id, path); }
uint32_t videoColor(unsigned source, uint16_t red, uint16_t green, uint16_t blue) { return bind->videoColor(source, red, green, blue); }

View File

@ -109,8 +109,10 @@ Interface::Interface() {
information.overscan = true;
information.aspectRatio = 8.0 / 7.0;
information.resettable = true;
information.capability.states = true;
information.capability.cheats = true;
media.append({ID::Famicom, "Famicom", "sys", "program.rom", "fc"});
media.append({ID::Famicom, "Famicom", "sys", "fc"});
{
Device device{0, ID::Port1 | ID::Port2, "Controller"};

View File

@ -14,7 +14,7 @@ namespace GameBoy {
#include "serialization.cpp"
Cartridge cartridge;
void Cartridge::load(System::Revision revision, const string &manifest, bool preloaded) {
void Cartridge::load(System::Revision revision, const string &manifest) {
information.markup = manifest;
information.mapper = Mapper::Unknown;
information.ram = false;
@ -49,7 +49,8 @@ void Cartridge::load(System::Revision revision, const string &manifest, bool pre
ramsize = numeral(ram["size"].data);
ramdata = allocate<uint8>(ramsize, 0xff);
if(preloaded == false) {
//Super Game Boy core loads memory from Super Famicom core
if(revision != System::Revision::SuperGameBoy) {
if(rom["name"].exists()) interface->loadRequest(ID::ROM, rom["name"].data);
if(ram["name"].exists()) interface->loadRequest(ID::RAM, ram["name"].data);
if(ram["name"].exists()) memory.append({ID::RAM, ram["name"].data});

View File

@ -51,7 +51,7 @@ struct Cartridge : MMIO, property<Cartridge> {
MMIO *mapper;
bool bootrom_enable;
void load(System::Revision revision, const string &manifest, bool preloaded = false);
void load(System::Revision revision, const string &manifest);
void unload();
uint8 rom_read(unsigned addr);

View File

@ -120,13 +120,15 @@ Interface::Interface() {
information.overscan = false;
information.aspectRatio = 1.0;
information.resettable = false;
information.capability.states = true;
information.capability.cheats = true;
firmware.append({ID::GameBoyBootROM, "Game Boy", "sys", "boot.rom"});
firmware.append({ID::SuperGameBoyBootROM, "Super Game Boy", "sfc", "boot.rom"});
firmware.append({ID::GameBoyColorBootROM, "Game Boy Color", "sys", "boot.rom"});
media.append({ID::GameBoy, "Game Boy", "sys", "program.rom", "gb" });
media.append({ID::GameBoyColor, "Game Boy Color", "sys", "program.rom", "gbc"});
media.append({ID::GameBoy, "Game Boy", "sys", "gb" });
media.append({ID::GameBoyColor, "Game Boy Color", "sys", "gbc"});
{
Device device{0, ID::Device, "Controller"};

View File

@ -101,10 +101,12 @@ Interface::Interface() {
information.overscan = false;
information.aspectRatio = 1.0;
information.resettable = false;
information.capability.states = true;
information.capability.cheats = false;
firmware.append({ID::BIOS, "Game Boy Advance", "sys", "bios.rom"});
media.append({ID::GameBoyAdvance, "Game Boy Advance", "sys", "program.rom", "gba"});
media.append({ID::GameBoyAdvance, "Game Boy Advance", "sys", "gba"});
{
Device device{0, ID::Device, "Controller"};

View File

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<cartridge region="NTSC">
<rom>
<rom name="program.rom" size="0x40000">
<map mode="linear" address="00-1f:8000-ffff"/>
<map mode="linear" address="80-9f:8000-ffff"/>
</rom>
@ -10,7 +10,7 @@
<map mode="linear" address="20-3f:8000-ffff"/>
<map mode="linear" address="a0-bf:8000-ffff"/>
</rom>
<ram size="131072">
<ram>
<map mode="linear" address="60-63:8000-ffff"/>
<map mode="linear" address="e0-e3:8000-ffff"/>
</ram>
@ -20,7 +20,7 @@
<map mode="linear" address="40-5f:8000-ffff"/>
<map mode="linear" address="c0-df:8000-ffff"/>
</rom>
<ram size="131072">
<ram>
<map mode="linear" address="70-73:8000-ffff"/>
<map mode="linear" address="f0-f3:8000-ffff"/>
</ram>

View File

@ -5,7 +5,6 @@ namespace SuperFamicom {
#include "markup.cpp"
#include "serialization.cpp"
Cartridge cartridge;
void Cartridge::load(const string &manifest) {
@ -59,63 +58,63 @@ void Cartridge::load(const string &manifest) {
loaded = true;
}
void Cartridge::load(const string &markup, const stream &stream) {
rom.copy(stream);
void Cartridge::load_super_game_boy(const string &manifest) {
XML::Document document(manifest);
auto &rom = document["cartridge"]["rom"];
auto &ram = document["cartridge"]["ram"];
region = Region::NTSC;
//ram_size = 0;
GameBoy::cartridge.load(GameBoy::System::Revision::SuperGameBoy, manifest);
has_gb_slot = false;
has_bs_cart = false;
has_bs_slot = false;
has_st_slots = false;
has_nss_dip = false;
has_sa1 = false;
has_superfx = false;
has_armdsp = false;
has_hitachidsp = false;
has_necdsp = false;
has_epsonrtc = false;
has_sharprtc = false;
has_spc7110 = false;
has_sdd1 = false;
has_obc1 = false;
has_msu1 = false;
has_link = false;
if(rom["name"].exists()) interface->loadRequest(ID::SuperGameBoyROM, rom["name"].data);
if(ram["name"].exists()) interface->loadRequest(ID::SuperGameBoyRAM, ram["name"].data);
if(ram["name"].exists()) memory.append({ID::SuperGameBoyRAM, ram["name"].data});
}
parse_markup(markup);
//print(markup, "\n\n");
void Cartridge::load_satellaview(const string &manifest) {
XML::Document document(manifest);
auto &rom = document["cartridge"]["rom"];
//Super Game Boy
if(cartridge.has_gb_slot()) {
sha256 = nall::sha256(GameBoy::cartridge.romdata, GameBoy::cartridge.romsize);
if(rom["name"].exists()) {
interface->loadRequest(ID::BsxFlashROM, rom["name"].data);
}
}
void Cartridge::load_sufami_turbo_a(const string &manifest) {
XML::Document document(manifest);
auto &rom = document["cartridge"]["rom"];
auto &ram = document["cartridge"]["ram"];
if(rom["name"].exists()) {
interface->loadRequest(ID::SufamiTurboSlotAROM, rom["name"].data);
}
//Broadcast Satellaview
else if(cartridge.has_bs_cart() && cartridge.has_bs_slot()) {
sha256 = nall::sha256(bsxflash.memory.data(), bsxflash.memory.size());
if(ram["name"].exists()) {
unsigned size = numeral(ram["size"].data);
sufamiturbo.slotA.ram.map(allocate<uint8>(size, 0xff), size);
interface->loadRequest(ID::SufamiTurboSlotARAM, ram["name"].data);
memory.append({ID::SufamiTurboSlotARAM, ram["name"].data});
}
//Sufami Turbo
else if(cartridge.has_st_slots()) {
sha256 = nall::sha256(sufamiturbo.slotA.rom.data(), sufamiturbo.slotA.rom.size());
if(document["cartridge"]["linkable"].data == "true") {
interface->loadRequest(ID::SufamiTurboSlotB, "Sufami Turbo - Slot B", "st");
}
}
void Cartridge::load_sufami_turbo_b(const string &manifest) {
XML::Document document(manifest);
auto &rom = document["cartridge"]["rom"];
auto &ram = document["cartridge"]["ram"];
if(rom["name"].exists()) {
interface->loadRequest(ID::SufamiTurboSlotBROM, rom["name"].data);
}
//Super Famicom
else {
sha256 = nall::sha256(rom.data(), rom.size());
if(ram["name"].exists()) {
unsigned size = numeral(ram["size"].data);
sufamiturbo.slotB.ram.map(allocate<uint8>(size, 0xff), size);
interface->loadRequest(ID::SufamiTurboSlotBRAM, ram["name"].data);
memory.append({ID::SufamiTurboSlotBRAM, ram["name"].data});
}
// if(ram_size > 0) {
// ram.map(allocate<uint8>(ram_size, 0xff), ram_size);
// interface->memory.append({ID::RAM, "save.ram"});
// }
rom.write_protect(true);
ram.write_protect(false);
system.load();
loaded = true;
}
void Cartridge::unload() {
@ -131,7 +130,6 @@ void Cartridge::unload() {
Cartridge::Cartridge() {
loaded = false;
unload();
}
Cartridge::~Cartridge() {

View File

@ -63,7 +63,10 @@ struct Cartridge : property<Cartridge> {
vector<Memory> memory;
void load(const string &manifest);
void load(const string &markup, const stream &stream);
void load_super_game_boy(const string &manifest);
void load_satellaview(const string &manifest);
void load_sufami_turbo_a(const string &manifest);
void load_sufami_turbo_b(const string &manifest);
void unload();
void serialize(serializer&);

View File

@ -103,14 +103,7 @@ void Cartridge::parse_markup_icd2(XML::Node &root) {
if(root.exists() == false) return;
has_gb_slot = true;
//Game Boy requires a cartridge to be loaded ...
//load "empty" cartridge, in case loadRequest() does not load one
vector<uint8> stream;
stream.resize(32768);
for(auto &byte : stream) byte = 0xff;
interface->load(ID::SuperGameBoyROM, vectorstream{stream}, "");
interface->loadRequest(ID::SuperGameBoyROM, "Game Boy", "gb", "program.rom");
interface->loadRequest(ID::SuperGameBoy, "Game Boy", "gb");
icd2.revision = max(1, numeral(root["revision"].data));
@ -127,7 +120,7 @@ void Cartridge::parse_markup_bsx(XML::Node &root) {
has_bs_cart = root["mmio"].exists();
has_bs_slot = true;
interface->loadRequest(ID::BsxFlashROM, "BS-X Satellaview", "bs", "program.rom");
interface->loadRequest(ID::Satellaview, "BS-X Satellaview", "bs");
if(has_bs_cart) {
parse_markup_memory(bsxcartridge.psram, root["psram"], ID::BsxPSRAM, true);
@ -135,6 +128,8 @@ void Cartridge::parse_markup_bsx(XML::Node &root) {
for(auto &node : root["slot"]) {
if(node.name != "map") continue;
if(bsxflash.memory.size() == 0) continue;
Mapping m(bsxflash.memory);
parse_markup_map(m, node);
mapping.append(m);
@ -159,8 +154,8 @@ void Cartridge::parse_markup_sufamiturbo(XML::Node &root) {
if(root.exists() == false) return;
has_st_slots = true;
interface->loadRequest(ID::SufamiTurboSlotAROM, "Sufami Turbo - Slot A", "st", "program.rom");
interface->loadRequest(ID::SufamiTurboSlotBROM, "Sufami Turbo - Slot B", "st", "program.rom");
//load required slot A (will request slot B if slot A cartridge is linkable)
interface->loadRequest(ID::SufamiTurboSlotA, "Sufami Turbo - Slot A", "st");
for(auto &slot : root) {
if(slot.name != "slot") continue;
@ -170,6 +165,8 @@ void Cartridge::parse_markup_sufamiturbo(XML::Node &root) {
for(auto &leaf : node) {
if(leaf.name != "map") continue;
SuperFamicom::Memory &memory = slotid == 0 ? sufamiturbo.slotA.rom : sufamiturbo.slotB.rom;
if(memory.size() == 0) continue;
Mapping m(memory);
parse_markup_map(m, leaf);
if(m.size == 0) m.size = memory.size();
@ -181,9 +178,11 @@ void Cartridge::parse_markup_sufamiturbo(XML::Node &root) {
for(auto &leaf : node) {
if(leaf.name != "map") continue;
SuperFamicom::Memory &memory = slotid == 0 ? sufamiturbo.slotA.ram : sufamiturbo.slotB.ram;
if(memory.size() == 0) continue;
Mapping m(memory);
parse_markup_map(m, leaf);
if(m.size == 0) m.size = ram_size;
if(m.size == 0) m.size = memory.size();
if(m.size) mapping.append(m);
}
}

View File

@ -7,16 +7,6 @@ namespace SuperFamicom {
SufamiTurbo sufamiturbo;
void SufamiTurbo::load() {
slotA.ram.map(allocate<uint8>(128 * 1024, 0xff), 128 * 1024);
slotB.ram.map(allocate<uint8>(128 * 1024, 0xff), 128 * 1024);
if(slotA.rom.data() == nullptr) {
slotA.rom.map(allocate<uint8>(128 * 1024, 0xff), 128 * 1024);
}
if(slotB.rom.data() == nullptr) {
slotB.rom.map(allocate<uint8>(128 * 1024, 0xff), 128 * 1024);
}
}
void SufamiTurbo::unload() {

View File

@ -34,14 +34,18 @@ unsigned Interface::group(unsigned id) {
case ID::NecDSPRAM:
case ID::BsxPSRAM:
return 0;
case ID::SuperGameBoy:
case ID::SuperGameBoyROM:
case ID::SuperGameBoyRAM:
return 1;
case ID::Satellaview:
case ID::BsxFlashROM:
return 2;
case ID::SufamiTurboSlotA:
case ID::SufamiTurboSlotAROM:
case ID::SufamiTurboSlotARAM:
return 3;
case ID::SufamiTurboSlotB:
case ID::SufamiTurboSlotBROM:
case ID::SufamiTurboSlotBRAM:
return 4;
@ -50,7 +54,11 @@ unsigned Interface::group(unsigned id) {
}
void Interface::load(unsigned id, const string &manifest) {
cartridge.load(manifest);
if(id == ID::SuperFamicom) cartridge.load(manifest);
if(id == ID::SuperGameBoy) cartridge.load_super_game_boy(manifest);
if(id == ID::Satellaview) cartridge.load_satellaview(manifest);
if(id == ID::SufamiTurboSlotA) cartridge.load_sufami_turbo_a(manifest);
if(id == ID::SufamiTurboSlotB) cartridge.load_sufami_turbo_b(manifest);
}
void Interface::save() {
@ -107,12 +115,11 @@ void Interface::load(unsigned id, const stream &stream, const string &manifest)
}
if(id == ID::SuperGameBoyROM) {
GameBoy::interface->load(GameBoy::ID::ROM, stream);
GameBoy::cartridge.load(GameBoy::System::Revision::SuperGameBoy, manifest, true);
stream.read(GameBoy::cartridge.romdata, min(GameBoy::cartridge.romsize, stream.size()));
}
if(id == ID::SuperGameBoyRAM) {
stream.read(GameBoy::cartridge.ramdata, GameBoy::cartridge.ramsize);
stream.read(GameBoy::cartridge.ramdata, min(GameBoy::cartridge.ramsize, stream.size()));
}
if(id == ID::BsxFlashROM) {
@ -132,11 +139,11 @@ void Interface::load(unsigned id, const stream &stream, const string &manifest)
}
if(id == ID::SufamiTurboSlotARAM) {
sufamiturbo.slotA.ram.copy(stream);
stream.read(sufamiturbo.slotA.ram.data(), min(sufamiturbo.slotA.ram.size(), stream.size()));
}
if(id == ID::SufamiTurboSlotBRAM) {
sufamiturbo.slotB.ram.copy(stream);
stream.read(sufamiturbo.slotB.ram.data(), min(sufamiturbo.slotB.ram.size(), stream.size()));
}
}
@ -284,13 +291,15 @@ Interface::Interface() {
information.overscan = true;
information.aspectRatio = 8.0 / 7.0;
information.resettable = true;
information.capability.states = true;
information.capability.cheats = true;
firmware.append({ID::IPLROM, "Super Famicom", "sys", "spc700.rom"});
media.append({ID::SuperFamicom, "Super Famicom", "sys", "program.rom", "sfc"});
media.append({ID::SuperFamicom, "Super Game Boy", "sfc", "program.rom", "gb" });
media.append({ID::SuperFamicom, "BS-X Satellaview", "sfc", "program.rom", "bs" });
media.append({ID::SuperFamicom, "Sufami Turbo", "sfc", "program.rom", "st" });
media.append({ID::SuperFamicom, "Super Famicom", "sys", "sfc"});
media.append({ID::SuperFamicom, "Super Game Boy", "sfc", "gb" });
media.append({ID::SuperFamicom, "BS-X Satellaview", "sfc", "bs" });
media.append({ID::SuperFamicom, "Sufami Turbo", "sfc", "st" });
{
Device device{0, ID::Port1 | ID::Port2, "Controller"};

View File

@ -4,10 +4,14 @@ namespace SuperFamicom {
struct ID {
enum : unsigned {
//cartridges
SuperFamicom,
};
SuperGameBoy,
Satellaview,
SufamiTurboSlotA,
SufamiTurboSlotB,
enum : unsigned {
//memory
IPLROM,
ROM,
@ -32,9 +36,8 @@ struct ID {
SufamiTurboSlotBROM,
SufamiTurboSlotARAM,
SufamiTurboSlotBRAM,
};
enum : unsigned {
//controller ports
Port1 = 1,
Port2 = 2,
};

View File

@ -27,7 +27,7 @@ void Application::commandLineLoad(string pathname) {
for(auto &emulator : this->emulator) {
for(auto &media : emulator->media) {
if(media.type != "sys") continue;
if(type != media.extension) continue;
if(type != media.path) continue;
return utility->loadMedia(emulator, media, pathname);
}
}

View File

@ -67,7 +67,7 @@ void Browser::bootstrap() {
for(auto &media : emulator->media) {
bool found = false;
for(auto &folder : folderList) {
if(folder.extension == media.extension) {
if(folder.extension == media.path) {
found = true;
break;
}
@ -75,7 +75,7 @@ void Browser::bootstrap() {
if(found == true) continue;
Folder folder;
folder.extension = media.extension;
folder.extension = media.path;
folder.path = application->basepath;
folder.selection = 0;
folderList.append(folder);

View File

@ -28,9 +28,19 @@ void Presentation::synchronize() {
synchronizeVideo.setChecked(config->video.synchronize);
synchronizeAudio.setChecked(config->audio.synchronize);
muteAudio.setChecked(config->audio.mute);
toolsMenu.setVisible(application->active);
synchronizeTime.setVisible(application->active && system().rtc());
resizeWindow.setVisible(config->video.scaleMode != 2);
if(application->active == nullptr) {
toolsMenu.setVisible(false);
} else {
toolsMenu.setVisible(true);
saveStateMenu.setVisible(system().information.capability.states);
loadStateMenu.setVisible(system().information.capability.states);
stateMenuSeparator.setVisible(system().information.capability.states);
resizeWindow.setVisible(config->video.scaleMode != 2);
stateManager.setVisible(system().information.capability.states);
cheatEditor.setVisible(system().information.capability.cheats);
synchronizeTime.setVisible(system().rtc());
}
}
void Presentation::setSystemName(const string &name) {
@ -69,10 +79,10 @@ Presentation::Presentation() : active(nullptr) {
for(unsigned n = 0; n < 5; n++) saveStateItem[n].setText({"Slot ", 1 + n});
loadStateMenu.setText("Load State");
for(unsigned n = 0; n < 5; n++) loadStateItem[n].setText({"Slot ", 1 + n});
synchronizeTime.setText("Synchronize Time");
resizeWindow.setText("Resize Window");
cheatEditor.setText("Cheat Editor");
stateManager.setText("State Manager");
cheatEditor.setText("Cheat Editor");
synchronizeTime.setText("Synchronize Time");
append(loadMenu);
for(auto &item : loadListSystem) loadMenu.append(*item);
@ -97,9 +107,8 @@ Presentation::Presentation() : active(nullptr) {
for(unsigned n = 0; n < 5; n++) saveStateMenu.append(saveStateItem[n]);
toolsMenu.append(loadStateMenu);
for(unsigned n = 0; n < 5; n++) loadStateMenu.append(loadStateItem[n]);
toolsMenu.append(synchronizeTime);
toolsMenu.append(stateMenuSeparator);
toolsMenu.append(resizeWindow, cheatEditor, stateManager);
toolsMenu.append(resizeWindow, stateManager, cheatEditor, synchronizeTime);
append(layout);
layout.append(viewport, {0, 0, 720, 480});
@ -120,10 +129,10 @@ Presentation::Presentation() : active(nullptr) {
configurationSettings.onActivate = [&] { settings->setVisible(); settings->panelList.setFocused(); };
for(unsigned n = 0; n < 5; n++) saveStateItem[n].onActivate = [=] { utility->saveState(1 + n); };
for(unsigned n = 0; n < 5; n++) loadStateItem[n].onActivate = [=] { utility->loadState(1 + n); };
synchronizeTime.onActivate = [&] { system().rtcsync(); };
resizeWindow.onActivate = [&] { utility->resize(true); };
cheatEditor.onActivate = [&] { ::cheatEditor->setVisible(); };
stateManager.onActivate = [&] { ::stateManager->setVisible(); };
cheatEditor.onActivate = [&] { ::cheatEditor->setVisible(); };
synchronizeTime.onActivate = [&] { system().rtcsync(); };
synchronize();
}

View File

@ -43,11 +43,11 @@ struct Presentation : Window {
Item saveStateItem[5];
Menu loadStateMenu;
Item loadStateItem[5];
Item synchronizeTime;
Separator stateMenuSeparator;
Item resizeWindow;
Item cheatEditor;
Item stateManager;
Item cheatEditor;
Item synchronizeTime;
void synchronize();
void setSystemName(const string &name);

View File

@ -102,7 +102,7 @@ int16_t DigitalInput::poll() {
//
bool AnalogInput::bind(unsigned scancode, int16_t value) {
bool RelativeInput::bind(unsigned scancode, int16_t value) {
using nall::Keyboard;
using nall::Mouse;
@ -118,16 +118,9 @@ bool AnalogInput::bind(unsigned scancode, int16_t value) {
if(Joypad::isAnyAxis(scancode)) return append(encode);
return false;
append:
if(mapping.position(encode)) return false; //mapping already bound
if(mapping.empty() || mapping == "None") mapping = encode; //remove "None"
else mapping.append(",", encode); //add to existing mapping list
AbstractInput::bind();
return true;
}
int16_t AnalogInput::poll() {
int16_t RelativeInput::poll() {
int16_t result = 0;
for(auto &item : inputList) {
@ -144,6 +137,76 @@ int16_t AnalogInput::poll() {
//
bool AbsoluteInput::bind(unsigned scancode, int16_t value) {
using nall::Keyboard;
using nall::Mouse;
if(scancode == Scancode::None || scancode == keyboard(0)[Keyboard::Escape]) {
inputList.reset();
mapping = "None";
return true;
}
string encode = Scancode::encode(scancode);
if(Mouse::isAnyAxis(scancode)) {
//only one input can be assigned for absolute positioning
inputList.reset();
mapping = encode;
return true;
}
return false;
}
int16_t AbsoluteInput::poll() {
int16_t result = -32768; //offscreen value
using nall::Mouse;
Position position = phoenix::Mouse::position();
Geometry geometry = presentation->geometry();
if(position.x < geometry.x
|| position.y < geometry.y
|| position.x >= geometry.x + geometry.width
|| position.y >= geometry.y + geometry.height) {
//cursor is offscreen
position.x = -32768;
position.y = -32768;
} else {
//convert from screen to viewport coordinates
double x = position.x - geometry.x;
double y = position.y - geometry.y;
//scale coordinate range to -0.5 to +0.5 (0.0 = center)
x = x * 1.0 / geometry.width - 0.5;
y = y * 1.0 / geometry.height - 0.5;
//scale coordinates to -32767 to +32767
signed px = (signed)(x * 65535.0);
signed py = (signed)(y * 65535.0);
//clamp to valid range
position.x = max(-32767, min(+32767, px));
position.y = max(-32767, min(+32767, py));
}
for(auto &item : inputList) {
if(item.scancode == mouse(0)[Mouse::Xaxis]) {
result = position.x;
}
if(item.scancode == mouse(0)[Mouse::Yaxis]) {
result = position.y;
}
}
return result;
}
//
HotkeyInput::HotkeyInput() {
logic = 1; //AND
inputManager->hotkeyMap.append(this);
@ -201,8 +264,9 @@ void InputManager::bootstrap() {
AbstractInput *abstract = nullptr;
if(input.type == 0) abstract = new DigitalInput;
if(input.type == 1) abstract = new AnalogInput;
if(input.type >= 2) continue;
if(input.type == 1) abstract = new RelativeInput;
if(input.type == 2) abstract = new AbsoluteInput;
if(input.type >= 3) continue;
abstract->name = {emulator->information.name, "::", port.name, "::", device.name, "::", input.name};
abstract->name.replace(" ", "");

View File

@ -23,7 +23,13 @@ struct DigitalInput : AbstractInput {
int16_t poll();
};
struct AnalogInput : AbstractInput {
struct RelativeInput : AbstractInput {
using AbstractInput::bind;
bool bind(unsigned scancode, int16_t value);
int16_t poll();
};
struct AbsoluteInput : AbstractInput {
using AbstractInput::bind;
bool bind(unsigned scancode, int16_t value);
int16_t poll();

View File

@ -1,8 +1,8 @@
#include "../ethos.hpp"
Interface *interface = nullptr;
void Interface::loadRequest(unsigned id, const string &name, const string &type, const string &path) {
return utility->loadRequest(id, name, type, path);
void Interface::loadRequest(unsigned id, const string &name, const string &type) {
return utility->loadRequest(id, name, type);
}
void Interface::loadRequest(unsigned id, const string &path) {

View File

@ -1,7 +1,7 @@
struct Interface : Emulator::Interface::Bind {
void loadRequest(unsigned id, const string &name, const string &type);
void loadRequest(unsigned id, const string &path);
void saveRequest(unsigned id, const string &path);
void loadRequest(unsigned id, const string &name, const string &type, const string &path);
uint32_t videoColor(unsigned source, uint16_t red, uint16_t green, uint16_t blue);
void videoRefresh(const uint32_t *data, unsigned pitch, unsigned width, unsigned height);
void audioSample(int16_t lsample, int16_t rsample);

View File

@ -72,7 +72,8 @@ void InputSettings::synchronize() {
assign[2].setVisible(true);
}
if(dynamic_cast<AnalogInput*>(selectedInput)) {
if(dynamic_cast<RelativeInput*>(selectedInput)
|| dynamic_cast<AbsoluteInput*>(selectedInput)) {
assign[0].setText("Mouse X-axis");
assign[1].setText("Mouse Y-axis");
assign[0].setVisible(true);
@ -170,7 +171,8 @@ void InputSettings::assignMouseInput(unsigned n) {
return inputEvent(mouse(0).button(n), 1, true);
}
if(dynamic_cast<AnalogInput*>(activeInput)) {
if(dynamic_cast<RelativeInput*>(activeInput)
|| dynamic_cast<AbsoluteInput*>(activeInput)) {
return inputEvent(mouse(0).axis(n), 1, true);
}
}

View File

@ -11,7 +11,7 @@ void Utility::setInterface(Emulator::Interface *emulator) {
void Utility::loadMedia(Emulator::Interface *emulator, Emulator::Interface::Media &media) {
string pathname;
if(media.type != "sys") pathname = application->path({media.name, ".", media.type, "/"});
if(!directory::exists(pathname)) pathname = browser->select({"Load ", media.name}, media.extension);
if(!directory::exists(pathname)) pathname = browser->select({"Load ", media.name}, media.path);
if(!directory::exists(pathname)) return;
if(!file::exists({pathname, "manifest.xml"})) return;
loadMedia(emulator, media, pathname);
@ -35,7 +35,7 @@ void Utility::loadMedia(Emulator::Interface *emulator, Emulator::Interface::Medi
}
//request from emulation core to load non-volatile media folder
void Utility::loadRequest(unsigned id, const string &name, const string &type, const string &path) {
void Utility::loadRequest(unsigned id, const string &name, const string &type) {
string pathname = browser->select({"Load ", name}, type);
if(pathname.empty()) return;
this->path(system().group(id)) = pathname;
@ -43,8 +43,7 @@ void Utility::loadRequest(unsigned id, const string &name, const string &type, c
string manifest;
manifest.readfile({pathname, "manifest.xml"});
mmapstream stream({pathname, path});
system().load(id, stream, manifest);
system().load(id, manifest);
}
//request from emulation core to load non-volatile media file
@ -62,29 +61,6 @@ void Utility::saveRequest(unsigned id, const string &path) {
return system().save(id, stream);
}
void Utility::loadMemory() {
// for(auto &memory : system().memory) {
// string pathname = path(system().group(memory.id));
// if(file::exists({pathname, memory.name}) == false) continue;
// filestream fs({pathname, memory.name});
// system().load(memory.id, fs);
// }
cheatEditor->load({pathname[0], "cheats.xml"});
stateManager->load({pathname[0], "bsnes/states.bsa"}, 1);
}
void Utility::saveMemory() {
// for(auto &memory : system().memory) {
// string pathname = path(system().group(memory.id));
// filestream fs({pathname, memory.name}, file::mode::write);
// system().save(memory.id, fs);
// }
cheatEditor->save({pathname[0], "cheats.xml"});
stateManager->save({pathname[0], "bsnes/states.bsa"}, 1);
}
void Utility::connect(unsigned port, unsigned device) {
if(application->active == nullptr) return;
system().connect(port, device);
@ -110,7 +86,8 @@ void Utility::load() {
title.rtrim<1>(" + ");
presentation->setTitle(title);
loadMemory();
cheatEditor->load({pathname[0], "cheats.xml"});
stateManager->load({pathname[0], "bsnes/states.bsa"}, 1);
system().paletteUpdate();
synchronizeDSP();
@ -124,7 +101,9 @@ void Utility::unload() {
if(application->active == nullptr) return;
if(tracerEnable) tracerToggle();
saveMemory();
cheatEditor->save({pathname[0], "cheats.xml"});
stateManager->save({pathname[0], "bsnes/states.bsa"}, 1);
system().unload();
path.reset();
pathname.reset();
@ -135,8 +114,9 @@ void Utility::unload() {
video.clear();
audio.clear();
presentation->setTitle({Emulator::Name, " v", Emulator::Version});
cheatEditor->synchronize();
stateManager->synchronize();
cheatDatabase->setVisible(false);
cheatEditor->setVisible(false);
stateManager->setVisible(false);
}
void Utility::saveState(unsigned slot) {

View File

@ -4,11 +4,9 @@ struct Utility {
void loadMedia(Emulator::Interface *emulator, Emulator::Interface::Media &media);
void loadMedia(Emulator::Interface *emulator, Emulator::Interface::Media &media, const string &pathname);
void loadRequest(unsigned id, const string &name, const string &type, const string &path);
void loadRequest(unsigned id, const string &name, const string &type);
void loadRequest(unsigned id, const string &path);
void saveRequest(unsigned id, const string &path);
void loadMemory();
void saveMemory();
void connect(unsigned port, unsigned device);
void power();