mirror of
https://github.com/rodamaral/lsnes.git
synced 2025-04-02 10:42:15 -04:00
354 lines
12 KiB
C++
Executable file
354 lines
12 KiB
C++
Executable file
void InterfaceSNES::initialize() {
|
|
SNES::interface = this;
|
|
SNES::system.init();
|
|
}
|
|
|
|
void InterfaceSNES::setController(bool port, unsigned device) {
|
|
if(port == 0) config->snes.controllerPort1Device = device;
|
|
if(port == 1) config->snes.controllerPort2Device = device;
|
|
|
|
if(port == 0) switch(device) {
|
|
case 0: return SNES::input.connect(0, SNES::Input::Device::None);
|
|
case 1: return SNES::input.connect(0, SNES::Input::Device::Joypad);
|
|
case 2: return SNES::input.connect(0, SNES::Input::Device::Multitap);
|
|
case 3: return SNES::input.connect(0, SNES::Input::Device::Mouse);
|
|
}
|
|
|
|
if(port == 1) switch(device) {
|
|
case 0: return SNES::input.connect(1, SNES::Input::Device::None);
|
|
case 1: return SNES::input.connect(1, SNES::Input::Device::Joypad);
|
|
case 2: return SNES::input.connect(1, SNES::Input::Device::Multitap);
|
|
case 3: return SNES::input.connect(1, SNES::Input::Device::Mouse);
|
|
case 4: return SNES::input.connect(1, SNES::Input::Device::SuperScope);
|
|
case 5: return SNES::input.connect(1, SNES::Input::Device::Justifier);
|
|
case 6: return SNES::input.connect(1, SNES::Input::Device::Justifiers);
|
|
case 7: return SNES::input.connect(1, SNES::Input::Device::Serial);
|
|
}
|
|
}
|
|
|
|
bool InterfaceSNES::cartridgeLoaded() {
|
|
return SNES::cartridge.loaded();
|
|
}
|
|
|
|
bool InterfaceSNES::loadCartridge(const string &basename) {
|
|
uint8_t *data;
|
|
unsigned size;
|
|
if(interface->loadFile(basename, data, size) == false) return false;
|
|
|
|
interface->unloadCartridge();
|
|
interface->baseName = nall::basename(basename);
|
|
interface->slotName = { nall::basename(basename) };
|
|
|
|
string markup;
|
|
markup.readfile({ interface->baseName, ".xml" });
|
|
if(markup == "") markup = SnesCartridge(data, size).markup;
|
|
|
|
SNES::cartridge.rom.copy(data, size);
|
|
SNES::cartridge.load(SNES::Cartridge::Mode::Normal, markup);
|
|
SNES::system.power();
|
|
|
|
delete[] data;
|
|
|
|
loadMemory();
|
|
interface->loadCartridge(::Interface::Mode::SNES);
|
|
SNES::video.generate(SNES::Video::Format::RGB30);
|
|
return true;
|
|
}
|
|
|
|
bool InterfaceSNES::loadSatellaviewSlottedCartridge(const string &basename, const string &slotname) {
|
|
uint8_t *data[2];
|
|
unsigned size[2];
|
|
if(interface->loadFile(basename, data[0], size[0]) == false) return false;
|
|
interface->loadFile(slotname, data[1], size[1]);
|
|
|
|
interface->unloadCartridge();
|
|
interface->baseName = nall::basename(basename);
|
|
if(data[1]) interface->baseName.append("+", nall::basename(notdir(slotname)));
|
|
interface->slotName = { nall::basename(basename), nall::basename(slotname) };
|
|
|
|
string markup;
|
|
markup.readfile({ interface->baseName, ".xml" });
|
|
if(markup == "") markup = SnesCartridge(data[0], size[0]).markup;
|
|
|
|
SNES::cartridge.rom.copy(data[0], size[0]);
|
|
if(data[1]) SNES::bsxflash.memory.copy(data[1], size[1]);
|
|
SNES::cartridge.load(SNES::Cartridge::Mode::BsxSlotted, markup);
|
|
SNES::system.power();
|
|
|
|
delete[] data[0];
|
|
if(data[1]) delete[] data[1];
|
|
|
|
loadMemory();
|
|
interface->loadCartridge(::Interface::Mode::SNES);
|
|
SNES::video.generate(SNES::Video::Format::RGB30);
|
|
return true;
|
|
}
|
|
|
|
bool InterfaceSNES::loadSatellaviewCartridge(const string &basename, const string &slotname) {
|
|
uint8_t *data[2];
|
|
unsigned size[2];
|
|
if(interface->loadFile(basename, data[0], size[0]) == false) return false;
|
|
interface->loadFile(slotname, data[1], size[1]);
|
|
|
|
interface->unloadCartridge();
|
|
interface->baseName = nall::basename(basename);
|
|
if(data[1]) interface->baseName.append("+", nall::basename(notdir(slotname)));
|
|
interface->slotName = { nall::basename(basename), nall::basename(slotname) };
|
|
|
|
string markup;
|
|
markup.readfile({ interface->baseName, ".xml" });
|
|
if(markup == "") markup = SnesCartridge(data[0], size[0]).markup;
|
|
|
|
SNES::cartridge.rom.copy(data[0], size[0]);
|
|
if(data[1]) SNES::bsxflash.memory.copy(data[1], size[1]);
|
|
SNES::cartridge.load(SNES::Cartridge::Mode::Bsx, markup);
|
|
SNES::system.power();
|
|
|
|
delete[] data[0];
|
|
if(data[1]) delete[] data[1];
|
|
|
|
loadMemory();
|
|
interface->loadCartridge(::Interface::Mode::SNES);
|
|
SNES::video.generate(SNES::Video::Format::RGB30);
|
|
return true;
|
|
}
|
|
|
|
bool InterfaceSNES::loadSufamiTurboCartridge(const string &basename, const string &slotAname, const string &slotBname) {
|
|
uint8_t *data[3];
|
|
unsigned size[3];
|
|
if(interface->loadFile(basename, data[0], size[0]) == false) return false;
|
|
interface->loadFile(slotAname, data[1], size[1]);
|
|
interface->loadFile(slotBname, data[2], size[2]);
|
|
|
|
interface->unloadCartridge();
|
|
interface->baseName = nall::basename(basename);
|
|
if(data[1] && data[2]) interface->baseName = { nall::basename(slotAname), "+", nall::basename(notdir(slotBname)) };
|
|
else if(data[1]) interface->baseName = nall::basename(slotAname);
|
|
else if(data[2]) interface->baseName = nall::basename(slotBname);
|
|
interface->slotName = { nall::basename(basename), nall::basename(slotAname), nall::basename(slotBname) };
|
|
|
|
string markup;
|
|
markup.readfile({ interface->baseName, ".xml" });
|
|
if(markup == "") markup = SnesCartridge(data[0], size[0]).markup;
|
|
|
|
SNES::cartridge.rom.copy(data[0], size[0]);
|
|
if(data[1]) SNES::sufamiturbo.slotA.rom.copy(data[1], size[1]);
|
|
if(data[2]) SNES::sufamiturbo.slotB.rom.copy(data[1], size[1]);
|
|
SNES::cartridge.load(SNES::Cartridge::Mode::SufamiTurbo, markup);
|
|
SNES::system.power();
|
|
|
|
delete[] data[0];
|
|
if(data[1]) delete[] data[1];
|
|
if(data[2]) delete[] data[2];
|
|
|
|
loadMemory();
|
|
interface->loadCartridge(::Interface::Mode::SNES);
|
|
SNES::video.generate(SNES::Video::Format::RGB30);
|
|
return true;
|
|
}
|
|
|
|
bool InterfaceSNES::loadSuperGameBoyCartridge(const string &basename, const string &slotname) {
|
|
uint8_t *data[2];
|
|
unsigned size[2];
|
|
if(interface->loadFile(basename, data[0], size[0]) == false) return false;
|
|
interface->loadFile(slotname, data[1], size[1]);
|
|
|
|
interface->unloadCartridge();
|
|
interface->baseName = nall::basename(basename);
|
|
if(data[1]) interface->baseName = nall::basename(slotname);
|
|
interface->slotName = { nall::basename(basename), nall::basename(slotname) };
|
|
|
|
string markup;
|
|
markup.readfile({ interface->baseName, ".xml" });
|
|
if(markup == "") markup = SnesCartridge(data[0], size[0]).markup;
|
|
|
|
string gbMarkup;
|
|
gbMarkup.readfile({ nall::basename(slotname), ".xml" });
|
|
if(gbMarkup == "") gbMarkup = GameBoyCartridge(data[1], size[1]).markup;
|
|
|
|
SNES::cartridge.rom.copy(data[0], size[0]);
|
|
GameBoy::cartridge.load(GameBoy::System::Revision::SuperGameBoy, gbMarkup, data[1], size[1]);
|
|
SNES::cartridge.load(SNES::Cartridge::Mode::SuperGameBoy, markup);
|
|
SNES::system.power();
|
|
|
|
delete[] data[0];
|
|
if(data[1]) delete[] data[1];
|
|
|
|
loadMemory();
|
|
interface->loadCartridge(::Interface::Mode::SNES);
|
|
SNES::video.generate(SNES::Video::Format::RGB30);
|
|
return true;
|
|
}
|
|
|
|
void InterfaceSNES::unloadCartridge() {
|
|
saveMemory();
|
|
SNES::cartridge.unload();
|
|
interface->baseName = "";
|
|
}
|
|
|
|
void InterfaceSNES::power() {
|
|
SNES::system.power();
|
|
}
|
|
|
|
void InterfaceSNES::reset() {
|
|
SNES::system.reset();
|
|
}
|
|
|
|
void InterfaceSNES::run() {
|
|
SNES::system.run();
|
|
}
|
|
|
|
//slot[] array = Cartridge::Slot to slot# conversion:
|
|
//{ Base, Bsx, SufamiTurbo, SufamiTurboA, SufamiTurboB, GameBoy }
|
|
|
|
void InterfaceSNES::loadMemory() {
|
|
static unsigned slot[] = { 0, 0, 0, 1, 2, 1 };
|
|
for(auto &memory : SNES::cartridge.nvram) {
|
|
if(memory.size == 0) continue;
|
|
string filename = { interface->slotName[slot[(unsigned)memory.slot]], memory.id };
|
|
uint8_t *data;
|
|
unsigned size;
|
|
if(file::read(filename, data, size)) {
|
|
memcpy(memory.data, data, min(memory.size, size));
|
|
delete[] data;
|
|
}
|
|
}
|
|
}
|
|
|
|
void InterfaceSNES::saveMemory() {
|
|
static unsigned slot[] = { 0, 0, 0, 1, 2, 1 };
|
|
for(auto &memory : SNES::cartridge.nvram) {
|
|
if(memory.size == 0) continue;
|
|
string filename = { interface->slotName[slot[(unsigned)memory.slot]], memory.id };
|
|
file::write(filename, memory.data, memory.size);
|
|
}
|
|
}
|
|
|
|
serializer InterfaceSNES::serialize() {
|
|
SNES::system.runtosave();
|
|
return SNES::system.serialize();
|
|
}
|
|
|
|
bool InterfaceSNES::unserialize(serializer &s) {
|
|
return SNES::system.unserialize(s);
|
|
}
|
|
|
|
void InterfaceSNES::setCheats(const lstring &list) {
|
|
if(SNES::cartridge.mode() == SNES::Cartridge::Mode::SuperGameBoy) {
|
|
GameBoy::cheat.reset();
|
|
for(auto &code : list) {
|
|
lstring codelist;
|
|
codelist.split("+", code);
|
|
for(auto &part : codelist) {
|
|
unsigned addr, data, comp;
|
|
if(GameBoy::Cheat::decode(part, addr, data, comp)) {
|
|
GameBoy::cheat.append({ addr, data, comp });
|
|
}
|
|
}
|
|
}
|
|
GameBoy::cheat.synchronize();
|
|
return;
|
|
}
|
|
|
|
SNES::cheat.reset();
|
|
for(auto &code : list) {
|
|
lstring codelist;
|
|
codelist.split("+", code);
|
|
for(auto &part : codelist) {
|
|
unsigned addr, data;
|
|
if(SNES::Cheat::decode(part, addr, data)) {
|
|
SNES::cheat.append({ addr, data });
|
|
}
|
|
}
|
|
}
|
|
SNES::cheat.synchronize();
|
|
}
|
|
|
|
//
|
|
|
|
void InterfaceSNES::videoRefresh(const uint32_t *data, bool hires, bool interlace, bool overscan) {
|
|
static uint32_t output[512 * 480];
|
|
|
|
unsigned width = 256 << hires;
|
|
unsigned height = 240 << interlace;
|
|
unsigned pitch = 1024 >> interlace;
|
|
|
|
//skip first line; as it is always blank (by SNES design)
|
|
if(overscan == false) data += 1 * 1024; // 8 + 224 + 8
|
|
if(overscan == true ) data += 9 * 1024; // 0 + 240 + 0
|
|
|
|
for(unsigned y = 0; y < height; y++) {
|
|
const uint32_t *sp = data + y * pitch;
|
|
uint32_t *dp = output + y * 512;
|
|
for(unsigned x = 0; x < width; x++) {
|
|
*dp++ = SNES::video.palette[*sp++];
|
|
}
|
|
}
|
|
|
|
if(config->video.maskOverscan) {
|
|
unsigned osw = config->video.maskOverscanHorizontal << hires;
|
|
unsigned osh = config->video.maskOverscanVertical << interlace;
|
|
|
|
for(unsigned y = 0; y < height; y++) {
|
|
uint32_t *dp = output + y * 512;
|
|
if(y < osh || y >= height - osh) {
|
|
memset(dp, 0, width * sizeof(uint32_t));
|
|
} else {
|
|
memset(dp + 0, 0, osw * sizeof(uint32_t));
|
|
memset(dp + width - osw, 0, osw * sizeof(uint32_t));
|
|
}
|
|
}
|
|
}
|
|
|
|
interface->videoRefresh(output, 512 * sizeof(uint32_t), width, height);
|
|
}
|
|
|
|
void InterfaceSNES::audioSample(int16_t lsample, int16_t rsample) {
|
|
signed samples[] = { lsample, rsample };
|
|
dspaudio.sample(samples);
|
|
while(dspaudio.pending()) {
|
|
dspaudio.read(samples);
|
|
audio.sample(samples[0], samples[1]);
|
|
}
|
|
}
|
|
|
|
int16_t InterfaceSNES::inputPoll(bool port, SNES::Input::Device device, unsigned index, unsigned id) {
|
|
if(port == 0) {
|
|
if(device == SNES::Input::Device::Joypad) return inputManager->snes.port1.gamepad.poll(id);
|
|
if(device == SNES::Input::Device::Multitap) {
|
|
if(index == 0) return inputManager->snes.port1.multitap1.poll(id);
|
|
if(index == 1) return inputManager->snes.port1.multitap1.poll(id);
|
|
if(index == 2) return inputManager->snes.port1.multitap1.poll(id);
|
|
if(index == 3) return inputManager->snes.port1.multitap1.poll(id);
|
|
}
|
|
if(device == SNES::Input::Device::Mouse) return inputManager->snes.port1.mouse.poll(id);
|
|
}
|
|
|
|
if(port == 1) {
|
|
if(device == SNES::Input::Device::Joypad) return inputManager->snes.port2.gamepad.poll(id);
|
|
if(device == SNES::Input::Device::Multitap) {
|
|
if(index == 0) return inputManager->snes.port2.multitap1.poll(id);
|
|
if(index == 1) return inputManager->snes.port2.multitap1.poll(id);
|
|
if(index == 2) return inputManager->snes.port2.multitap1.poll(id);
|
|
if(index == 3) return inputManager->snes.port2.multitap1.poll(id);
|
|
}
|
|
if(device == SNES::Input::Device::Mouse) return inputManager->snes.port2.mouse.poll(id);
|
|
if(device == SNES::Input::Device::SuperScope) return inputManager->snes.port2.superScope.poll(id);
|
|
if(device == SNES::Input::Device::Justifier) return inputManager->snes.port2.justifier1.poll(id);
|
|
if(device == SNES::Input::Device::Justifiers) {
|
|
if(index == 0) return inputManager->snes.port2.justifier1.poll(id);
|
|
if(index == 1) return inputManager->snes.port2.justifier2.poll(id);
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
string InterfaceSNES::path(SNES::Cartridge::Slot slot, const string &hint) {
|
|
static unsigned index[] = { 0, 0, 0, 1, 2, 1 };
|
|
return { interface->slotName[index[(unsigned)slot]], hint };
|
|
}
|
|
|
|
void InterfaceSNES::message(const string &text) {
|
|
MessageWindow::information(*mainWindow, text);
|
|
}
|