mirror of
https://github.com/SourMesen/Mesen2.git
synced 2025-04-02 10:21:44 -04:00
NES/SNES: Allow more flexibility when configuring inputs (2x multitap, etc.)
This commit is contained in:
parent
e19ea0b0cd
commit
1f222fd300
44 changed files with 950 additions and 693 deletions
|
@ -68,6 +68,7 @@
|
|||
<ClInclude Include="NES\Input\PowerPad.h" />
|
||||
<ClInclude Include="NES\Input\SuborKeyboard.h" />
|
||||
<ClInclude Include="NES\Input\SuborMouse.h" />
|
||||
<ClInclude Include="NES\Input\TwoPlayerAdapter.h" />
|
||||
<ClInclude Include="NES\Input\VirtualBoyController.h" />
|
||||
<ClInclude Include="NES\Input\VsZapper.h" />
|
||||
<ClInclude Include="NES\Input\Zapper.h" />
|
||||
|
@ -115,6 +116,7 @@
|
|||
<ClInclude Include="Shared\BaseState.h" />
|
||||
<ClInclude Include="Gameboy\GbControlManager.h" />
|
||||
<ClInclude Include="Gameboy\stdafx.h" />
|
||||
<ClInclude Include="Shared\ControllerHub.h" />
|
||||
<ClInclude Include="Shared\Interfaces\IAudioProvider.h" />
|
||||
<ClInclude Include="Shared\Interfaces\IBattery.h" />
|
||||
<ClInclude Include="Shared\Interfaces\IControlManager.h" />
|
||||
|
|
|
@ -820,6 +820,8 @@
|
|||
<ClInclude Include="Gameboy\Debugger\DummyGbCpu.h" />
|
||||
<ClInclude Include="Debugger\DebuggerFeatures.h" />
|
||||
<ClInclude Include="Shared\RenderedFrame.h" />
|
||||
<ClInclude Include="Shared\ControllerHub.h" />
|
||||
<ClInclude Include="NES\Input\TwoPlayerAdapter.h" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="SNES\SnesCpu.cpp">
|
||||
|
|
|
@ -59,7 +59,7 @@ public:
|
|||
{
|
||||
}
|
||||
|
||||
void DrawController(InputHud& hud)
|
||||
void InternalDrawController(InputHud& hud) override
|
||||
{
|
||||
hud.DrawOutline(35, 14);
|
||||
|
||||
|
|
|
@ -1,50 +1,77 @@
|
|||
#pragma once
|
||||
#include "stdafx.h"
|
||||
#include "Shared/BaseControlDevice.h"
|
||||
#include "Shared/ControllerHub.h"
|
||||
#include "Utilities/Serializer.h"
|
||||
|
||||
class FourScore : public BaseControlDevice
|
||||
class FourScore : public ControllerHub<4>
|
||||
{
|
||||
private:
|
||||
uint32_t _signature4016 = 0;
|
||||
uint32_t _signature4017 = 0;
|
||||
uint8_t _signature[2] = {};
|
||||
uint8_t _sigCounter[2] = {};
|
||||
|
||||
protected:
|
||||
void Serialize(Serializer &s) override
|
||||
{
|
||||
BaseControlDevice::Serialize(s);
|
||||
s.Stream(_signature4016, _signature4017);
|
||||
ControllerHub::Serialize(s);
|
||||
s.Stream(_signature[0], _signature[1], _sigCounter[0], _sigCounter[1]);
|
||||
}
|
||||
|
||||
void RefreshStateBuffer() override
|
||||
{
|
||||
//Signature for port 0 = 0x10, reversed bit order => 0x08
|
||||
//Signature for port 1 = 0x20, reversed bit order => 0x04
|
||||
_signature4016 = (0x08 << 16);
|
||||
_signature4017 = (0x04 << 16);
|
||||
if(GetControllerType() == ControllerType::FourScore) {
|
||||
_signature[0] = 0x08;
|
||||
_signature[1] = 0x04;
|
||||
} else {
|
||||
//Signature is reversed for Hori 4p adapter
|
||||
_signature[0] = 0x04;
|
||||
_signature[1] = 0x08;
|
||||
}
|
||||
_sigCounter[0] = 16;
|
||||
_sigCounter[1] = 16;
|
||||
}
|
||||
|
||||
|
||||
public:
|
||||
FourScore(Emulator* emu) : BaseControlDevice(emu, ControllerType::FourScore, BaseControlDevice::ExpDevicePort)
|
||||
FourScore(Emulator* emu, ControllerType type, ControllerConfig controllers[]) : ControllerHub(emu, type, 0, controllers)
|
||||
{
|
||||
}
|
||||
|
||||
void WriteRam(uint16_t addr, uint8_t value)
|
||||
{
|
||||
value &= 0x01;
|
||||
ControllerHub::WriteRam(addr, value);
|
||||
}
|
||||
|
||||
uint8_t ReadRam(uint16_t addr) override
|
||||
{
|
||||
StrobeProcessRead();
|
||||
uint8_t output = 0;
|
||||
if(addr == 0x4016) {
|
||||
output = _signature4016 & 0x01;
|
||||
_signature4016 >>= 1;
|
||||
} else if(addr == 0x4017) {
|
||||
output = _signature4017 & 0x01;
|
||||
_signature4017 >>= 1;
|
||||
uint8_t i = addr - 0x4016;
|
||||
|
||||
if(_sigCounter[i] > 0) {
|
||||
_sigCounter[i]--;
|
||||
|
||||
if(_sigCounter[i] < 8) {
|
||||
i += 2;
|
||||
}
|
||||
|
||||
if(_ports[i]) {
|
||||
output |= ReadPort(i);
|
||||
}
|
||||
} else {
|
||||
output |= _signature[i] & 0x01;
|
||||
_signature[i] = (_signature[i] >> 1) | 0x80;
|
||||
}
|
||||
|
||||
output &= 0x01;
|
||||
|
||||
if(GetControllerType() == ControllerType::FourPlayerAdapter) {
|
||||
output <<= 1;
|
||||
}
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
void WriteRam(uint16_t addr, uint8_t value) override
|
||||
{
|
||||
StrobeProcessWrite(value);
|
||||
}
|
||||
};
|
||||
|
|
|
@ -13,7 +13,7 @@ private:
|
|||
uint32_t _turboSpeed = 0;
|
||||
|
||||
protected:
|
||||
uint32_t _stateBuffer = 0;
|
||||
uint8_t _stateBuffer = 0;
|
||||
|
||||
void Serialize(Serializer& s) override
|
||||
{
|
||||
|
@ -69,16 +69,7 @@ protected:
|
|||
|
||||
void RefreshStateBuffer() override
|
||||
{
|
||||
if(_emu->GetControlManager()->HasControlDevice(ControllerType::FourScore)) {
|
||||
if(_port >= 2) {
|
||||
_stateBuffer = ToByte() << 8;
|
||||
} else {
|
||||
//Add some 0 bit padding to allow P3/P4 controller bits + signature bits
|
||||
_stateBuffer = (_port == 0 ? 0xFF000000 : 0xFF000000) | ToByte();
|
||||
}
|
||||
} else {
|
||||
_stateBuffer = 0xFFFFFF00 | ToByte();
|
||||
}
|
||||
_stateBuffer = ToByte();
|
||||
}
|
||||
|
||||
public:
|
||||
|
@ -108,21 +99,17 @@ public:
|
|||
{
|
||||
uint8_t output = 0;
|
||||
|
||||
if((addr == 0x4016 && (_port & 0x01) == 0) || (addr == 0x4017 && (_port & 0x01) == 1)) {
|
||||
if(IsCurrentPort(addr)) {
|
||||
StrobeProcessRead();
|
||||
|
||||
output = _stateBuffer & 0x01;
|
||||
if(_port >= 2 && GetControllerType() == ControllerType::FamicomController) {
|
||||
//Famicom outputs P3 & P4 on bit 1
|
||||
output <<= 1;
|
||||
}
|
||||
_stateBuffer >>= 1;
|
||||
|
||||
//"All subsequent reads will return D=1 on an authentic controller but may return D=0 on third party controllers."
|
||||
_stateBuffer |= 0x80000000;
|
||||
_stateBuffer |= 0x80;
|
||||
}
|
||||
|
||||
if(addr == 0x4016 && IsPressed(NesController::Buttons::Microphone)) {
|
||||
if(addr == 0x4016 && _type == ControllerType::FamicomController && IsPressed(NesController::Buttons::Microphone)) {
|
||||
output |= 0x04;
|
||||
}
|
||||
|
||||
|
@ -134,7 +121,7 @@ public:
|
|||
StrobeProcessWrite(value);
|
||||
}
|
||||
|
||||
void DrawController(InputHud& hud)
|
||||
void InternalDrawController(InputHud& hud) override
|
||||
{
|
||||
hud.DrawOutline(35, 14);
|
||||
|
||||
|
@ -150,6 +137,6 @@ public:
|
|||
hud.DrawButton(13, 9, 4, 2, IsPressed(Buttons::Select));
|
||||
hud.DrawButton(18, 9, 4, 2, IsPressed(Buttons::Start));
|
||||
|
||||
hud.DrawNumber(_port + 1, 16, 2);
|
||||
hud.DrawNumber(hud.GetControllerIndex() + 1, 16, 2);
|
||||
}
|
||||
};
|
31
Core/NES/Input/TwoPlayerAdapter.h
Normal file
31
Core/NES/Input/TwoPlayerAdapter.h
Normal file
|
@ -0,0 +1,31 @@
|
|||
#pragma once
|
||||
#include "stdafx.h"
|
||||
#include "Shared/BaseControlDevice.h"
|
||||
#include "Shared/ControllerHub.h"
|
||||
#include "Utilities/Serializer.h"
|
||||
|
||||
class TwoPlayerAdapter : public ControllerHub<2>
|
||||
{
|
||||
public:
|
||||
TwoPlayerAdapter(Emulator* emu, ControllerType type, ControllerConfig controllers[]) : ControllerHub(emu, type, 0, controllers)
|
||||
{}
|
||||
|
||||
void WriteRam(uint16_t addr, uint8_t value)
|
||||
{
|
||||
value &= 0x01;
|
||||
ControllerHub::WriteRam(addr, value);
|
||||
}
|
||||
|
||||
uint8_t ReadRam(uint16_t addr) override
|
||||
{
|
||||
StrobeProcessRead();
|
||||
uint8_t output = 0;
|
||||
uint8_t i = addr - 0x4016;
|
||||
|
||||
if(_ports[i]) {
|
||||
output |= (ReadPort(i) & 0x01) << 1;
|
||||
}
|
||||
|
||||
return output;
|
||||
}
|
||||
};
|
|
@ -22,7 +22,7 @@ protected:
|
|||
}
|
||||
|
||||
public:
|
||||
VsZapper(NesConsole* console, uint8_t port) : Zapper(console, ControllerType::VsZapper, port)
|
||||
VsZapper(NesConsole* console, uint8_t port) : Zapper(console, ControllerType::NesZapper, port)
|
||||
{
|
||||
}
|
||||
|
||||
|
|
|
@ -29,15 +29,6 @@ VsControlManager::~VsControlManager()
|
|||
UnregisterInputProvider(this);
|
||||
}
|
||||
|
||||
ControllerType VsControlManager::GetControllerType(uint8_t port)
|
||||
{
|
||||
ControllerType type = NesControlManager::GetControllerType(port);
|
||||
if(type == ControllerType::NesZapper) {
|
||||
type = ControllerType::VsZapper;
|
||||
}
|
||||
return type;
|
||||
}
|
||||
|
||||
void VsControlManager::Reset(bool softReset)
|
||||
{
|
||||
NesControlManager::Reset(softReset);
|
||||
|
|
|
@ -41,7 +41,6 @@ private:
|
|||
};
|
||||
|
||||
protected:
|
||||
ControllerType GetControllerType(uint8_t port) override;
|
||||
void RemapControllerButtons() override;
|
||||
uint8_t GetOpenBusMask(uint8_t port) override;
|
||||
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
#include "NES/Input/Zapper.h"
|
||||
#include "NES/Input/ArkanoidController.h"
|
||||
#include "NES/Input/OekaKidsTablet.h"
|
||||
#include "NES/Input/TwoPlayerAdapter.h"
|
||||
#include "NES/Input/FourScore.h"
|
||||
#include "NES/Input/PowerPad.h"
|
||||
#include "NES/Input/FamilyMatTrainer.h"
|
||||
|
@ -46,29 +47,42 @@ NesControlManager::~NesControlManager()
|
|||
{
|
||||
}
|
||||
|
||||
ControllerType NesControlManager::GetControllerType(uint8_t port)
|
||||
{
|
||||
return _emu->GetSettings()->GetNesConfig().Controllers[port].Type;
|
||||
}
|
||||
|
||||
shared_ptr<BaseControlDevice> NesControlManager::CreateControllerDevice(ControllerType type, uint8_t port)
|
||||
{
|
||||
shared_ptr<BaseControlDevice> device;
|
||||
|
||||
NesConfig cfg = _emu->GetSettings()->GetNesConfig();
|
||||
KeyMappingSet keys = cfg.Controllers[port].Keys;
|
||||
ControllerConfig controllers[4];
|
||||
NesConfig& cfg = _emu->GetSettings()->GetNesConfig();
|
||||
KeyMappingSet keys;
|
||||
switch(port) {
|
||||
default:
|
||||
case 0: keys = cfg.Port1.Keys; break;
|
||||
case 1: keys = cfg.Port2.Keys; break;
|
||||
|
||||
//Used by VS system
|
||||
case 2: keys = cfg.Port1SubPorts[2].Keys; break;
|
||||
case 3: keys = cfg.Port1SubPorts[3].Keys; break;
|
||||
}
|
||||
|
||||
switch(type) {
|
||||
case ControllerType::None: break;
|
||||
case ControllerType::NesController: device.reset(new NesController(_emu, type, port, keys)); break;
|
||||
case ControllerType::FamicomController: device.reset(new NesController(_emu, type, port, keys)); break;
|
||||
case ControllerType::NesZapper: device.reset(new Zapper(_console, type, port)); break;
|
||||
case ControllerType::NesZapper: {
|
||||
RomFormat romFormat = _console->GetRomFormat();
|
||||
if(romFormat == RomFormat::VsSystem || romFormat == RomFormat::VsDualSystem) {
|
||||
device.reset(new VsZapper(_console, port));
|
||||
} else {
|
||||
device.reset(new Zapper(_console, type, port));
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case ControllerType::NesArkanoidController: device.reset(new ArkanoidController(_emu, type, port)); break;
|
||||
case ControllerType::SnesController: device.reset(new SnesController(_emu, port, keys)); break;
|
||||
case ControllerType::PowerPad: device.reset(new PowerPad(_emu, type, port, keys)); break;
|
||||
case ControllerType::SnesMouse: device.reset(new SnesMouse(_emu, port)); break;
|
||||
case ControllerType::SuborMouse: device.reset(new SuborMouse(_emu, port)); break;
|
||||
case ControllerType::VsZapper: device.reset(new VsZapper(_console, port)); break;
|
||||
case ControllerType::VirtualBoyController: device.reset(new VirtualBoyController(_emu, port, keys)); break;
|
||||
|
||||
//Exp port devices
|
||||
|
@ -76,7 +90,7 @@ shared_ptr<BaseControlDevice> NesControlManager::CreateControllerDevice(Controll
|
|||
case ControllerType::FamicomArkanoidController: device.reset(new ArkanoidController(_emu, type, BaseControlDevice::ExpDevicePort)); break;
|
||||
case ControllerType::OekaKidsTablet: device.reset(new OekaKidsTablet(_emu)); break;
|
||||
case ControllerType::FamilyTrainerMat: device.reset(new FamilyMatTrainer(_emu, keys)); break;
|
||||
case ControllerType::KonamiHyperShot: device.reset(new KonamiHyperShot(_emu, keys, cfg.Controllers[1].Keys)); break;
|
||||
case ControllerType::KonamiHyperShot: device.reset(new KonamiHyperShot(_emu, keys, cfg.Port2.Keys)); break;
|
||||
case ControllerType::FamilyBasicKeyboard: device.reset(new FamilyBasicKeyboard(_emu, keys)); break;
|
||||
case ControllerType::PartyTap: device.reset(new PartyTap(_emu, keys)); break;
|
||||
case ControllerType::Pachinko: device.reset(new PachinkoController(_emu, keys)); break;
|
||||
|
@ -88,9 +102,26 @@ shared_ptr<BaseControlDevice> NesControlManager::CreateControllerDevice(Controll
|
|||
case ControllerType::BandaiHyperShot: device.reset(new BandaiHyperShot(_console, keys)); break;
|
||||
case ControllerType::AsciiTurboFile: device.reset(new AsciiTurboFile(_emu)); break;
|
||||
case ControllerType::BattleBox: device.reset(new BattleBox(_emu)); break;
|
||||
case ControllerType::FourScore: device.reset(new FourScore(_emu)); break;
|
||||
|
||||
case ControllerType::FourScore: {
|
||||
std::copy(cfg.Port1SubPorts, cfg.Port1SubPorts + 4, controllers);
|
||||
controllers[0].Keys = cfg.Port1.Keys;
|
||||
device.reset(new FourScore(_emu, type, controllers));
|
||||
break;
|
||||
}
|
||||
|
||||
case ControllerType::TwoPlayerAdapter:
|
||||
case ControllerType::FourPlayerAdapter: {
|
||||
std::copy(cfg.ExpPortSubPorts, cfg.ExpPortSubPorts + 4, controllers);
|
||||
controllers[0].Keys = cfg.ExpPort.Keys;
|
||||
if(type == ControllerType::TwoPlayerAdapter) {
|
||||
device.reset(new TwoPlayerAdapter(_emu, type, controllers));
|
||||
} else {
|
||||
device.reset(new FourScore(_emu, type, controllers));
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case ControllerType::FourPlayerAdapter:
|
||||
default: break;
|
||||
}
|
||||
|
||||
|
@ -99,7 +130,7 @@ shared_ptr<BaseControlDevice> NesControlManager::CreateControllerDevice(Controll
|
|||
|
||||
void NesControlManager::UpdateControlDevices()
|
||||
{
|
||||
NesConfig cfg = _emu->GetSettings()->GetNesConfig();
|
||||
NesConfig& cfg = _emu->GetSettings()->GetNesConfig();
|
||||
if(_emu->GetSettings()->IsEqual(_prevConfig, cfg) && _controlDevices.size() > 0) {
|
||||
//Do nothing if configuration is unchanged
|
||||
return;
|
||||
|
@ -110,18 +141,15 @@ void NesControlManager::UpdateControlDevices()
|
|||
|
||||
ClearDevices();
|
||||
|
||||
ControllerType expansionDevice = GetControllerType(BaseControlDevice::ExpDevicePort);
|
||||
bool allowFourPlayers = (expansionDevice == ControllerType::FourPlayerAdapter || expansionDevice == ControllerType::FourScore);
|
||||
|
||||
for(int i = 0; i < (allowFourPlayers ? 4 : 2); i++) {
|
||||
shared_ptr<BaseControlDevice> device = CreateControllerDevice(GetControllerType(i), i);
|
||||
for(int i = 0; i < 2; i++) {
|
||||
shared_ptr<BaseControlDevice> device = CreateControllerDevice(i == 0 ? cfg.Port1.Type : cfg.Port2.Type, i);
|
||||
if(device) {
|
||||
RegisterControlDevice(device);
|
||||
}
|
||||
}
|
||||
|
||||
if(expansionDevice != ControllerType::None) {
|
||||
shared_ptr<BaseControlDevice> expDevice = CreateControllerDevice(expansionDevice, BaseControlDevice::ExpDevicePort);
|
||||
if(cfg.ExpPort.Type != ControllerType::None) {
|
||||
shared_ptr<BaseControlDevice> expDevice = CreateControllerDevice(cfg.ExpPort.Type, BaseControlDevice::ExpDevicePort);
|
||||
if(expDevice) {
|
||||
RegisterControlDevice(expDevice);
|
||||
}
|
||||
|
@ -231,7 +259,7 @@ void NesControlManager::Reset(bool softReset)
|
|||
void NesControlManager::Serialize(Serializer& s)
|
||||
{
|
||||
//Restore controllers that were being used at the time the snapshot was made
|
||||
//This is particularely important to ensure proper sync during NetPlay
|
||||
//This is particularly important to ensure proper sync during NetPlay
|
||||
ControllerType controllerTypes[4];
|
||||
ExpansionPortDevice expansionDevice;
|
||||
ConsoleType consoleType;
|
||||
|
|
|
@ -27,7 +27,6 @@ protected:
|
|||
NesConsole* _console;
|
||||
|
||||
virtual void Serialize(Serializer& s) override;
|
||||
virtual ControllerType GetControllerType(uint8_t port);
|
||||
virtual void RemapControllerButtons();
|
||||
virtual uint8_t GetOpenBusMask(uint8_t port);
|
||||
|
||||
|
|
|
@ -4,7 +4,6 @@
|
|||
#include "Netplay/PlayerListMessage.h"
|
||||
#include "Shared/Emulator.h"
|
||||
#include "Shared/Interfaces/IControlManager.h"
|
||||
#include "SNES/Input/Multitap.h"
|
||||
#include "Shared/NotificationManager.h"
|
||||
#include "Shared/MessageManager.h"
|
||||
#include "Utilities/Socket.h"
|
||||
|
@ -61,10 +60,10 @@ bool GameServer::SetInput(BaseControlDevice *device)
|
|||
{
|
||||
uint8_t port = device->GetPort();
|
||||
|
||||
//TODO?
|
||||
if(device->GetControllerType() == ControllerType::Multitap) {
|
||||
//TODO
|
||||
//Need special handling for the multitap, merge data from P3/4/5 with P1 (or P2, depending which port the multitap is plugged into)
|
||||
GameServerConnection* connection = GetNetPlayDevice(port);
|
||||
/*GameServerConnection* connection = GetNetPlayDevice(port);
|
||||
if(connection) {
|
||||
((Multitap*)device)->SetControllerState(0, connection->GetState());
|
||||
}
|
||||
|
@ -74,7 +73,7 @@ bool GameServer::SetInput(BaseControlDevice *device)
|
|||
if(connection) {
|
||||
((Multitap*)device)->SetControllerState(i - 1, connection->GetState());
|
||||
}
|
||||
}
|
||||
}*/
|
||||
} else {
|
||||
GameServerConnection* connection = GetNetPlayDevice(port);
|
||||
if(connection) {
|
||||
|
|
|
@ -1,193 +1,31 @@
|
|||
#include "stdafx.h"
|
||||
#include "SNES/Input/Multitap.h"
|
||||
#include "SNES/Input/SnesController.h"
|
||||
#include "SNES/SnesConsole.h"
|
||||
#include "SNES/InternalRegisters.h"
|
||||
#include "Shared/InputHud.h"
|
||||
#include "Shared/Emulator.h"
|
||||
#include "Shared/EmuSettings.h"
|
||||
|
||||
string Multitap::GetKeyNames()
|
||||
Multitap::Multitap(SnesConsole* console, uint8_t port, ControllerConfig controllers[]) : ControllerHub(console->GetEmulator(), ControllerType::Multitap, port, controllers)
|
||||
{
|
||||
//Repeat key names 4x, once for each controller
|
||||
return "ABXYLRSTUDLR:ABXYLRSTUDLR:ABXYLRSTUDLR:ABXYLRSTUDLR";
|
||||
}
|
||||
|
||||
void Multitap::InternalSetStateFromInput()
|
||||
{
|
||||
for(int i = 0; i < 4; i++) {
|
||||
int offset = Multitap::ButtonCount * i;
|
||||
for(KeyMapping& keyMapping : _mappings[i]) {
|
||||
SetPressedState(Buttons::A + offset, keyMapping.A);
|
||||
SetPressedState(Buttons::B + offset, keyMapping.B);
|
||||
SetPressedState(Buttons::X + offset, keyMapping.X);
|
||||
SetPressedState(Buttons::Y + offset, keyMapping.Y);
|
||||
SetPressedState(Buttons::L + offset, keyMapping.L);
|
||||
SetPressedState(Buttons::R + offset, keyMapping.R);
|
||||
SetPressedState(Buttons::Start + offset, keyMapping.Start);
|
||||
SetPressedState(Buttons::Select + offset, keyMapping.Select);
|
||||
SetPressedState(Buttons::Up + offset, keyMapping.Up);
|
||||
SetPressedState(Buttons::Down + offset, keyMapping.Down);
|
||||
SetPressedState(Buttons::Left + offset, keyMapping.Left);
|
||||
SetPressedState(Buttons::Right + offset, keyMapping.Right);
|
||||
|
||||
uint8_t turboFreq = 1 << (4 - _turboSpeed[i]);
|
||||
bool turboOn = (uint8_t)(_emu->GetPpuFrame().FrameCount % turboFreq) < turboFreq / 2;
|
||||
if(turboOn) {
|
||||
SetPressedState(Buttons::A + offset, keyMapping.TurboA);
|
||||
SetPressedState(Buttons::B + offset, keyMapping.TurboB);
|
||||
SetPressedState(Buttons::X + offset, keyMapping.TurboX);
|
||||
SetPressedState(Buttons::Y + offset, keyMapping.TurboY);
|
||||
SetPressedState(Buttons::L + offset, keyMapping.TurboL);
|
||||
SetPressedState(Buttons::R + offset, keyMapping.TurboR);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Multitap::UpdateControllerState(uint8_t controllerNumber, SnesController &controller)
|
||||
{
|
||||
int offset = Multitap::ButtonCount * controllerNumber;
|
||||
SetBitValue(Buttons::A + offset, controller.IsPressed(Buttons::A));
|
||||
SetBitValue(Buttons::B + offset, controller.IsPressed(Buttons::B));
|
||||
SetBitValue(Buttons::X + offset, controller.IsPressed(Buttons::X));
|
||||
SetBitValue(Buttons::Y + offset, controller.IsPressed(Buttons::Y));
|
||||
SetBitValue(Buttons::L + offset, controller.IsPressed(Buttons::L));
|
||||
SetBitValue(Buttons::R + offset, controller.IsPressed(Buttons::R));
|
||||
SetBitValue(Buttons::Start + offset, controller.IsPressed(Buttons::Start));
|
||||
SetBitValue(Buttons::Select + offset, controller.IsPressed(Buttons::Select));
|
||||
SetBitValue(Buttons::Up + offset, controller.IsPressed(Buttons::Up));
|
||||
SetBitValue(Buttons::Down + offset, controller.IsPressed(Buttons::Down));
|
||||
SetBitValue(Buttons::Left + offset, controller.IsPressed(Buttons::Left));
|
||||
SetBitValue(Buttons::Right + offset, controller.IsPressed(Buttons::Right));
|
||||
}
|
||||
|
||||
uint16_t Multitap::ToByte(uint8_t port)
|
||||
{
|
||||
//"A Super NES controller returns a 16-bit report in a similar order: B, Y, Select, Start, Up, Down, Left, Right, A, X, L, R, then four 0 bits."
|
||||
|
||||
int offset = port * Multitap::ButtonCount;
|
||||
|
||||
return
|
||||
(uint8_t)IsPressed(Buttons::B + offset) |
|
||||
((uint8_t)IsPressed(Buttons::Y + offset) << 1) |
|
||||
((uint8_t)IsPressed(Buttons::Select + offset) << 2) |
|
||||
((uint8_t)IsPressed(Buttons::Start + offset) << 3) |
|
||||
((uint8_t)IsPressed(Buttons::Up + offset) << 4) |
|
||||
((uint8_t)IsPressed(Buttons::Down + offset) << 5) |
|
||||
((uint8_t)IsPressed(Buttons::Left + offset) << 6) |
|
||||
((uint8_t)IsPressed(Buttons::Right + offset) << 7) |
|
||||
((uint8_t)IsPressed(Buttons::A + offset) << 8) |
|
||||
((uint8_t)IsPressed(Buttons::X + offset) << 9) |
|
||||
((uint8_t)IsPressed(Buttons::L + offset) << 10) |
|
||||
((uint8_t)IsPressed(Buttons::R + offset) << 11);
|
||||
}
|
||||
|
||||
void Multitap::Serialize(Serializer & s)
|
||||
{
|
||||
BaseControlDevice::Serialize(s);
|
||||
s.Stream(_stateBuffer[0], _stateBuffer[1], _stateBuffer[2], _stateBuffer[3]);
|
||||
}
|
||||
|
||||
void Multitap::RefreshStateBuffer()
|
||||
{
|
||||
for(int i = 0; i < 4; i++) {
|
||||
_stateBuffer[i] = ToByte(i);
|
||||
}
|
||||
}
|
||||
|
||||
Multitap::Multitap(SnesConsole* console, uint8_t port, KeyMappingSet keyMappings1, KeyMappingSet keyMappings2, KeyMappingSet keyMappings3, KeyMappingSet keyMappings4) : BaseControlDevice(console->GetEmulator(), ControllerType::Multitap, port, keyMappings1)
|
||||
{
|
||||
_turboSpeed[0] = keyMappings1.TurboSpeed;
|
||||
_turboSpeed[1] = keyMappings2.TurboSpeed;
|
||||
_turboSpeed[2] = keyMappings3.TurboSpeed;
|
||||
_turboSpeed[3] = keyMappings4.TurboSpeed;
|
||||
|
||||
_mappings[0] = keyMappings1.GetKeyMappingArray();
|
||||
_mappings[1] = keyMappings2.GetKeyMappingArray();
|
||||
_mappings[2] = keyMappings3.GetKeyMappingArray();
|
||||
_mappings[3] = keyMappings4.GetKeyMappingArray();
|
||||
_internalRegs = console->GetInternalRegisters();
|
||||
}
|
||||
|
||||
void Multitap::SetControllerState(uint8_t controllerNumber, ControlDeviceState state)
|
||||
{
|
||||
SnesController controller(_emu, 0, KeyMappingSet());
|
||||
controller.SetRawState(state);
|
||||
UpdateControllerState(controllerNumber, controller);
|
||||
}
|
||||
|
||||
uint8_t Multitap::ReadRam(uint16_t addr)
|
||||
{
|
||||
uint8_t selectBit = 0x80 >> ((_port == 0) ? 1 : 0);
|
||||
uint8_t portSelect = (_internalRegs->GetIoPortOutput() & selectBit) ? 0 : 2;
|
||||
uint8_t output = 0;
|
||||
|
||||
if(IsCurrentPort(addr)) {
|
||||
StrobeProcessRead();
|
||||
|
||||
output = _stateBuffer[portSelect] & 0x01;
|
||||
output |= (_stateBuffer[portSelect + 1] & 0x01) << 1; //P3 & P5 are reported in bit 1
|
||||
uint8_t selectBit = 0x80 >> ((_port == 0) ? 1 : 0);
|
||||
uint8_t portSelect = (_internalRegs->GetIoPortOutput() & selectBit) ? 0 : 2;
|
||||
output = ReadPort(portSelect) & 0x01;
|
||||
output |= (ReadPort(portSelect + 1) & 0x01) << 1; //P3 & P5 are reported in bit 1
|
||||
|
||||
if(_strobe) {
|
||||
//Bit 1 is always set when strobe is high
|
||||
output |= 0x02;
|
||||
//Bit 1 is always set and bit 0 is always clear (when strobe is high)
|
||||
return 0x02;
|
||||
}
|
||||
|
||||
_stateBuffer[portSelect] >>= 1;
|
||||
_stateBuffer[portSelect + 1] >>= 1;
|
||||
|
||||
//"All subsequent reads will return D=1 on an authentic controller but may return D=0 on third party controllers."
|
||||
_stateBuffer[portSelect] |= 0x8000;
|
||||
_stateBuffer[portSelect + 1] |= 0x8000;
|
||||
}
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
void Multitap::WriteRam(uint16_t addr, uint8_t value)
|
||||
{
|
||||
if(addr == 0x4016) {
|
||||
StrobeProcessWrite(value);
|
||||
}
|
||||
}
|
||||
|
||||
void Multitap::DrawController(InputHud& hud)
|
||||
{
|
||||
InputConfig& cfg = _emu->GetSettings()->GetInputConfig();
|
||||
|
||||
for(int j = 0; j < 4; j++) {
|
||||
int port = j == 0 ? _port : (j + 1);
|
||||
if(cfg.DisplayInputPort[port]) {
|
||||
DrawController(hud, port);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Multitap::DrawController(InputHud& hud, int port)
|
||||
{
|
||||
int offset = port * Multitap::ButtonCount;
|
||||
|
||||
hud.DrawOutline(35, 14);
|
||||
|
||||
hud.DrawButton(5, 3, 3, 3, IsPressed(Buttons::Up + offset));
|
||||
hud.DrawButton(5, 9, 3, 3, IsPressed(Buttons::Down + offset));
|
||||
hud.DrawButton(2, 6, 3, 3, IsPressed(Buttons::Left + offset));
|
||||
hud.DrawButton(8, 6, 3, 3, IsPressed(Buttons::Right + offset));
|
||||
hud.DrawButton(5, 6, 3, 3, false);
|
||||
|
||||
hud.DrawButton(27, 3, 3, 3, IsPressed(Buttons::X + offset));
|
||||
hud.DrawButton(27, 9, 3, 3, IsPressed(Buttons::B + offset));
|
||||
hud.DrawButton(30, 6, 3, 3, IsPressed(Buttons::A + offset));
|
||||
hud.DrawButton(24, 6, 3, 3, IsPressed(Buttons::Y + offset));
|
||||
|
||||
hud.DrawButton(4, 0, 5, 2, IsPressed(Buttons::L + offset));
|
||||
hud.DrawButton(26, 0, 5, 2, IsPressed(Buttons::R + offset));
|
||||
|
||||
hud.DrawButton(13, 9, 4, 2, IsPressed(Buttons::Select + offset));
|
||||
hud.DrawButton(18, 9, 4, 2, IsPressed(Buttons::Start + offset));
|
||||
|
||||
hud.DrawNumber(port + 1, 16, 2);
|
||||
|
||||
hud.EndDrawController();
|
||||
}
|
|
@ -1,41 +1,18 @@
|
|||
#pragma once
|
||||
#include "stdafx.h"
|
||||
#include "Shared/BaseControlDevice.h"
|
||||
#include "Utilities/Serializer.h"
|
||||
#include "Shared/ControllerHub.h"
|
||||
|
||||
class InternalRegisters;
|
||||
class SnesController;
|
||||
class SnesConsole;
|
||||
class InputHud;
|
||||
|
||||
class Multitap : public BaseControlDevice
|
||||
class Multitap : public ControllerHub<4>
|
||||
{
|
||||
private:
|
||||
enum Buttons { A = 0, B, X, Y, L, R, Select, Start, Up, Down, Left, Right };
|
||||
static constexpr int ButtonCount = 12;
|
||||
|
||||
vector<KeyMapping> _mappings[4];
|
||||
uint8_t _turboSpeed[4] = {};
|
||||
uint16_t _stateBuffer[4] = {};
|
||||
InternalRegisters *_internalRegs = nullptr;
|
||||
|
||||
void DrawController(InputHud& hud, int port);
|
||||
|
||||
protected:
|
||||
string GetKeyNames() override;
|
||||
void InternalSetStateFromInput() override;
|
||||
void UpdateControllerState(uint8_t controllerNumber, SnesController &controller);
|
||||
uint16_t ToByte(uint8_t port);
|
||||
void Serialize(Serializer &s) override;
|
||||
void RefreshStateBuffer() override;
|
||||
|
||||
public:
|
||||
Multitap(SnesConsole* console, uint8_t port, KeyMappingSet keyMappings1, KeyMappingSet keyMappings2, KeyMappingSet keyMappings3, KeyMappingSet keyMappings4);
|
||||
|
||||
void SetControllerState(uint8_t controllerNumber, ControlDeviceState state);
|
||||
Multitap(SnesConsole* console, uint8_t port, ControllerConfig controllers[]);
|
||||
|
||||
uint8_t ReadRam(uint16_t addr) override;
|
||||
void WriteRam(uint16_t addr, uint8_t value) override;
|
||||
|
||||
void DrawController(InputHud& hud) override;
|
||||
};
|
|
@ -98,7 +98,7 @@ void SnesController::WriteRam(uint16_t addr, uint8_t value)
|
|||
StrobeProcessWrite(value);
|
||||
}
|
||||
|
||||
void SnesController::DrawController(InputHud& hud)
|
||||
void SnesController::InternalDrawController(InputHud& hud)
|
||||
{
|
||||
hud.DrawOutline(35, 14);
|
||||
|
||||
|
@ -119,5 +119,5 @@ void SnesController::DrawController(InputHud& hud)
|
|||
hud.DrawButton(13, 9, 4, 2, IsPressed(Buttons::Select));
|
||||
hud.DrawButton(18, 9, 4, 2, IsPressed(Buttons::Start));
|
||||
|
||||
hud.DrawNumber(_port + 1, 16, 2);
|
||||
hud.DrawNumber(hud.GetControllerIndex() + 1, 16, 2);
|
||||
}
|
||||
|
|
|
@ -24,5 +24,5 @@ public:
|
|||
uint8_t ReadRam(uint16_t addr) override;
|
||||
void WriteRam(uint16_t addr, uint8_t value) override;
|
||||
|
||||
void DrawController(InputHud& hud) override;
|
||||
void InternalDrawController(InputHud& hud) override;
|
||||
};
|
|
@ -87,13 +87,13 @@ public:
|
|||
_stateBuffer = (byte1 << 24) | (byte2 << 16) | (byte3 << 8) | byte4;
|
||||
}
|
||||
|
||||
void DrawController(InputHud& hud)
|
||||
void InternalDrawController(InputHud& hud) override
|
||||
{
|
||||
hud.DrawOutline(11, 14);
|
||||
|
||||
hud.DrawButton(1, 1, 4, 5, IsPressed(Buttons::Left));
|
||||
hud.DrawButton(6, 1, 4, 5, IsPressed(Buttons::Right));
|
||||
|
||||
hud.DrawNumber(_port + 1, 4, 7);
|
||||
hud.DrawNumber(hud.GetControllerIndex() + 1, 4, 7);
|
||||
}
|
||||
};
|
|
@ -26,23 +26,34 @@ SnesControlManager::~SnesControlManager()
|
|||
{
|
||||
}
|
||||
|
||||
ControllerType SnesControlManager::GetControllerType(uint8_t port)
|
||||
{
|
||||
return _emu->GetSettings()->GetSnesConfig().Controllers[port].Type;
|
||||
}
|
||||
|
||||
shared_ptr<BaseControlDevice> SnesControlManager::CreateControllerDevice(ControllerType type, uint8_t port)
|
||||
{
|
||||
shared_ptr<BaseControlDevice> device;
|
||||
|
||||
SnesConfig cfg = _emu->GetSettings()->GetSnesConfig();
|
||||
SnesConfig& cfg = _emu->GetSettings()->GetSnesConfig();
|
||||
|
||||
switch(type) {
|
||||
case ControllerType::None: break;
|
||||
case ControllerType::SnesController: device.reset(new SnesController(_emu, port, cfg.Controllers[port].Keys)); break;
|
||||
case ControllerType::SnesController:
|
||||
device.reset(new SnesController(_emu, port, port == 0 ? cfg.Port1.Keys : cfg.Port2.Keys));
|
||||
break;
|
||||
|
||||
case ControllerType::SnesMouse: device.reset(new SnesMouse(_emu, port)); break;
|
||||
case ControllerType::SuperScope: device.reset(new SuperScope(_console, port, cfg.Controllers[port].Keys)); break;
|
||||
case ControllerType::Multitap: device.reset(new Multitap(_console, port, cfg.Controllers[port].Keys, cfg.Controllers[2].Keys, cfg.Controllers[3].Keys, cfg.Controllers[4].Keys)); break;
|
||||
|
||||
case ControllerType::SuperScope: device.reset(new SuperScope(_console, port, port == 0 ? cfg.Port1.Keys : cfg.Port2.Keys)); break;
|
||||
|
||||
case ControllerType::Multitap: {
|
||||
ControllerConfig controllers[4];
|
||||
if(port == 0) {
|
||||
std::copy(cfg.Port1SubPorts, cfg.Port1SubPorts + 4, controllers);
|
||||
controllers[0].Keys = cfg.Port1.Keys;
|
||||
} else {
|
||||
std::copy(cfg.Port2SubPorts, cfg.Port2SubPorts + 4, controllers);
|
||||
controllers[0].Keys = cfg.Port2.Keys;
|
||||
}
|
||||
device.reset(new Multitap(_console, port, controllers));
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
throw std::runtime_error("Unsupported controller type");
|
||||
|
@ -53,7 +64,7 @@ shared_ptr<BaseControlDevice> SnesControlManager::CreateControllerDevice(Control
|
|||
|
||||
void SnesControlManager::UpdateControlDevices()
|
||||
{
|
||||
SnesConfig cfg = _emu->GetSettings()->GetSnesConfig();
|
||||
SnesConfig& cfg = _emu->GetSettings()->GetSnesConfig();
|
||||
if(_emu->GetSettings()->IsEqual(_prevConfig, cfg) && _controlDevices.size() > 0) {
|
||||
//Do nothing if configuration is unchanged
|
||||
return;
|
||||
|
@ -62,7 +73,7 @@ void SnesControlManager::UpdateControlDevices()
|
|||
auto lock = _deviceLock.AcquireSafe();
|
||||
ClearDevices();
|
||||
for(int i = 0; i < 2; i++) {
|
||||
shared_ptr<BaseControlDevice> device = CreateControllerDevice(GetControllerType(i), i);
|
||||
shared_ptr<BaseControlDevice> device = CreateControllerDevice(i == 0 ? cfg.Port1.Type : cfg.Port2.Type, i);
|
||||
if(device) {
|
||||
RegisterControlDevice(device);
|
||||
}
|
||||
|
@ -88,8 +99,9 @@ void SnesControlManager::Write(uint16_t addr, uint8_t value)
|
|||
|
||||
void SnesControlManager::Serialize(Serializer &s)
|
||||
{
|
||||
SnesConfig cfg = _emu->GetSettings()->GetSnesConfig();
|
||||
s.Stream(cfg.Controllers[0].Type, cfg.Controllers[1].Type, cfg.Controllers[2].Type, cfg.Controllers[3].Type, cfg.Controllers[4].Type);
|
||||
SnesConfig& cfg = _emu->GetSettings()->GetSnesConfig();
|
||||
//TODO
|
||||
//s.Stream(cfg.Controllers[0].Type, cfg.Controllers[1].Type, cfg.Controllers[2].Type, cfg.Controllers[3].Type, cfg.Controllers[4].Type);
|
||||
if(!s.IsSaving()) {
|
||||
_emu->GetSettings()->SetSnesConfig(cfg);
|
||||
UpdateControlDevices();
|
||||
|
|
|
@ -25,8 +25,6 @@ private:
|
|||
protected:
|
||||
SnesConsole* _console;
|
||||
|
||||
ControllerType GetControllerType(uint8_t port);
|
||||
|
||||
public:
|
||||
SnesControlManager(SnesConsole* console);
|
||||
virtual ~SnesControlManager();
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
#include "Shared/Emulator.h"
|
||||
#include "Shared/KeyManager.h"
|
||||
#include "Shared/EmuSettings.h"
|
||||
#include "Shared/InputHud.h"
|
||||
#include "Utilities/StringUtilities.h"
|
||||
#include "Utilities/Serializer.h"
|
||||
|
||||
|
@ -78,6 +79,15 @@ ControlDeviceState BaseControlDevice::GetRawState()
|
|||
return _state;
|
||||
}
|
||||
|
||||
void BaseControlDevice::DrawController(InputHud& hud)
|
||||
{
|
||||
InputConfig& cfg = _emu->GetSettings()->GetInputConfig();
|
||||
if(hud.GetControllerIndex() < 8 && cfg.DisplayInputPort[hud.GetControllerIndex()]) {
|
||||
InternalDrawController(hud);
|
||||
}
|
||||
hud.EndDrawController();
|
||||
}
|
||||
|
||||
void BaseControlDevice::SetRawState(ControlDeviceState state)
|
||||
{
|
||||
auto lock = _stateLock.AcquireSafe();
|
||||
|
|
|
@ -12,9 +12,10 @@ class InputHud;
|
|||
class BaseControlDevice : public ISerializable
|
||||
{
|
||||
private:
|
||||
ControlDeviceState _state = {};
|
||||
|
||||
protected:
|
||||
ControlDeviceState _state = {};
|
||||
|
||||
Emulator* _emu = nullptr;
|
||||
vector<KeyMapping> _keyMappings;
|
||||
bool _strobe = false;
|
||||
|
@ -24,7 +25,7 @@ protected:
|
|||
SimpleLock _stateLock;
|
||||
|
||||
virtual void RefreshStateBuffer() { }
|
||||
|
||||
|
||||
void EnsureCapacity(int32_t minBitCount);
|
||||
uint32_t GetByteIndex(uint8_t bit);
|
||||
virtual bool HasCoordinates();
|
||||
|
@ -44,9 +45,9 @@ protected:
|
|||
|
||||
void SetMovement(MouseMovement mov);
|
||||
MouseMovement GetMovement();
|
||||
|
||||
|
||||
virtual void InternalSetStateFromInput();
|
||||
|
||||
|
||||
public:
|
||||
static constexpr uint8_t ExpDevicePort = 4;
|
||||
static constexpr uint8_t ConsoleInputPort = 5;
|
||||
|
@ -73,16 +74,17 @@ public:
|
|||
void InvertBit(uint8_t bit);
|
||||
void SetBitValue(uint8_t bit, bool set);
|
||||
|
||||
void SetTextState(string state);
|
||||
string GetTextState();
|
||||
virtual void SetTextState(string state);
|
||||
virtual string GetTextState();
|
||||
|
||||
void SetStateFromInput();
|
||||
virtual void OnAfterSetState() { }
|
||||
|
||||
void SetRawState(ControlDeviceState state);
|
||||
ControlDeviceState GetRawState();
|
||||
virtual void SetRawState(ControlDeviceState state);
|
||||
virtual ControlDeviceState GetRawState();
|
||||
|
||||
virtual void DrawController(InputHud& hud) {}
|
||||
virtual void InternalDrawController(InputHud& hud) {}
|
||||
virtual void DrawController(InputHud& hud);
|
||||
|
||||
virtual uint8_t ReadRam(uint16_t addr) = 0;
|
||||
virtual void WriteRam(uint16_t addr, uint8_t value) = 0;
|
||||
|
|
138
Core/Shared/ControllerHub.h
Normal file
138
Core/Shared/ControllerHub.h
Normal file
|
@ -0,0 +1,138 @@
|
|||
#pragma once
|
||||
|
||||
#include "stdafx.h"
|
||||
#include "Shared/BaseControlDevice.h"
|
||||
#include "Shared/InputHud.h"
|
||||
#include "SNES/Input/SnesController.h"
|
||||
#include "SNES/Input/SnesMouse.h"
|
||||
#include "NES/Input/NesController.h"
|
||||
#include "Utilities/Serializer.h"
|
||||
#include "Utilities/StringUtilities.h"
|
||||
|
||||
template<int HubPortCount>
|
||||
class ControllerHub : public BaseControlDevice
|
||||
{
|
||||
protected:
|
||||
shared_ptr<BaseControlDevice> _ports[HubPortCount];
|
||||
|
||||
void InternalSetStateFromInput() override
|
||||
{
|
||||
for(int i = 0; i < HubPortCount; i++) {
|
||||
if(_ports[i]) {
|
||||
_ports[i]->SetStateFromInput();
|
||||
}
|
||||
}
|
||||
|
||||
UpdateStateFromPorts();
|
||||
}
|
||||
|
||||
void UpdateStateFromPorts()
|
||||
{
|
||||
for(int i = 0; i < HubPortCount; i++) {
|
||||
if(_ports[i]) {
|
||||
ControlDeviceState portState = _ports[i]->GetRawState();
|
||||
_state.State.push_back((uint8_t)portState.State.size());
|
||||
_state.State.insert(_state.State.end(), portState.State.begin(), portState.State.end());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t ReadPort(int i)
|
||||
{
|
||||
return _ports[i]->ReadRam(0x4016);
|
||||
}
|
||||
|
||||
public:
|
||||
ControllerHub(Emulator* emu, ControllerType type, int port, ControllerConfig controllers[]) : BaseControlDevice(emu, type, port)
|
||||
{
|
||||
for(int i = 0; i < HubPortCount; i++) {
|
||||
switch(controllers[i].Type) {
|
||||
case ControllerType::FamicomController:
|
||||
case ControllerType::NesController:
|
||||
_ports[i].reset(new NesController(emu, controllers[i].Type, 0, controllers[i].Keys));
|
||||
break;
|
||||
|
||||
case ControllerType::SnesController:
|
||||
_ports[i].reset(new SnesController(emu, 0, controllers[i].Keys));
|
||||
break;
|
||||
|
||||
case ControllerType::SnesMouse:
|
||||
_ports[i].reset(new SnesMouse(emu, 0));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void WriteRam(uint16_t addr, uint8_t value) override
|
||||
{
|
||||
StrobeProcessWrite(value);
|
||||
for(int i = 0; i < HubPortCount; i++) {
|
||||
if(_ports[i]) {
|
||||
_ports[i]->WriteRam(addr, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DrawController(InputHud& hud) override
|
||||
{
|
||||
for(int i = 0; i < HubPortCount; i++) {
|
||||
if(_ports[i]) {
|
||||
_ports[i]->DrawController(hud);
|
||||
} else {
|
||||
hud.EndDrawController();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SetTextState(string state) override
|
||||
{
|
||||
vector<string> portStates = StringUtilities::Split(state, ':');
|
||||
int i = 0;
|
||||
for(string& portState : portStates) {
|
||||
if(_ports[i]) {
|
||||
_ports[i]->SetTextState(portState);
|
||||
}
|
||||
i++;
|
||||
}
|
||||
|
||||
UpdateStateFromPorts();
|
||||
}
|
||||
|
||||
string GetTextState() override
|
||||
{
|
||||
auto lock = _stateLock.AcquireSafe();
|
||||
|
||||
string state;
|
||||
for(int i = 0; i < HubPortCount; i++) {
|
||||
if(i != 0) {
|
||||
state += ":";
|
||||
}
|
||||
if(_ports[i]) {
|
||||
state += _ports[i]->GetTextState();
|
||||
}
|
||||
}
|
||||
|
||||
return state;
|
||||
}
|
||||
|
||||
void SetRawState(ControlDeviceState state) override
|
||||
{
|
||||
auto lock = _stateLock.AcquireSafe();
|
||||
_state = state;
|
||||
|
||||
vector<uint8_t> data = state.State;
|
||||
int pos = 0;
|
||||
|
||||
for(int i = 0; i < HubPortCount; i++) {
|
||||
if(_ports[i]) {
|
||||
int length = data[pos++];
|
||||
|
||||
ControlDeviceState portState;
|
||||
portState.State.insert(portState.State.begin(), data.begin() + pos, data.begin() + pos + length);
|
||||
_ports[i]->SetRawState(portState);
|
||||
pos += length;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
|
@ -59,6 +59,24 @@ void InputHud::DrawNumber(int number, int x, int y)
|
|||
_hud->DrawLine(x + _xOffset, 4 + y + _yOffset, x+2 + _xOffset, 4 + y + _yOffset, color[0], 1);
|
||||
break;
|
||||
|
||||
case 6:
|
||||
_hud->DrawLine(x + _xOffset, y + _yOffset, x + 2 + _xOffset, y + _yOffset, color[0], 1);
|
||||
_hud->DrawLine(x + _xOffset, 1 + y + _yOffset, x + _xOffset, y + _yOffset + 3, color[0], 1);
|
||||
_hud->DrawLine(x + _xOffset, 2 + y + _yOffset, x + 2 + _xOffset, 2 + y + _yOffset, color[0], 1);
|
||||
_hud->DrawPixel(x + 2 + _xOffset, 3 + y + _yOffset, color[0], 1);
|
||||
_hud->DrawLine(x + _xOffset, 4 + y + _yOffset, x + 2 + _xOffset, 4 + y + _yOffset, color[0], 1);
|
||||
break;
|
||||
|
||||
case 7:
|
||||
_hud->DrawLine(x + _xOffset, y + _yOffset, x + 2 + _xOffset, y + _yOffset, color[0], 1);
|
||||
_hud->DrawLine(x + _xOffset + 2, y + _yOffset, x + _xOffset + 2, 4 + y + _yOffset, color[0], 1);
|
||||
break;
|
||||
|
||||
case 8:
|
||||
_hud->DrawRectangle(x + _xOffset, y + _yOffset, 3, 5, color[0], false, 1);
|
||||
_hud->DrawPixel(x + _xOffset + 1, y + _yOffset + 2, color[0], 1);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
@ -108,7 +126,6 @@ void InputHud::DrawController(ControllerData& data)
|
|||
|
||||
controller->SetRawState(data.State);
|
||||
controller->DrawController(*this);
|
||||
EndDrawController();
|
||||
}
|
||||
|
||||
void InputHud::EndDrawController()
|
||||
|
@ -152,6 +169,8 @@ void InputHud::EndDrawController()
|
|||
_outlineWidth = 0;
|
||||
_outlineHeight = 0;
|
||||
}
|
||||
|
||||
_controllerIndex++;
|
||||
}
|
||||
|
||||
void InputHud::DrawControllers(FrameInfo size, vector<ControllerData> controllerData)
|
||||
|
@ -178,6 +197,7 @@ void InputHud::DrawControllers(FrameInfo size, vector<ControllerData> controller
|
|||
break;
|
||||
}
|
||||
|
||||
_controllerIndex = 0;
|
||||
for(ControllerData& portData : controllerData) {
|
||||
DrawController(portData);
|
||||
}
|
||||
|
|
|
@ -16,6 +16,7 @@ private:
|
|||
int _yOffset = 0;
|
||||
int _outlineWidth = 0;
|
||||
int _outlineHeight = 0;
|
||||
int _controllerIndex = 0;
|
||||
|
||||
void DrawController(ControllerData& data);
|
||||
|
||||
|
@ -27,5 +28,7 @@ public:
|
|||
void DrawNumber(int number, int x, int y);
|
||||
void EndDrawController();
|
||||
|
||||
int GetControllerIndex() { return _controllerIndex; }
|
||||
|
||||
void DrawControllers(FrameInfo size, vector<ControllerData> controllerData);
|
||||
};
|
|
@ -163,12 +163,12 @@ enum class ControllerType
|
|||
NesArkanoidController,
|
||||
PowerPad,
|
||||
SuborMouse,
|
||||
VsZapper,
|
||||
VirtualBoyController,
|
||||
|
||||
//NES/Famicon expansion devices
|
||||
FourScore,
|
||||
FamicomZapper,
|
||||
TwoPlayerAdapter,
|
||||
FourPlayerAdapter,
|
||||
FamicomArkanoidController,
|
||||
OekaKidsTablet,
|
||||
|
@ -279,7 +279,7 @@ struct InputConfig
|
|||
uint32_t MouseSensitivity = 1;
|
||||
|
||||
InputDisplayPosition DisplayInputPosition = InputDisplayPosition::TopLeft;
|
||||
bool DisplayInputPort[5] = { false, false, false, false, false};
|
||||
bool DisplayInputPort[8] = { };
|
||||
bool DisplayInputHorizontally = true;
|
||||
};
|
||||
|
||||
|
@ -347,7 +347,11 @@ struct GameboyConfig
|
|||
|
||||
struct SnesConfig
|
||||
{
|
||||
ControllerConfig Controllers[5];
|
||||
ControllerConfig Port1;
|
||||
ControllerConfig Port2;
|
||||
|
||||
ControllerConfig Port1SubPorts[4];
|
||||
ControllerConfig Port2SubPorts[4];
|
||||
|
||||
ConsoleRegion Region = ConsoleRegion::Auto;
|
||||
|
||||
|
@ -402,7 +406,13 @@ enum class NesConsoleType
|
|||
|
||||
struct NesConfig
|
||||
{
|
||||
ControllerConfig Controllers[5] = {};
|
||||
ControllerConfig Port1;
|
||||
ControllerConfig Port2;
|
||||
ControllerConfig ExpPort;
|
||||
|
||||
ControllerConfig Port1SubPorts[4];
|
||||
ControllerConfig ExpPortSubPorts[4];
|
||||
|
||||
uint32_t ZapperDetectionRadius = 0;
|
||||
|
||||
ConsoleRegion Region = ConsoleRegion::Auto;
|
||||
|
|
|
@ -22,6 +22,9 @@ namespace Mesen.Config
|
|||
[Reactive] public bool DisplayInputPort3 { get; set; } = false;
|
||||
[Reactive] public bool DisplayInputPort4 { get; set; } = false;
|
||||
[Reactive] public bool DisplayInputPort5 { get; set; } = false;
|
||||
[Reactive] public bool DisplayInputPort6 { get; set; } = false;
|
||||
[Reactive] public bool DisplayInputPort7 { get; set; } = false;
|
||||
[Reactive] public bool DisplayInputPort8 { get; set; } = false;
|
||||
[Reactive] public bool DisplayInputHorizontally { get; set; } = true;
|
||||
|
||||
public InputConfig()
|
||||
|
@ -39,6 +42,9 @@ namespace Mesen.Config
|
|||
DisplayInputPort3 = this.DisplayInputPort3,
|
||||
DisplayInputPort4 = this.DisplayInputPort4,
|
||||
DisplayInputPort5 = this.DisplayInputPort5,
|
||||
DisplayInputPort6 = this.DisplayInputPort6,
|
||||
DisplayInputPort7 = this.DisplayInputPort7,
|
||||
DisplayInputPort8 = this.DisplayInputPort8,
|
||||
DisplayInputHorizontally = this.DisplayInputHorizontally
|
||||
});
|
||||
}
|
||||
|
@ -193,6 +199,9 @@ namespace Mesen.Config
|
|||
[MarshalAs(UnmanagedType.I1)] public bool DisplayInputPort3;
|
||||
[MarshalAs(UnmanagedType.I1)] public bool DisplayInputPort4;
|
||||
[MarshalAs(UnmanagedType.I1)] public bool DisplayInputPort5;
|
||||
[MarshalAs(UnmanagedType.I1)] public bool DisplayInputPort6;
|
||||
[MarshalAs(UnmanagedType.I1)] public bool DisplayInputPort7;
|
||||
[MarshalAs(UnmanagedType.I1)] public bool DisplayInputPort8;
|
||||
[MarshalAs(UnmanagedType.I1)] public bool DisplayInputHorizontally;
|
||||
}
|
||||
|
||||
|
@ -259,12 +268,12 @@ namespace Mesen.Config
|
|||
NesArkanoidController,
|
||||
PowerPad,
|
||||
SuborMouse,
|
||||
VsZapper,
|
||||
VbController,
|
||||
|
||||
//NES/Famicon expansion devices
|
||||
FourScore,
|
||||
FamicomZapper,
|
||||
TwoPlayerAdapter,
|
||||
FourPlayerAdapter,
|
||||
FamicomArkanoidController,
|
||||
OekaKidsTablet,
|
||||
|
@ -288,6 +297,33 @@ namespace Mesen.Config
|
|||
|
||||
public static class ControllerTypeExtensions
|
||||
{
|
||||
public static bool HasPresets(this ControllerType type)
|
||||
{
|
||||
switch(type) {
|
||||
case ControllerType.SnesController:
|
||||
case ControllerType.NesController:
|
||||
case ControllerType.GameboyController:
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public static bool HasTurbo(this ControllerType type)
|
||||
{
|
||||
switch(type) {
|
||||
case ControllerType.SnesController:
|
||||
case ControllerType.NesController:
|
||||
case ControllerType.GameboyController:
|
||||
case ControllerType.HoriTrack:
|
||||
case ControllerType.Pachinko:
|
||||
case ControllerType.KonamiHyperShot:
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public static bool CanConfigure(this ControllerType type)
|
||||
{
|
||||
switch(type) {
|
||||
|
@ -303,6 +339,9 @@ namespace Mesen.Config
|
|||
case ControllerType.JissenMahjong:
|
||||
case ControllerType.ExcitingBoxing:
|
||||
case ControllerType.GameboyController:
|
||||
//TODO
|
||||
//case ControllerType.HoriTrack:
|
||||
//case ControllerType.KonamiHyperShot:
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
@ -13,7 +13,20 @@ namespace Mesen.Config
|
|||
public class NesConfig : BaseConfig<NesConfig>
|
||||
{
|
||||
//Input
|
||||
[Reactive] public List<NesControllerConfig> Controllers { get; set; } = new List<NesControllerConfig> { new (), new (), new (), new (), new () };
|
||||
[Reactive] public NesControllerConfig Port1 { get; set; } = new();
|
||||
[Reactive] public NesControllerConfig Port2 { get; set; } = new();
|
||||
[Reactive] public NesControllerConfig ExpPort { get; set; } = new();
|
||||
|
||||
[Reactive] public NesControllerConfig Port1A { get; set; } = new();
|
||||
[Reactive] public NesControllerConfig Port1B { get; set; } = new();
|
||||
[Reactive] public NesControllerConfig Port1C { get; set; } = new();
|
||||
[Reactive] public NesControllerConfig Port1D { get; set; } = new();
|
||||
|
||||
[Reactive] public NesControllerConfig ExpPortA { get; set; } = new();
|
||||
[Reactive] public NesControllerConfig ExpPortB { get; set; } = new();
|
||||
[Reactive] public NesControllerConfig ExpPortC { get; set; } = new();
|
||||
[Reactive] public NesControllerConfig ExpPortD { get; set; } = new();
|
||||
|
||||
[Reactive] public UInt32 ZapperDetectionRadius { get; set; } = 0;
|
||||
|
||||
//General
|
||||
|
@ -111,13 +124,20 @@ namespace Mesen.Config
|
|||
bool isFullPalette = UserPalette.Length == 512;
|
||||
|
||||
ConfigApi.SetNesConfig(new InteropNesConfig() {
|
||||
Controllers = new InteropControllerConfig[5] {
|
||||
this.Controllers[0].ToInterop(),
|
||||
this.Controllers[1].ToInterop(),
|
||||
this.Controllers[2].ToInterop(),
|
||||
this.Controllers[3].ToInterop(),
|
||||
this.Controllers[4].ToInterop()
|
||||
},
|
||||
Port1 = Port1.ToInterop(),
|
||||
Port1A = Port1A.ToInterop(),
|
||||
Port1B = Port1B.ToInterop(),
|
||||
Port1C = Port1C.ToInterop(),
|
||||
Port1D = Port1D.ToInterop(),
|
||||
|
||||
Port2 = Port2.ToInterop(),
|
||||
|
||||
ExpPort = ExpPort.ToInterop(),
|
||||
ExpPortA = ExpPortA.ToInterop(),
|
||||
ExpPortB = ExpPortB.ToInterop(),
|
||||
ExpPortC = ExpPortC.ToInterop(),
|
||||
ExpPortD = ExpPortD.ToInterop(),
|
||||
|
||||
ZapperDetectionRadius = ZapperDetectionRadius,
|
||||
|
||||
Region = Region,
|
||||
|
@ -230,16 +250,16 @@ namespace Mesen.Config
|
|||
mappings.Add(mapping);
|
||||
}
|
||||
|
||||
Controllers[0].Type = ControllerType.NesController;
|
||||
Controllers[0].TurboSpeed = 2;
|
||||
Port1.Type = ControllerType.NesController;
|
||||
Port1.TurboSpeed = 2;
|
||||
if(mappings.Count > 0) {
|
||||
Controllers[0].Mapping1 = mappings[0];
|
||||
Port1.Mapping1 = mappings[0];
|
||||
if(mappings.Count > 1) {
|
||||
Controllers[0].Mapping2 = mappings[1];
|
||||
Port1.Mapping2 = mappings[1];
|
||||
if(mappings.Count > 2) {
|
||||
Controllers[0].Mapping3 = mappings[2];
|
||||
Port1.Mapping3 = mappings[2];
|
||||
if(mappings.Count > 3) {
|
||||
Controllers[0].Mapping4 = mappings[3];
|
||||
Port1.Mapping4 = mappings[3];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -250,7 +270,20 @@ namespace Mesen.Config
|
|||
[StructLayout(LayoutKind.Sequential)]
|
||||
public struct InteropNesConfig
|
||||
{
|
||||
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 5)] public InteropControllerConfig[] Controllers;
|
||||
public InteropControllerConfig Port1;
|
||||
public InteropControllerConfig Port2;
|
||||
public InteropControllerConfig ExpPort;
|
||||
|
||||
public InteropControllerConfig Port1A;
|
||||
public InteropControllerConfig Port1B;
|
||||
public InteropControllerConfig Port1C;
|
||||
public InteropControllerConfig Port1D;
|
||||
|
||||
public InteropControllerConfig ExpPortA;
|
||||
public InteropControllerConfig ExpPortB;
|
||||
public InteropControllerConfig ExpPortC;
|
||||
public InteropControllerConfig ExpPortD;
|
||||
|
||||
public UInt32 ZapperDetectionRadius;
|
||||
|
||||
public ConsoleRegion Region;
|
||||
|
|
|
@ -12,7 +12,18 @@ namespace Mesen.Config
|
|||
public class SnesConfig : BaseConfig<SnesConfig>
|
||||
{
|
||||
//Input
|
||||
[Reactive] public List<ControllerConfig> Controllers { get; set; } = new List<ControllerConfig> { new ControllerConfig(), new ControllerConfig(), new ControllerConfig(), new ControllerConfig(), new ControllerConfig() };
|
||||
[Reactive] public ControllerConfig Port1 { get; set; } = new ControllerConfig();
|
||||
[Reactive] public ControllerConfig Port2 { get; set; } = new ControllerConfig();
|
||||
|
||||
[Reactive] public ControllerConfig Port1A { get; set; } = new ControllerConfig();
|
||||
[Reactive] public ControllerConfig Port1B { get; set; } = new ControllerConfig();
|
||||
[Reactive] public ControllerConfig Port1C { get; set; } = new ControllerConfig();
|
||||
[Reactive] public ControllerConfig Port1D { get; set; } = new ControllerConfig();
|
||||
|
||||
[Reactive] public ControllerConfig Port2A { get; set; } = new ControllerConfig();
|
||||
[Reactive] public ControllerConfig Port2B { get; set; } = new ControllerConfig();
|
||||
[Reactive] public ControllerConfig Port2C { get; set; } = new ControllerConfig();
|
||||
[Reactive] public ControllerConfig Port2D { get; set; } = new ControllerConfig();
|
||||
|
||||
[Reactive] public ConsoleRegion Region { get; set; } = ConsoleRegion.Auto;
|
||||
|
||||
|
@ -50,23 +61,18 @@ namespace Mesen.Config
|
|||
|
||||
public void ApplyConfig()
|
||||
{
|
||||
while(Controllers.Count < 5) {
|
||||
Controllers.Add(new ControllerConfig());
|
||||
}
|
||||
|
||||
//Force SNES controllers for multitap
|
||||
Controllers[2].Type = ControllerType.SnesController;
|
||||
Controllers[3].Type = ControllerType.SnesController;
|
||||
Controllers[4].Type = ControllerType.SnesController;
|
||||
|
||||
ConfigApi.SetSnesConfig(new InteropSnesConfig() {
|
||||
Controllers = new InteropControllerConfig[5] {
|
||||
this.Controllers[0].ToInterop(),
|
||||
this.Controllers[1].ToInterop(),
|
||||
this.Controllers[2].ToInterop(),
|
||||
this.Controllers[3].ToInterop(),
|
||||
this.Controllers[4].ToInterop()
|
||||
},
|
||||
Port1 = Port1.ToInterop(),
|
||||
Port1A = Port1A.ToInterop(),
|
||||
Port1B = Port1B.ToInterop(),
|
||||
Port1C = Port1C.ToInterop(),
|
||||
Port1D = Port1D.ToInterop(),
|
||||
|
||||
Port2 = Port2.ToInterop(),
|
||||
Port2A = Port2A.ToInterop(),
|
||||
Port2B = Port2B.ToInterop(),
|
||||
Port2C = Port2C.ToInterop(),
|
||||
Port2D = Port2D.ToInterop(),
|
||||
|
||||
Region = this.Region,
|
||||
|
||||
|
@ -119,16 +125,16 @@ namespace Mesen.Config
|
|||
mappings.Add(mapping);
|
||||
}
|
||||
|
||||
Controllers[0].Type = ControllerType.SnesController;
|
||||
Controllers[0].TurboSpeed = 2;
|
||||
Port1.Type = ControllerType.SnesController;
|
||||
Port1.TurboSpeed = 2;
|
||||
if(mappings.Count > 0) {
|
||||
Controllers[0].Mapping1 = mappings[0];
|
||||
Port1.Mapping1 = mappings[0];
|
||||
if(mappings.Count > 1) {
|
||||
Controllers[0].Mapping2 = mappings[1];
|
||||
Port1.Mapping2 = mappings[1];
|
||||
if(mappings.Count > 2) {
|
||||
Controllers[0].Mapping3 = mappings[2];
|
||||
Port1.Mapping3 = mappings[2];
|
||||
if(mappings.Count > 3) {
|
||||
Controllers[0].Mapping4 = mappings[3];
|
||||
Port1.Mapping4 = mappings[3];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -139,8 +145,18 @@ namespace Mesen.Config
|
|||
[StructLayout(LayoutKind.Sequential)]
|
||||
public struct InteropSnesConfig
|
||||
{
|
||||
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 5)]
|
||||
public InteropControllerConfig[] Controllers;
|
||||
public InteropControllerConfig Port1;
|
||||
public InteropControllerConfig Port2;
|
||||
|
||||
public InteropControllerConfig Port1A;
|
||||
public InteropControllerConfig Port1B;
|
||||
public InteropControllerConfig Port1C;
|
||||
public InteropControllerConfig Port1D;
|
||||
|
||||
public InteropControllerConfig Port2A;
|
||||
public InteropControllerConfig Port2B;
|
||||
public InteropControllerConfig Port2C;
|
||||
public InteropControllerConfig Port2D;
|
||||
|
||||
public ConsoleRegion Region;
|
||||
|
||||
|
|
29
NewUI/Controls/InputComboBox.axaml
Normal file
29
NewUI/Controls/InputComboBox.axaml
Normal file
|
@ -0,0 +1,29 @@
|
|||
<UserControl
|
||||
xmlns="https://github.com/avaloniaui"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:l="using:Mesen.Localization"
|
||||
xmlns:c="using:Mesen.Controls"
|
||||
mc:Ignorable="d"
|
||||
d:DesignHeight="55"
|
||||
x:Name="root"
|
||||
HorizontalAlignment="Stretch"
|
||||
x:Class="Mesen.Controls.InputComboBox"
|
||||
>
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<c:EnumComboBox
|
||||
Margin="5 0"
|
||||
Width="190"
|
||||
VerticalAlignment="Center"
|
||||
SelectedItem="{CompiledBinding ControllerType, ElementName=root}"
|
||||
AvailableValues="{CompiledBinding AvailableValues, ElementName=root}"
|
||||
/>
|
||||
<Button
|
||||
VerticalAlignment="Center"
|
||||
Content="{l:Translate btnSetup}"
|
||||
Click="btnSetup_Click"
|
||||
IsEnabled="{CompiledBinding SetupEnabled, ElementName=root}"
|
||||
/>
|
||||
</StackPanel>
|
||||
</UserControl>
|
81
NewUI/Controls/InputComboBox.axaml.cs
Normal file
81
NewUI/Controls/InputComboBox.axaml.cs
Normal file
|
@ -0,0 +1,81 @@
|
|||
using Avalonia;
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Data;
|
||||
using Avalonia.Interactivity;
|
||||
using Avalonia.Markup.Xaml;
|
||||
using Mesen.Config;
|
||||
using Mesen.Utilities;
|
||||
using Mesen.ViewModels;
|
||||
using Mesen.Windows;
|
||||
using System;
|
||||
|
||||
namespace Mesen.Controls
|
||||
{
|
||||
public class InputComboBox : UserControl
|
||||
{
|
||||
public static readonly StyledProperty<ControllerType> ControllerTypeProperty = AvaloniaProperty.Register<InputComboBox, ControllerType>(nameof(ControllerType), defaultBindingMode: BindingMode.TwoWay);
|
||||
public static readonly StyledProperty<ControllerConfig> ConfigProperty = AvaloniaProperty.Register<InputComboBox, ControllerConfig>(nameof(Config), defaultBindingMode: BindingMode.TwoWay);
|
||||
|
||||
public static readonly StyledProperty<Enum[]> AvailableValuesProperty = AvaloniaProperty.Register<InputComboBox, Enum[]>(nameof(AvailableValues));
|
||||
public static readonly StyledProperty<bool> SetupEnabledProperty = AvaloniaProperty.Register<InputComboBox, bool>(nameof(SetupEnabled));
|
||||
|
||||
public ControllerType ControllerType
|
||||
{
|
||||
get { return GetValue(ControllerTypeProperty); }
|
||||
set { SetValue(ControllerTypeProperty, value); }
|
||||
}
|
||||
|
||||
public ControllerConfig Config
|
||||
{
|
||||
get { return GetValue(ConfigProperty); }
|
||||
set { SetValue(ConfigProperty, value); }
|
||||
}
|
||||
|
||||
public bool SetupEnabled
|
||||
{
|
||||
get { return GetValue(SetupEnabledProperty); }
|
||||
set { SetValue(SetupEnabledProperty, value); }
|
||||
}
|
||||
|
||||
public Enum[] AvailableValues
|
||||
{
|
||||
get { return GetValue(AvailableValuesProperty); }
|
||||
set { SetValue(AvailableValuesProperty, value); }
|
||||
}
|
||||
|
||||
static InputComboBox()
|
||||
{
|
||||
ControllerTypeProperty.Changed.AddClassHandler<InputComboBox>((x, e) => {
|
||||
x.SetupEnabled = x.ControllerType.CanConfigure();
|
||||
});
|
||||
}
|
||||
|
||||
public InputComboBox()
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
|
||||
private void InitializeComponent()
|
||||
{
|
||||
AvaloniaXamlLoader.Load(this);
|
||||
}
|
||||
|
||||
private async void btnSetup_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
Button btn = (Button)sender;
|
||||
|
||||
PixelPoint startPosition = btn.PointToScreen(new Point(-7, btn.Height));
|
||||
|
||||
ControllerConfigWindow wnd = new ControllerConfigWindow();
|
||||
wnd.WindowStartupLocation = WindowStartupLocation.Manual;
|
||||
wnd.Position = startPosition;
|
||||
|
||||
ControllerConfig cfg = JsonHelper.Clone(Config);
|
||||
wnd.DataContext = new ControllerConfigViewModel(ControllerType, cfg);
|
||||
|
||||
if(await wnd.ShowDialog<bool>(btn.Parent?.VisualRoot as Window)) {
|
||||
Config = cfg;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -68,36 +68,39 @@
|
|||
<Form ID="NesInputConfigView" Title="Input Settings">
|
||||
<Control ID="grpControllers">Controllers</Control>
|
||||
<Control ID="grpHardware">Hardware buttons</Control>
|
||||
|
||||
<Control ID="lblFourScoreConfig">Four Score Configuration</Control>
|
||||
<Control ID="lblFourPlayerAdapterConfig">4-Player Adapter Configuration</Control>
|
||||
<Control ID="lblTwoPlayerAdapterConfig">2-Player Adapter Configuration</Control>
|
||||
|
||||
<Control ID="btnSetup">Setup</Control>
|
||||
<Control ID="lblPlayer1">Player 1:</Control>
|
||||
<Control ID="lblPlayer2">Player 2:</Control>
|
||||
<Control ID="lblPlayer3">Player 3:</Control>
|
||||
<Control ID="lblPlayer4">Player 4:</Control>
|
||||
<Control ID="lblPlayer1">Port 1:</Control>
|
||||
<Control ID="lblPlayer2">Port 2:</Control>
|
||||
|
||||
<Control ID="lblPort1A">Port 1:</Control>
|
||||
<Control ID="lblPort1B">Port 2:</Control>
|
||||
<Control ID="lblPort1C">Port 3:</Control>
|
||||
<Control ID="lblPort1D">Port 4:</Control>
|
||||
|
||||
<Control ID="lblExpPortA">Port 1:</Control>
|
||||
<Control ID="lblExpPortB">Port 2:</Control>
|
||||
<Control ID="lblExpPortC">Port 3:</Control>
|
||||
<Control ID="lblExpPortD">Port 4:</Control>
|
||||
|
||||
<Control ID="lblNesType">Console Type:</Control>
|
||||
<Control ID="lblExpansionDevice">Expansion device:</Control>
|
||||
<Control ID="chkFourScore">Use Four Score accessory</Control>
|
||||
<Control ID="chkAutoConfigureInput">Automatically configure controllers when loading a game</Control>
|
||||
</Form>
|
||||
<Form ID="SnesInputConfigView" Title="Input Settings">
|
||||
<Control ID="tpgControllers">Controllers</Control>
|
||||
<Control ID="btnSetupExp">Setup</Control>
|
||||
<Control ID="btnSetupCartridge">Setup</Control>
|
||||
<Control ID="lblCartridge">Cartridge:</Control>
|
||||
<Control ID="lblPlayer1">Player 1:</Control>
|
||||
<Control ID="lblPlayer2">Player 2:</Control>
|
||||
<Control ID="lblMultitap1">Player 2:</Control>
|
||||
<Control ID="lblMultitap2">Player 3:</Control>
|
||||
<Control ID="lblMultitap3">Player 4:</Control>
|
||||
<Control ID="lblMultitap4">Player 5:</Control>
|
||||
<Control ID="btnSetupP1">Setup</Control>
|
||||
<Control ID="btnSetupP2">Setup</Control>
|
||||
<Control ID="lblPort1">Port 1:</Control>
|
||||
<Control ID="lblPort2">Port 2:</Control>
|
||||
<Control ID="lblMultitap1A">Port 1:</Control>
|
||||
<Control ID="lblMultitap1B">Port 2:</Control>
|
||||
<Control ID="lblMultitap1C">Port 3:</Control>
|
||||
<Control ID="lblMultitap1D">Port 4:</Control>
|
||||
<Control ID="lblMultitap2A">Port 1:</Control>
|
||||
<Control ID="lblMultitap2B">Port 2:</Control>
|
||||
<Control ID="lblMultitap2C">Port 3:</Control>
|
||||
<Control ID="lblMultitap2D">Port 4:</Control>
|
||||
<Control ID="grpMultitap">Multitap Configuration</Control>
|
||||
<Control ID="btnSetupMultitap1">Setup</Control>
|
||||
<Control ID="btnSetupMultitap2">Setup</Control>
|
||||
<Control ID="btnSetupMultitap3">Setup</Control>
|
||||
<Control ID="btnSetupMultitap4">Setup</Control>
|
||||
<Control ID="lblKeyBinding">Warning: Your current configuration contains conflicting key bindings - some physical buttons on your keyboard or gamepad are mapped to multiple buttons on the NES controller. If this is not intentional, please review and correct your key bindings.</Control>
|
||||
</Form>
|
||||
<Form ID="InputConfigView">
|
||||
|
@ -112,16 +115,22 @@
|
|||
|
||||
<Control ID="tpgDisplay">Display</Control>
|
||||
<Control ID="lblDisplayInputPorts">Display ports: </Control>
|
||||
<Control ID="chkDisplayPort1">Port 1</Control>
|
||||
<Control ID="chkDisplayPort2">Port 2</Control>
|
||||
<Control ID="chkDisplayPort3">Port 3</Control>
|
||||
<Control ID="chkDisplayPort4">Port 4</Control>
|
||||
<Control ID="chkDisplayPort5">Port 5</Control>
|
||||
<Control ID="chkDisplayPort1">Player 1</Control>
|
||||
<Control ID="chkDisplayPort2">Player 2</Control>
|
||||
<Control ID="chkDisplayPort3">Player 3</Control>
|
||||
<Control ID="chkDisplayPort4">Player 4</Control>
|
||||
<Control ID="chkDisplayPort5">Player 5</Control>
|
||||
<Control ID="chkDisplayPort6">Player 6</Control>
|
||||
<Control ID="chkDisplayPort7">Player 7</Control>
|
||||
<Control ID="chkDisplayPort8">Player 8</Control>
|
||||
<Control ID="lblDisplayPosition">Display position:</Control>
|
||||
<Control ID="chkDisplayInputHorizontally">Horizontal display</Control>
|
||||
|
||||
<Control ID="chkHideMousePointerForZapper">Hide mouse pointer when using zapper</Control>
|
||||
</Form>
|
||||
<Form ID="InputComboBox">
|
||||
<Control ID="btnSetup">Setup</Control>
|
||||
</Form>
|
||||
<Form ID="OverscanConfig">
|
||||
<Control ID="lblLeft">Left</Control>
|
||||
<Control ID="lblTop">Top</Control>
|
||||
|
@ -1319,7 +1328,8 @@ x == [$150] || y == [10]
|
|||
|
||||
<Value ID="FourScore">Four Score (NES)</Value>
|
||||
<Value ID="FamicomZapper">Zapper (Famicom)</Value>
|
||||
<Value ID="FourPlayerAdapter">Four Player Adapter (Famicom)</Value>
|
||||
<Value ID="TwoPlayerAdapter">2-Player Adapter (Famicom)</Value>
|
||||
<Value ID="FourPlayerAdapter">4-Player Adapter (Famicom)</Value>
|
||||
<Value ID="FamicomArkanoidController">Arkanoid Controller (Famicom)</Value>
|
||||
<Value ID="OekaKidsTablet">Oeka Kids Tablet</Value>
|
||||
<Value ID="FamilyTrainerMat">Family Trainer</Value>
|
||||
|
|
|
@ -183,6 +183,9 @@
|
|||
<Compile Update="Controls\ButtonWithIcon.axaml.cs">
|
||||
<DependentUpon>ButtonWithIcon.axaml</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Update="Controls\InputComboBox.axaml.cs">
|
||||
<DependentUpon>InputComboBox.axaml</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Update="Controls\IconButton.axaml.cs">
|
||||
<DependentUpon>IconButton.axaml</DependentUpon>
|
||||
</Compile>
|
||||
|
|
|
@ -19,11 +19,11 @@ namespace Mesen.Utilities
|
|||
IncludeFields = true
|
||||
};
|
||||
|
||||
public static T Clone<T>(T obj)
|
||||
public static T Clone<T>(T obj) where T : notnull
|
||||
{
|
||||
using MemoryStream stream = new MemoryStream();
|
||||
byte[] jsonData = JsonSerializer.SerializeToUtf8Bytes(obj, JsonHelper.Options);
|
||||
T? clone = JsonSerializer.Deserialize<T>(jsonData, JsonHelper.Options);
|
||||
T? clone = (T?)JsonSerializer.Deserialize(jsonData.AsSpan<byte>(), obj.GetType(), JsonHelper.Options);
|
||||
if(clone == null) {
|
||||
throw new Exception("Invalid object");
|
||||
}
|
||||
|
|
|
@ -12,7 +12,8 @@ namespace Mesen.ViewModels
|
|||
{
|
||||
public class ControllerConfigViewModel : ViewModelBase
|
||||
{
|
||||
[Reactive] public ControllerConfig Config { get; set; }
|
||||
public ControllerConfig Config { get; }
|
||||
public ControllerType Type { get; }
|
||||
|
||||
[Reactive] public KeyMappingViewModel KeyMapping1 { get; set; }
|
||||
[Reactive] public KeyMappingViewModel KeyMapping2 { get; set; }
|
||||
|
@ -23,19 +24,20 @@ namespace Mesen.ViewModels
|
|||
[Reactive] public bool ShowTurbo { get; set; } = false;
|
||||
|
||||
[Obsolete("For designer only")]
|
||||
public ControllerConfigViewModel() : this(new ControllerConfig()) { }
|
||||
public ControllerConfigViewModel() : this(ControllerType.SnesController, new ControllerConfig()) { }
|
||||
|
||||
public ControllerConfigViewModel(ControllerConfig config)
|
||||
public ControllerConfigViewModel(ControllerType type, ControllerConfig config)
|
||||
{
|
||||
Config = config;
|
||||
Type = type;
|
||||
|
||||
KeyMapping1 = new KeyMappingViewModel(config.Type, config.Mapping1);
|
||||
KeyMapping2 = new KeyMappingViewModel(config.Type, config.Mapping2);
|
||||
KeyMapping3 = new KeyMappingViewModel(config.Type, config.Mapping3);
|
||||
KeyMapping4 = new KeyMappingViewModel(config.Type, config.Mapping4);
|
||||
KeyMapping1 = new KeyMappingViewModel(type, config.Mapping1);
|
||||
KeyMapping2 = new KeyMappingViewModel(type, config.Mapping2);
|
||||
KeyMapping3 = new KeyMappingViewModel(type, config.Mapping3);
|
||||
KeyMapping4 = new KeyMappingViewModel(type, config.Mapping4);
|
||||
|
||||
ShowPresets = config.Type == ControllerType.SnesController || config.Type == ControllerType.NesController || config.Type == ControllerType.GameboyController;
|
||||
ShowTurbo = config.Type == ControllerType.SnesController || config.Type == ControllerType.NesController || config.Type == ControllerType.GameboyController;
|
||||
ShowPresets = type.HasPresets();
|
||||
ShowTurbo = type.HasTurbo();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -39,7 +39,7 @@ namespace Mesen.ViewModels
|
|||
wnd.Position = startPosition;
|
||||
|
||||
ControllerConfig cfg = JsonHelper.Clone(this.Config.Controller);
|
||||
wnd.DataContext = new ControllerConfigViewModel(cfg);
|
||||
wnd.DataContext = new ControllerConfigViewModel(ControllerType.GameboyController, cfg);
|
||||
|
||||
if(await wnd.ShowDialog<bool>(btn.Parent?.VisualRoot as Window)) {
|
||||
Config.Controller = cfg;
|
||||
|
|
|
@ -37,6 +37,7 @@ namespace Mesen.ViewModels
|
|||
return;
|
||||
}
|
||||
|
||||
AddDisposable(Input);
|
||||
AddDisposable(ReactiveHelper.RegisterRecursiveObserver(Config, (s, e) => { Config.ApplyConfig(); }));
|
||||
AddDisposable(this.WhenAnyValue(x => x.Config.StereoFilter).Select(x => x == StereoFilter.Delay).ToPropertyEx(this, x => x.IsDelayStereoEffect));
|
||||
AddDisposable(this.WhenAnyValue(x => x.Config.StereoFilter).Select(x => x == StereoFilter.Panning).ToPropertyEx(this, x => x.IsPanningStereoEffect));
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
#pragma warning disable CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable.
|
||||
|
||||
using Avalonia;
|
||||
using Avalonia;
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Threading;
|
||||
using Mesen.Config;
|
||||
using Mesen.Config.Shortcuts;
|
||||
using Mesen.Localization;
|
||||
|
@ -21,24 +20,24 @@ using System.Threading.Tasks;
|
|||
|
||||
namespace Mesen.ViewModels
|
||||
{
|
||||
public class NesInputConfigViewModel : ViewModelBase
|
||||
public class NesInputConfigViewModel : DisposableViewModel
|
||||
{
|
||||
[Reactive] public NesConfig Config { get; set; }
|
||||
|
||||
public ReactiveCommand<Button, Unit> SetupPlayer1 { get; }
|
||||
public ReactiveCommand<Button, Unit> SetupPlayer2 { get; }
|
||||
public ReactiveCommand<Button, Unit> SetupPlayer3 { get; }
|
||||
public ReactiveCommand<Button, Unit> SetupPlayer4 { get; }
|
||||
public ReactiveCommand<Button, Unit> SetupPlayer5 { get; }
|
||||
public List<ShortcutKeyInfo> ShortcutKeys { get; set; }
|
||||
|
||||
[Reactive] public bool HasFourScore { get; private set; }
|
||||
[ObservableAsProperty] public bool HasFourPlayerAdapter { get; }
|
||||
[ObservableAsProperty] public bool HasExpansionHub { get; }
|
||||
[ObservableAsProperty] public string ExpConfigLabel { get; } = "";
|
||||
[ObservableAsProperty] public Enum[] AvailableControllerTypesExpansionHub { get; } = Array.Empty<Enum>();
|
||||
|
||||
public Enum[] AvailableControllerTypesP12 => new Enum[] {
|
||||
ControllerType.None,
|
||||
ControllerType.NesController,
|
||||
ControllerType.FamicomController,
|
||||
ControllerType.NesZapper,
|
||||
ControllerType.FourScore,
|
||||
ControllerType.NesArkanoidController,
|
||||
ControllerType.PowerPad,
|
||||
ControllerType.SnesController,
|
||||
|
@ -47,17 +46,25 @@ namespace Mesen.ViewModels
|
|||
ControllerType.VbController
|
||||
};
|
||||
|
||||
public Enum[] AvailableControllerTypesP34 => new Enum[] {
|
||||
public Enum[] AvailableControllerTypesFourPlayer => new Enum[] {
|
||||
ControllerType.None,
|
||||
ControllerType.NesController,
|
||||
ControllerType.FamicomController,
|
||||
ControllerType.SnesController
|
||||
};
|
||||
|
||||
public Enum[] AvailableControllerTypesTwoPlayer => new Enum[] {
|
||||
ControllerType.None,
|
||||
ControllerType.NesController,
|
||||
ControllerType.Pachinko,
|
||||
ControllerType.SnesController,
|
||||
ControllerType.SnesMouse,
|
||||
ControllerType.SuborMouse,
|
||||
ControllerType.VbController,
|
||||
};
|
||||
|
||||
public Enum[] AvailableExpansionTypes => new Enum[] {
|
||||
ControllerType.None,
|
||||
ControllerType.FourScore,
|
||||
ControllerType.FamicomZapper,
|
||||
ControllerType.TwoPlayerAdapter,
|
||||
ControllerType.FourPlayerAdapter,
|
||||
ControllerType.FamicomArkanoidController,
|
||||
ControllerType.OekaKidsTablet,
|
||||
|
@ -76,29 +83,42 @@ namespace Mesen.ViewModels
|
|||
ControllerType.BattleBox
|
||||
};
|
||||
|
||||
//For designer preview
|
||||
[Obsolete("For designer only")]
|
||||
public NesInputConfigViewModel() : this(new NesConfig(), new PreferencesConfig()) { }
|
||||
|
||||
public NesInputConfigViewModel(NesConfig config, PreferencesConfig preferences)
|
||||
{
|
||||
Config = config;
|
||||
|
||||
this.WhenAnyValue(x => x.Config.Controllers[4].Type).Select(t => t == ControllerType.FourPlayerAdapter || t == ControllerType.FourScore).ToPropertyEx(this, x => x.HasFourPlayerAdapter);
|
||||
AddDisposable(this.WhenAnyValue(x => x.Config.Port1.Type, x => x.Config.Port2.Type).Subscribe(t => {
|
||||
Dispatcher.UIThread.Post(() => {
|
||||
HasFourScore = Config.Port1.Type == ControllerType.FourScore || Config.Port2.Type == ControllerType.FourScore;
|
||||
if(HasFourScore) {
|
||||
Config.Port1.Type = ControllerType.FourScore;
|
||||
Config.Port2.Type = ControllerType.None;
|
||||
}
|
||||
});
|
||||
}));
|
||||
|
||||
IObservable<bool> button1Enabled = this.WhenAnyValue(x => x.Config.Controllers[0].Type, x => x.CanConfigure());
|
||||
this.SetupPlayer1 = ReactiveCommand.Create<Button>(btn => this.OpenSetup(btn, 0), button1Enabled);
|
||||
AddDisposable(this.WhenAnyValue(x => x.Config.ExpPort.Type).Select(t => t == ControllerType.FourPlayerAdapter).ToPropertyEx(this, x => x.HasFourPlayerAdapter));
|
||||
|
||||
AddDisposable(
|
||||
this.WhenAnyValue(x => x.Config.ExpPort.Type)
|
||||
.Select(t => t == ControllerType.TwoPlayerAdapter || t == ControllerType.FourPlayerAdapter)
|
||||
.ToPropertyEx(this, x => x.HasExpansionHub)
|
||||
);
|
||||
|
||||
IObservable<bool> button2Enabled = this.WhenAnyValue(x => x.Config.Controllers[1].Type, x => x.CanConfigure());
|
||||
this.SetupPlayer2 = ReactiveCommand.Create<Button>(btn => this.OpenSetup(btn, 1), button2Enabled);
|
||||
AddDisposable(
|
||||
this.WhenAnyValue(x => x.Config.ExpPort.Type)
|
||||
.Select(t => ResourceHelper.GetViewLabel(nameof(NesInputConfigView), t == ControllerType.TwoPlayerAdapter ? "lblTwoPlayerAdapterConfig" : "lblFourPlayerAdapterConfig"))
|
||||
.ToPropertyEx(this, x => x.ExpConfigLabel)
|
||||
);
|
||||
|
||||
IObservable<bool> button3Enabled = this.WhenAnyValue(x => x.Config.Controllers[2].Type, x => x.CanConfigure());
|
||||
this.SetupPlayer3 = ReactiveCommand.Create<Button>(btn => this.OpenSetup(btn, 2), button3Enabled);
|
||||
|
||||
IObservable<bool> button4Enabled = this.WhenAnyValue(x => x.Config.Controllers[3].Type, x => x.CanConfigure());
|
||||
this.SetupPlayer4 = ReactiveCommand.Create<Button>(btn => this.OpenSetup(btn, 3), button4Enabled);
|
||||
|
||||
IObservable<bool> button5Enabled = this.WhenAnyValue(x => x.Config.Controllers[4].Type, x => x.CanConfigure());
|
||||
this.SetupPlayer5 = ReactiveCommand.Create<Button>(btn => this.OpenSetup(btn, 4), button5Enabled);
|
||||
AddDisposable(
|
||||
this.WhenAnyValue(x => x.Config.ExpPort.Type)
|
||||
.Select(t => t == ControllerType.TwoPlayerAdapter ? AvailableControllerTypesTwoPlayer : AvailableControllerTypesFourPlayer)
|
||||
.ToPropertyEx(this, x => x.AvailableControllerTypesExpansionHub)
|
||||
);
|
||||
|
||||
EmulatorShortcut[] displayOrder = new EmulatorShortcut[] {
|
||||
EmulatorShortcut.FdsSwitchDiskSide,
|
||||
|
@ -118,32 +138,15 @@ namespace Mesen.ViewModels
|
|||
shortcuts[shortcut.Shortcut] = shortcut;
|
||||
}
|
||||
|
||||
ShortcutKeys = new List<ShortcutKeyInfo>();
|
||||
|
||||
if(Design.IsDesignMode) {
|
||||
return;
|
||||
}
|
||||
|
||||
ShortcutKeys = new List<ShortcutKeyInfo>();
|
||||
for(int i = 0; i < displayOrder.Length; i++) {
|
||||
ShortcutKeys.Add(shortcuts[displayOrder[i]]);
|
||||
}
|
||||
}
|
||||
|
||||
private async void OpenSetup(Button btn, int port)
|
||||
{
|
||||
PixelPoint startPosition = btn.PointToScreen(new Point(-7, btn.Height));
|
||||
ControllerConfigWindow wnd = new ControllerConfigWindow();
|
||||
wnd.WindowStartupLocation = WindowStartupLocation.Manual;
|
||||
wnd.Position = startPosition;
|
||||
|
||||
NesControllerConfig cfg = JsonHelper.Clone(this.Config.Controllers[port]);
|
||||
wnd.DataContext = new ControllerConfigViewModel(cfg);
|
||||
|
||||
if(await wnd.ShowDialog<bool>(btn.Parent?.VisualRoot as Window)) {
|
||||
//Create a new list to trigger UI refresh
|
||||
List<NesControllerConfig> controllers = new List<NesControllerConfig>(Config.Controllers);
|
||||
controllers[port] = cfg;
|
||||
Config.Controllers = controllers;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -28,6 +28,7 @@ namespace Mesen.ViewModels
|
|||
return;
|
||||
}
|
||||
|
||||
AddDisposable(Input);
|
||||
AddDisposable(ReactiveHelper.RegisterRecursiveObserver(Config, (s, e) => { Config.ApplyConfig(); }));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,41 +1,18 @@
|
|||
using Avalonia;
|
||||
using Avalonia.Controls;
|
||||
using Mesen.Config;
|
||||
using Mesen.Localization;
|
||||
using Mesen.Utilities;
|
||||
using Mesen.Views;
|
||||
using Mesen.Windows;
|
||||
using Mesen.Config;
|
||||
using ReactiveUI;
|
||||
using ReactiveUI.Fody.Helpers;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Linq;
|
||||
using System.Reactive;
|
||||
using System.Reactive.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Mesen.ViewModels
|
||||
{
|
||||
public class SnesInputConfigViewModel : ViewModelBase
|
||||
public class SnesInputConfigViewModel : DisposableViewModel
|
||||
{
|
||||
[Reactive] public SnesConfig Config { get; set; }
|
||||
|
||||
[Reactive] public ControllerConfig Multitap1 { get; set; }
|
||||
[Reactive] public ControllerConfig Multitap2 { get; set; }
|
||||
[Reactive] public ControllerConfig Multitap3 { get; set; }
|
||||
[Reactive] public ControllerConfig Multitap4 { get; set; }
|
||||
|
||||
[ObservableAsProperty] public bool HasMultitap { get; }
|
||||
[ObservableAsProperty] public string? Multitap1Label { get; }
|
||||
|
||||
public ReactiveCommand<Button, Unit> SetupPlayer1 { get; }
|
||||
public ReactiveCommand<Button, Unit> SetupPlayer2 { get; }
|
||||
public ReactiveCommand<Button, Unit> SetupMultitap1 { get; }
|
||||
public ReactiveCommand<Button, Unit> SetupMultitap2 { get; }
|
||||
public ReactiveCommand<Button, Unit> SetupMultitap3 { get; }
|
||||
public ReactiveCommand<Button, Unit> SetupMultitap4 { get; }
|
||||
[ObservableAsProperty] public bool HasMultitap1 { get; }
|
||||
[ObservableAsProperty] public bool HasMultitap2 { get; }
|
||||
|
||||
public Enum[] AvailableControllerTypesP12 => new Enum[] {
|
||||
ControllerType.None,
|
||||
|
@ -45,86 +22,27 @@ namespace Mesen.ViewModels
|
|||
ControllerType.Multitap,
|
||||
};
|
||||
|
||||
public Enum[] AvailableControllerTypesP345 => new Enum[] {
|
||||
ControllerType.SnesController
|
||||
public Enum[] AvailableControllerTypesMultitap => new Enum[] {
|
||||
ControllerType.None,
|
||||
ControllerType.SnesController,
|
||||
ControllerType.SnesMouse,
|
||||
ControllerType.SuperScope,
|
||||
};
|
||||
|
||||
//For designer preview
|
||||
[Obsolete("For designer only")]
|
||||
public SnesInputConfigViewModel() : this(new SnesConfig()) { }
|
||||
|
||||
public SnesInputConfigViewModel(SnesConfig config)
|
||||
{
|
||||
Config = config;
|
||||
|
||||
this.Multitap1 = Config.Controllers[1];
|
||||
this.Multitap2 = Config.Controllers[2];
|
||||
this.Multitap3 = Config.Controllers[3];
|
||||
this.Multitap4 = Config.Controllers[4];
|
||||
AddDisposable(this.WhenAnyValue(x => x.Config.Port1.Type)
|
||||
.Select(x => x == ControllerType.Multitap)
|
||||
.ToPropertyEx(this, x => x.HasMultitap1));
|
||||
|
||||
this.WhenAnyValue(x => x.Config.Controllers[0].Type, x => x.Config.Controllers[1].Type)
|
||||
.Select(x => x.Item1 == ControllerType.Multitap || x.Item2 == ControllerType.Multitap)
|
||||
.ToPropertyEx(this, x => x.HasMultitap);
|
||||
|
||||
this.WhenAnyValue(x => x.Config.Controllers[0].Type, x => x.Config.Controllers[1].Type)
|
||||
.Select(x => ResourceHelper.GetViewLabel(nameof(SnesInputConfigView), x.Item1 == ControllerType.Multitap ? "lblPlayer1" : "lblPlayer2"))
|
||||
.ToPropertyEx(this, x => x.Multitap1Label);
|
||||
|
||||
this.WhenAnyValue(x => x.Config.Controllers[0].Type).Subscribe((t) => {
|
||||
if(t == ControllerType.Multitap) {
|
||||
this.Multitap1 = Config.Controllers[0];
|
||||
Config.Controllers[1].Type = ControllerType.SnesController;
|
||||
}
|
||||
});
|
||||
|
||||
this.WhenAnyValue(x => x.Config.Controllers[1].Type).Subscribe((t) => {
|
||||
if(t == ControllerType.Multitap) {
|
||||
this.Multitap1 = Config.Controllers[1];
|
||||
Config.Controllers[0].Type = ControllerType.SnesController;
|
||||
}
|
||||
});
|
||||
|
||||
IObservable<bool> button1Enabled = this.WhenAnyValue(x => x.Config.Controllers[0].Type, x => x.CanConfigure());
|
||||
this.SetupPlayer1 = ReactiveCommand.Create<Button>(btn => this.OpenSetup(btn, 0), button1Enabled);
|
||||
|
||||
IObservable<bool> button2Enabled = this.WhenAnyValue(x => x.Config.Controllers[1].Type, x => x.CanConfigure());
|
||||
this.SetupPlayer2 = ReactiveCommand.Create<Button>(btn => this.OpenSetup(btn, 1), button2Enabled);
|
||||
|
||||
this.SetupMultitap1 = ReactiveCommand.Create<Button>(btn => this.OpenMultitapSetup(btn, 0));
|
||||
this.SetupMultitap2 = ReactiveCommand.Create<Button>(btn => this.OpenMultitapSetup(btn, 1));
|
||||
this.SetupMultitap3 = ReactiveCommand.Create<Button>(btn => this.OpenMultitapSetup(btn, 2));
|
||||
this.SetupMultitap4 = ReactiveCommand.Create<Button>(btn => this.OpenMultitapSetup(btn, 3));
|
||||
}
|
||||
|
||||
private async void OpenSetup(Button btn, int port)
|
||||
{
|
||||
PixelPoint startPosition = btn.PointToScreen(new Point(-7, btn.Height));
|
||||
ControllerConfigWindow wnd = new ControllerConfigWindow();
|
||||
wnd.WindowStartupLocation = WindowStartupLocation.Manual;
|
||||
wnd.Position = startPosition;
|
||||
|
||||
ControllerConfig cfg = JsonHelper.Clone(this.Config.Controllers[port]);
|
||||
wnd.DataContext = new ControllerConfigViewModel(cfg);
|
||||
|
||||
if(await wnd.ShowDialog<bool>(btn.Parent?.VisualRoot as Window)) {
|
||||
//Create a new list to trigger UI refresh
|
||||
List<ControllerConfig> controllers = new List<ControllerConfig>(Config.Controllers);
|
||||
controllers[port] = cfg;
|
||||
Config.Controllers = controllers;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
private void OpenMultitapSetup(Button btn, int port)
|
||||
{
|
||||
if(port > 0) {
|
||||
this.OpenSetup(btn, port + 1);
|
||||
} else {
|
||||
if(this.Config.Controllers[0].Type == ControllerType.Multitap) {
|
||||
this.OpenSetup(btn, 0);
|
||||
} else {
|
||||
this.OpenSetup(btn, 1);
|
||||
}
|
||||
}
|
||||
AddDisposable(this.WhenAnyValue(x => x.Config.Port2.Type)
|
||||
.Select(x => x == ControllerType.Multitap)
|
||||
.ToPropertyEx(this, x => x.HasMultitap2));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -18,7 +18,7 @@ namespace Mesen.Views
|
|||
|
||||
if(mappings != null) {
|
||||
return mappings.Type switch {
|
||||
ControllerType.SnesController or ControllerType.Multitap => new SnesControllerView(),
|
||||
ControllerType.SnesController => new SnesControllerView(),
|
||||
ControllerType.NesController => new NesControllerView(),
|
||||
ControllerType.GameboyController => new NesControllerView(),
|
||||
_ => new DefaultControllerView()
|
||||
|
|
|
@ -68,18 +68,21 @@
|
|||
<TabItem Header="{l:Translate tpgDisplay}">
|
||||
<Grid ColumnDefinitions="Auto,Auto" RowDefinitions="Auto,Auto,Auto">
|
||||
<TextBlock Text="{l:Translate lblDisplayInputPorts}" VerticalAlignment="Top" Margin="0 5 0 0" />
|
||||
<Grid Grid.Column="1" ColumnDefinitions="Auto,Auto" RowDefinitions="Auto,Auto,Auto">
|
||||
<Grid Grid.Column="1" ColumnDefinitions="Auto,Auto" RowDefinitions="Auto,Auto,Auto,Auto">
|
||||
<Grid.Styles>
|
||||
<Style Selector="CheckBox">
|
||||
<Setter Property="Margin" Value="0 0 5 0" />
|
||||
</Style>
|
||||
</Grid.Styles>
|
||||
|
||||
<CheckBox Content="{l:Translate chkDisplayPort1}" IsChecked="{CompiledBinding Config.DisplayInputPort1}" />
|
||||
<CheckBox Grid.Column="1" Content="{l:Translate chkDisplayPort2}" IsChecked="{CompiledBinding Config.DisplayInputPort2}" />
|
||||
<CheckBox Grid.Row="0" Content="{l:Translate chkDisplayPort1}" IsChecked="{CompiledBinding Config.DisplayInputPort1}" />
|
||||
<CheckBox Grid.Row="0" Grid.Column="1" Content="{l:Translate chkDisplayPort2}" IsChecked="{CompiledBinding Config.DisplayInputPort2}" />
|
||||
<CheckBox Grid.Row="1" Content="{l:Translate chkDisplayPort3}" IsChecked="{CompiledBinding Config.DisplayInputPort3}" />
|
||||
<CheckBox Grid.Row="1" Grid.Column="1" Content="{l:Translate chkDisplayPort4}" IsChecked="{CompiledBinding Config.DisplayInputPort4}" />
|
||||
<CheckBox Grid.Row="2" Content="{l:Translate chkDisplayPort5}" IsChecked="{CompiledBinding Config.DisplayInputPort5}" />
|
||||
<CheckBox Grid.Row="2" Grid.Column="1" Content="{l:Translate chkDisplayPort6}" IsChecked="{CompiledBinding Config.DisplayInputPort6}" />
|
||||
<CheckBox Grid.Row="3" Content="{l:Translate chkDisplayPort7}" IsChecked="{CompiledBinding Config.DisplayInputPort7}" />
|
||||
<CheckBox Grid.Row="3" Grid.Column="1" Content="{l:Translate chkDisplayPort8}" IsChecked="{CompiledBinding Config.DisplayInputPort8}" />
|
||||
</Grid>
|
||||
|
||||
<TextBlock Grid.Row="1" Text="{l:Translate lblDisplayPosition}" />
|
||||
|
|
|
@ -21,85 +21,130 @@
|
|||
<ScrollViewer AllowAutoHide="False">
|
||||
<StackPanel>
|
||||
<c:OptionSection Header="{l:Translate grpControllers}" Margin="0">
|
||||
<Grid ColumnDefinitions="Auto,*,Auto" RowDefinitions="Auto,Auto,Auto,Auto,Auto" Width="300" HorizontalAlignment="Left">
|
||||
<Grid ColumnDefinitions="Auto,*,Auto" RowDefinitions="Auto,Auto,Auto,Auto,Auto" HorizontalAlignment="Left">
|
||||
<TextBlock Grid.Row="0" Grid.Column="0" Text="{l:Translate lblPlayer1}" />
|
||||
<c:EnumComboBox
|
||||
<c:InputComboBox
|
||||
Grid.Row="0" Grid.Column="1"
|
||||
Margin="5 0"
|
||||
SelectedItem="{CompiledBinding Config.Controllers[0].Type}"
|
||||
Config="{CompiledBinding Config.Port1}"
|
||||
ControllerType="{CompiledBinding Config.Port1.Type}"
|
||||
AvailableValues="{CompiledBinding AvailableControllerTypesP12}"
|
||||
/>
|
||||
<Button
|
||||
Grid.Row="0"
|
||||
Grid.Column="2"
|
||||
Command="{CompiledBinding SetupPlayer1}"
|
||||
CommandParameter="{Binding $self}"
|
||||
Content="{l:Translate btnSetup}"
|
||||
/>
|
||||
|
||||
<TextBlock Grid.Row="1" Grid.Column="0" Text="{l:Translate lblPlayer2}" />
|
||||
<c:EnumComboBox
|
||||
<c:InputComboBox
|
||||
Grid.Row="1" Grid.Column="1"
|
||||
Margin="5 0"
|
||||
SelectedItem="{CompiledBinding Config.Controllers[1].Type}"
|
||||
Config="{CompiledBinding Config.Port2}"
|
||||
IsEnabled="{CompiledBinding !HasFourScore}"
|
||||
ControllerType="{CompiledBinding Config.Port2.Type}"
|
||||
AvailableValues="{CompiledBinding AvailableControllerTypesP12}"
|
||||
/>
|
||||
<Button
|
||||
Grid.Row="1"
|
||||
Grid.Column="2"
|
||||
Command="{CompiledBinding SetupPlayer2}"
|
||||
CommandParameter="{Binding $self}"
|
||||
Content="{l:Translate btnSetup}"
|
||||
/>
|
||||
|
||||
<TextBlock Grid.Row="2" Grid.Column="0" Text="{l:Translate lblExpansionDevice}" />
|
||||
<c:EnumComboBox
|
||||
Grid.Row="2" Grid.Column="1"
|
||||
Margin="5 0"
|
||||
SelectedItem="{CompiledBinding Config.Controllers[4].Type}"
|
||||
<c:GroupBox
|
||||
Header="{l:Translate lblFourScoreConfig}"
|
||||
IsVisible="{CompiledBinding HasFourScore}"
|
||||
HorizontalAlignment="Right"
|
||||
Margin="0 5 -6 5"
|
||||
Grid.Row="2"
|
||||
Grid.Column="0"
|
||||
Grid.ColumnSpan="3"
|
||||
>
|
||||
<Grid ColumnDefinitions="Auto,*,Auto" RowDefinitions="Auto,Auto,Auto,Auto">
|
||||
<TextBlock Grid.Row="0" Grid.Column="0" Text="{l:Translate lblPort1A}" />
|
||||
<c:InputComboBox
|
||||
Grid.Row="0" Grid.Column="1"
|
||||
Config="{CompiledBinding Config.Port1}"
|
||||
ControllerType="{CompiledBinding Config.Port1A.Type}"
|
||||
AvailableValues="{CompiledBinding AvailableControllerTypesFourPlayer}"
|
||||
/>
|
||||
|
||||
<TextBlock Grid.Row="1" Grid.Column="0" Text="{l:Translate lblPort1B}" />
|
||||
<c:InputComboBox
|
||||
Grid.Row="1" Grid.Column="1"
|
||||
Config="{CompiledBinding Config.Port2}"
|
||||
ControllerType="{CompiledBinding Config.Port1B.Type}"
|
||||
AvailableValues="{CompiledBinding AvailableControllerTypesFourPlayer}"
|
||||
/>
|
||||
|
||||
<TextBlock Grid.Row="2" Grid.Column="0" Text="{l:Translate lblPort1C}" />
|
||||
<c:InputComboBox
|
||||
Grid.Row="2" Grid.Column="1"
|
||||
Config="{CompiledBinding Config.Port1C}"
|
||||
ControllerType="{CompiledBinding Config.Port1C.Type}"
|
||||
AvailableValues="{CompiledBinding AvailableControllerTypesFourPlayer}"
|
||||
/>
|
||||
|
||||
<TextBlock Grid.Row="3" Grid.Column="0" Text="{l:Translate lblPort1D}" />
|
||||
<c:InputComboBox
|
||||
Grid.Row="3" Grid.Column="1"
|
||||
Config="{CompiledBinding Config.Port1D}"
|
||||
ControllerType="{CompiledBinding Config.Port1D.Type}"
|
||||
AvailableValues="{CompiledBinding AvailableControllerTypesFourPlayer}"
|
||||
/>
|
||||
</Grid>
|
||||
</c:GroupBox>
|
||||
|
||||
<TextBlock Grid.Row="3" Grid.Column="0" Text="{l:Translate lblExpansionDevice}" Margin="0 10 0 0" />
|
||||
<c:InputComboBox
|
||||
Margin="0 10 0 0"
|
||||
Grid.Row="3" Grid.Column="1"
|
||||
Config="{CompiledBinding Config.ExpPort}"
|
||||
ControllerType="{CompiledBinding Config.ExpPort.Type}"
|
||||
AvailableValues="{CompiledBinding AvailableExpansionTypes}"
|
||||
/>
|
||||
<Button
|
||||
Grid.Row="2"
|
||||
Grid.Column="2"
|
||||
Command="{CompiledBinding SetupPlayer5}"
|
||||
CommandParameter="{Binding $self}"
|
||||
Content="{l:Translate btnSetup}"
|
||||
/>
|
||||
|
||||
<TextBlock Grid.Row="3" Grid.Column="0" Text="{l:Translate lblPlayer3}" IsVisible="{CompiledBinding HasFourPlayerAdapter}" />
|
||||
<c:EnumComboBox
|
||||
Grid.Row="3" Grid.Column="1"
|
||||
Margin="5 0"
|
||||
SelectedItem="{CompiledBinding Config.Controllers[2].Type}"
|
||||
IsVisible="{CompiledBinding HasFourPlayerAdapter}"
|
||||
AvailableValues="{CompiledBinding AvailableControllerTypesP34}"
|
||||
/>
|
||||
<Button
|
||||
Grid.Row="3"
|
||||
Grid.Column="2"
|
||||
Command="{CompiledBinding SetupPlayer3}"
|
||||
CommandParameter="{Binding $self}"
|
||||
Content="{l:Translate btnSetup}"
|
||||
IsVisible="{CompiledBinding HasFourPlayerAdapter}"
|
||||
/>
|
||||
|
||||
<TextBlock Grid.Row="4" Grid.Column="0" Text="{l:Translate lblPlayer4}" IsVisible="{CompiledBinding HasFourPlayerAdapter}" />
|
||||
<c:EnumComboBox
|
||||
Grid.Row="4" Grid.Column="1"
|
||||
Margin="5 0"
|
||||
SelectedItem="{CompiledBinding Config.Controllers[3].Type}"
|
||||
IsVisible="{CompiledBinding HasFourPlayerAdapter}"
|
||||
AvailableValues="{CompiledBinding AvailableControllerTypesP34}"
|
||||
/>
|
||||
<Button
|
||||
<c:GroupBox
|
||||
Header="{CompiledBinding ExpConfigLabel}"
|
||||
IsVisible="{CompiledBinding HasExpansionHub}"
|
||||
HorizontalAlignment="Right"
|
||||
Margin="0 5 -6 5"
|
||||
Grid.Row="4"
|
||||
Grid.Column="2"
|
||||
Command="{CompiledBinding SetupPlayer4}"
|
||||
CommandParameter="{Binding $self}"
|
||||
Content="{l:Translate btnSetup}"
|
||||
IsVisible="{CompiledBinding HasFourPlayerAdapter}"
|
||||
/>
|
||||
Grid.Column="0"
|
||||
Grid.ColumnSpan="3"
|
||||
>
|
||||
<Grid ColumnDefinitions="Auto,*,Auto" RowDefinitions="Auto,Auto,Auto,Auto">
|
||||
<TextBlock Grid.Row="0" Grid.Column="0" Text="{l:Translate lblExpPortA}" />
|
||||
<c:InputComboBox
|
||||
Grid.Row="0" Grid.Column="1"
|
||||
Config="{CompiledBinding Config.ExpPort}"
|
||||
ControllerType="{CompiledBinding Config.ExpPortA.Type}"
|
||||
AvailableValues="{CompiledBinding AvailableControllerTypesExpansionHub}"
|
||||
/>
|
||||
|
||||
<TextBlock Grid.Row="1" Grid.Column="0" Text="{l:Translate lblExpPortB}" />
|
||||
<c:InputComboBox
|
||||
Grid.Row="1" Grid.Column="1"
|
||||
Config="{CompiledBinding Config.ExpPortB}"
|
||||
ControllerType="{CompiledBinding Config.ExpPortB.Type}"
|
||||
AvailableValues="{CompiledBinding AvailableControllerTypesExpansionHub}"
|
||||
/>
|
||||
|
||||
<TextBlock
|
||||
Grid.Row="2" Grid.Column="0"
|
||||
Text="{l:Translate lblExpPortC}"
|
||||
IsVisible="{CompiledBinding HasFourPlayerAdapter}"
|
||||
/>
|
||||
<c:InputComboBox
|
||||
Grid.Row="2" Grid.Column="1"
|
||||
IsVisible="{CompiledBinding HasFourPlayerAdapter}"
|
||||
Config="{CompiledBinding Config.ExpPortC}"
|
||||
ControllerType="{CompiledBinding Config.ExpPortC.Type}"
|
||||
AvailableValues="{CompiledBinding AvailableControllerTypesExpansionHub}"
|
||||
/>
|
||||
|
||||
<TextBlock
|
||||
Grid.Row="3" Grid.Column="0"
|
||||
Text="{l:Translate lblExpPortD}"
|
||||
IsVisible="{CompiledBinding HasFourPlayerAdapter}"
|
||||
/>
|
||||
<c:InputComboBox
|
||||
Grid.Row="3" Grid.Column="1"
|
||||
IsVisible="{CompiledBinding HasFourPlayerAdapter}"
|
||||
Config="{CompiledBinding Config.ExpPortD}"
|
||||
ControllerType="{CompiledBinding Config.ExpPortD.Type}"
|
||||
AvailableValues="{CompiledBinding AvailableControllerTypesExpansionHub}"
|
||||
/>
|
||||
</Grid>
|
||||
</c:GroupBox>
|
||||
</Grid>
|
||||
</c:OptionSection>
|
||||
<c:OptionSection Header="{l:Translate grpHardware}">
|
||||
|
|
|
@ -17,104 +17,108 @@
|
|||
<vm:SnesInputConfigViewModel />
|
||||
</Design.DataContext>
|
||||
|
||||
<StackPanel Width="300" HorizontalAlignment="Left">
|
||||
<Grid ColumnDefinitions="Auto,*,Auto" RowDefinitions="Auto,Auto,Auto">
|
||||
<TextBlock Grid.Row="0" Grid.Column="0">Player 1:</TextBlock>
|
||||
<c:EnumComboBox
|
||||
<StackPanel HorizontalAlignment="Left">
|
||||
<Grid ColumnDefinitions="Auto,*,Auto" RowDefinitions="Auto,Auto,Auto,Auto">
|
||||
<TextBlock Grid.Row="0" Grid.Column="0" Text="{l:Translate lblPort1}" Margin="5 0 30 0" />
|
||||
<c:InputComboBox
|
||||
Grid.Row="0" Grid.Column="1"
|
||||
Margin="5 0"
|
||||
SelectedItem="{CompiledBinding Config.Controllers[0].Type}"
|
||||
Config="{CompiledBinding Config.Port1}"
|
||||
ControllerType="{CompiledBinding Config.Port1.Type}"
|
||||
AvailableValues="{CompiledBinding AvailableControllerTypesP12}"
|
||||
/>
|
||||
<Button
|
||||
Grid.Row="0"
|
||||
Grid.Column="2"
|
||||
Command="{CompiledBinding SetupPlayer1}"
|
||||
CommandParameter="{Binding $self}"
|
||||
Content="{l:Translate btnSetupP1}"
|
||||
/>
|
||||
|
||||
<TextBlock Grid.Row="1" Grid.Column="0">Player 2:</TextBlock>
|
||||
<c:EnumComboBox
|
||||
Grid.Row="1" Grid.Column="1"
|
||||
Margin="5 0"
|
||||
SelectedItem="{CompiledBinding Config.Controllers[1].Type}"
|
||||
AvailableValues="{CompiledBinding AvailableControllerTypesP12}"
|
||||
/>
|
||||
<Button
|
||||
Grid.Row="1"
|
||||
Grid.Column="2"
|
||||
Command="{CompiledBinding SetupPlayer2}"
|
||||
CommandParameter="{Binding $self}"
|
||||
Content="{l:Translate btnSetupP2}"
|
||||
/>
|
||||
|
||||
<c:GroupBox
|
||||
Header="Multitap Configuration"
|
||||
IsVisible="{CompiledBinding HasMultitap}"
|
||||
Grid.Row="2"
|
||||
Header="{l:Translate grpMultitap}"
|
||||
IsVisible="{CompiledBinding HasMultitap1}"
|
||||
HorizontalAlignment="Right"
|
||||
Margin="0 5 -6 5"
|
||||
Grid.Row="1"
|
||||
Grid.Column="0"
|
||||
Grid.ColumnSpan="3"
|
||||
>
|
||||
<Grid ColumnDefinitions="Auto,*,Auto" RowDefinitions="Auto,Auto,Auto,Auto">
|
||||
<TextBlock Grid.Row="0" Grid.Column="0" Text="{CompiledBinding Multitap1Label}" />
|
||||
<c:EnumComboBox
|
||||
<TextBlock Grid.Row="0" Grid.Column="0" Text="{l:Translate lblMultitap1A}" />
|
||||
<c:InputComboBox
|
||||
Grid.Row="0" Grid.Column="1"
|
||||
Margin="5 0"
|
||||
SelectedItem="{x:Static cfg:ControllerType.SnesController}"
|
||||
AvailableValues="{CompiledBinding AvailableControllerTypesP345}"
|
||||
/>
|
||||
<Button
|
||||
Grid.Row="0"
|
||||
Grid.Column="2"
|
||||
Command="{CompiledBinding SetupMultitap1}"
|
||||
CommandParameter="{Binding $self}"
|
||||
Content="{l:Translate btnSetupMultitap1}"
|
||||
Config="{CompiledBinding Config.Port1}"
|
||||
ControllerType="{CompiledBinding Config.Port1A.Type}"
|
||||
AvailableValues="{CompiledBinding AvailableControllerTypesMultitap}"
|
||||
/>
|
||||
|
||||
<TextBlock Grid.Row="1" Grid.Column="0" Text="{l:Translate lblMultitap2}" />
|
||||
<c:EnumComboBox
|
||||
<TextBlock Grid.Row="1" Grid.Column="0" Text="{l:Translate lblMultitap1B}" />
|
||||
<c:InputComboBox
|
||||
Grid.Row="1" Grid.Column="1"
|
||||
Margin="5 0"
|
||||
SelectedItem="{x:Static cfg:ControllerType.SnesController}"
|
||||
AvailableValues="{CompiledBinding AvailableControllerTypesP345}"
|
||||
/>
|
||||
<Button
|
||||
Grid.Row="1"
|
||||
Grid.Column="2"
|
||||
Command="{CompiledBinding SetupMultitap2}"
|
||||
CommandParameter="{Binding $self}"
|
||||
Content="{l:Translate btnSetupMultitap2}"
|
||||
Config="{CompiledBinding Config.Port1B}"
|
||||
ControllerType="{CompiledBinding Config.Port1B.Type}"
|
||||
AvailableValues="{CompiledBinding AvailableControllerTypesMultitap}"
|
||||
/>
|
||||
|
||||
<TextBlock Grid.Row="2" Grid.Column="0" Text="{l:Translate lblMultitap3}" />
|
||||
<c:EnumComboBox
|
||||
<TextBlock Grid.Row="2" Grid.Column="0" Text="{l:Translate lblMultitap1C}" />
|
||||
<c:InputComboBox
|
||||
Grid.Row="2" Grid.Column="1"
|
||||
Margin="5 0"
|
||||
SelectedItem="{x:Static cfg:ControllerType.SnesController}"
|
||||
AvailableValues="{CompiledBinding AvailableControllerTypesP345}"
|
||||
/>
|
||||
<Button
|
||||
Grid.Row="2"
|
||||
Grid.Column="2"
|
||||
Command="{CompiledBinding SetupMultitap3}"
|
||||
CommandParameter="{Binding $self}"
|
||||
Content="{l:Translate btnSetupMultitap3}"
|
||||
Config="{CompiledBinding Config.Port1C}"
|
||||
ControllerType="{CompiledBinding Config.Port1C.Type}"
|
||||
AvailableValues="{CompiledBinding AvailableControllerTypesMultitap}"
|
||||
/>
|
||||
|
||||
<TextBlock Grid.Row="3" Grid.Column="0" Text="{l:Translate lblMultitap4}" />
|
||||
<c:EnumComboBox
|
||||
<TextBlock Grid.Row="3" Grid.Column="0" Text="{l:Translate lblMultitap1D}" />
|
||||
<c:InputComboBox
|
||||
Grid.Row="3" Grid.Column="1"
|
||||
Margin="5 0"
|
||||
SelectedItem="{x:Static cfg:ControllerType.SnesController}"
|
||||
AvailableValues="{CompiledBinding AvailableControllerTypesP345}"
|
||||
Config="{CompiledBinding Config.Port1D}"
|
||||
ControllerType="{CompiledBinding Config.Port1D.Type}"
|
||||
AvailableValues="{CompiledBinding AvailableControllerTypesMultitap}"
|
||||
/>
|
||||
<Button
|
||||
Grid.Row="3"
|
||||
Grid.Column="2"
|
||||
Command="{CompiledBinding SetupMultitap4}"
|
||||
CommandParameter="{Binding $self}"
|
||||
Content="{l:Translate btnSetupMultitap4}"
|
||||
</Grid>
|
||||
</c:GroupBox>
|
||||
|
||||
<TextBlock Grid.Row="2" Grid.Column="0" Text="{l:Translate lblPort2}" Margin="5 0 30 0" />
|
||||
<c:InputComboBox
|
||||
Grid.Row="2" Grid.Column="1"
|
||||
Config="{CompiledBinding Config.Port2}"
|
||||
ControllerType="{CompiledBinding Config.Port2.Type}"
|
||||
AvailableValues="{CompiledBinding AvailableControllerTypesP12}"
|
||||
/>
|
||||
|
||||
<c:GroupBox
|
||||
Header="{l:Translate grpMultitap}"
|
||||
IsVisible="{CompiledBinding HasMultitap2}"
|
||||
HorizontalAlignment="Right"
|
||||
Margin="0 5 -6 5"
|
||||
Grid.Row="3"
|
||||
Grid.Column="0"
|
||||
Grid.ColumnSpan="3"
|
||||
>
|
||||
<Grid ColumnDefinitions="Auto,*,Auto" RowDefinitions="Auto,Auto,Auto,Auto">
|
||||
<TextBlock Grid.Row="0" Grid.Column="0" Text="{l:Translate lblMultitap2A}" />
|
||||
<c:InputComboBox
|
||||
Grid.Row="0" Grid.Column="1"
|
||||
Config="{CompiledBinding Config.Port2}"
|
||||
ControllerType="{CompiledBinding Config.Port2A.Type}"
|
||||
AvailableValues="{CompiledBinding AvailableControllerTypesMultitap}"
|
||||
/>
|
||||
|
||||
<TextBlock Grid.Row="1" Grid.Column="0" Text="{l:Translate lblMultitap2B}" />
|
||||
<c:InputComboBox
|
||||
Grid.Row="1" Grid.Column="1"
|
||||
Config="{CompiledBinding Config.Port2B}"
|
||||
ControllerType="{CompiledBinding Config.Port2B.Type}"
|
||||
AvailableValues="{CompiledBinding AvailableControllerTypesMultitap}"
|
||||
/>
|
||||
|
||||
<TextBlock Grid.Row="2" Grid.Column="0" Text="{l:Translate lblMultitap2C}" />
|
||||
<c:InputComboBox
|
||||
Grid.Row="2" Grid.Column="1"
|
||||
Config="{CompiledBinding Config.Port2C}"
|
||||
ControllerType="{CompiledBinding Config.Port2C.Type}"
|
||||
AvailableValues="{CompiledBinding AvailableControllerTypesMultitap}"
|
||||
/>
|
||||
|
||||
<TextBlock Grid.Row="3" Grid.Column="0" Text="{l:Translate lblMultitap2D}" />
|
||||
<c:InputComboBox
|
||||
Grid.Row="3" Grid.Column="1"
|
||||
Config="{CompiledBinding Config.Port2D}"
|
||||
ControllerType="{CompiledBinding Config.Port2D.Type}"
|
||||
AvailableValues="{CompiledBinding AvailableControllerTypesMultitap}"
|
||||
/>
|
||||
</Grid>
|
||||
</c:GroupBox>
|
||||
|
|
|
@ -39,13 +39,11 @@ namespace Mesen.Windows
|
|||
{
|
||||
int index = this.FindControl<TabControl>("tabMain").SelectedIndex;
|
||||
ControllerConfig cfg = Model.Config;
|
||||
if(cfg != null) {
|
||||
switch(index) {
|
||||
case 0: cfg.Mapping1.SetDefaultKeys(cfg.Type, preset); Model.KeyMapping1.RefreshCustomKeys(); break;
|
||||
case 1: cfg.Mapping2.SetDefaultKeys(cfg.Type, preset); Model.KeyMapping2.RefreshCustomKeys(); break;
|
||||
case 2: cfg.Mapping3.SetDefaultKeys(cfg.Type, preset); Model.KeyMapping3.RefreshCustomKeys(); break;
|
||||
case 3: cfg.Mapping4.SetDefaultKeys(cfg.Type, preset); Model.KeyMapping4.RefreshCustomKeys(); break;
|
||||
}
|
||||
switch(index) {
|
||||
case 0: cfg.Mapping1.SetDefaultKeys(Model.Type, preset); Model.KeyMapping1.RefreshCustomKeys(); break;
|
||||
case 1: cfg.Mapping2.SetDefaultKeys(Model.Type, preset); Model.KeyMapping2.RefreshCustomKeys(); break;
|
||||
case 2: cfg.Mapping3.SetDefaultKeys(Model.Type, preset); Model.KeyMapping3.RefreshCustomKeys(); break;
|
||||
case 3: cfg.Mapping4.SetDefaultKeys(Model.Type, preset); Model.KeyMapping4.RefreshCustomKeys(); break;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -53,13 +51,11 @@ namespace Mesen.Windows
|
|||
{
|
||||
int index = this.FindControl<TabControl>("tabMain").SelectedIndex;
|
||||
ControllerConfig cfg = Model.Config;
|
||||
if(cfg != null) {
|
||||
switch(index) {
|
||||
case 0: cfg.Mapping1.ClearKeys(cfg.Type); Model.KeyMapping1.RefreshCustomKeys(); break;
|
||||
case 1: cfg.Mapping2.ClearKeys(cfg.Type); Model.KeyMapping2.RefreshCustomKeys(); break;
|
||||
case 2: cfg.Mapping3.ClearKeys(cfg.Type); Model.KeyMapping3.RefreshCustomKeys(); break;
|
||||
case 3: cfg.Mapping4.ClearKeys(cfg.Type); Model.KeyMapping4.RefreshCustomKeys(); break;
|
||||
}
|
||||
switch(index) {
|
||||
case 0: cfg.Mapping1.ClearKeys(Model.Type); Model.KeyMapping1.RefreshCustomKeys(); break;
|
||||
case 1: cfg.Mapping2.ClearKeys(Model.Type); Model.KeyMapping2.RefreshCustomKeys(); break;
|
||||
case 2: cfg.Mapping3.ClearKeys(Model.Type); Model.KeyMapping3.RefreshCustomKeys(); break;
|
||||
case 3: cfg.Mapping4.ClearKeys(Model.Type); Model.KeyMapping4.RefreshCustomKeys(); break;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue