mirror of https://github.com/bsnes-emu/bsnes.git
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:
parent
9a8a54c75e
commit
189e707594
|
@ -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";
|
||||
}
|
||||
|
|
|
@ -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); }
|
||||
|
|
|
@ -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"};
|
||||
|
|
|
@ -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});
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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"};
|
||||
|
|
|
@ -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"};
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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() {
|
||||
|
|
|
@ -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&);
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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() {
|
||||
|
|
|
@ -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"};
|
||||
|
|
|
@ -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,
|
||||
};
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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(" ", "");
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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();
|
||||
|
|
Loading…
Reference in New Issue