Config UI, 6-button controller, turbo tap

This commit is contained in:
Sour 2022-04-30 14:02:48 -04:00
parent 979e055209
commit 66c8e281dd
49 changed files with 1117 additions and 100 deletions

View file

@ -19,6 +19,8 @@
</ProjectConfiguration>
</ItemGroup>
<ItemGroup>
<ClInclude Include="PCE\Input\PceAvenuePad6.h" />
<ClInclude Include="PCE\Input\PceTurboTap.h" />
<ClInclude Include="PCE\Debugger\DummyPceCpu.h" />
<ClInclude Include="PCE\Debugger\PceEventManager.h" />
<ClInclude Include="PCE\Input\PceController.h" />
@ -442,6 +444,7 @@
<ClCompile Include="NES\Mappers\NsfMapper.cpp" />
<ClCompile Include="NES\Mappers\VsSystem\VsControlManager.cpp" />
<ClCompile Include="NES\NesNtscFilter.cpp" />
<ClCompile Include="PCE\Input\PceTurboTap.cpp" />
<ClCompile Include="PCE\Debugger\DummyPceCpu.cpp" />
<ClCompile Include="PCE\Debugger\PceDebugger.cpp" />
<ClCompile Include="PCE\Debugger\PceDisUtils.cpp" />

View file

@ -1760,9 +1760,6 @@
<ClInclude Include="PCE\PcePpu.h">
<Filter>PCE</Filter>
</ClInclude>
<ClInclude Include="PCE\Input\PceController.h">
<Filter>PCE</Filter>
</ClInclude>
<ClInclude Include="PCE\PceTimer.h">
<Filter>PCE</Filter>
</ClInclude>
@ -1799,6 +1796,15 @@
<ClInclude Include="PCE\PceCdAudioPlayer.h">
<Filter>PCE</Filter>
</ClInclude>
<ClInclude Include="PCE\Input\PceController.h">
<Filter>PCE\Input</Filter>
</ClInclude>
<ClInclude Include="PCE\Input\PceTurboTap.h">
<Filter>PCE\Input</Filter>
</ClInclude>
<ClInclude Include="PCE\Input\PceAvenuePad6.h">
<Filter>PCE\Input</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<ClCompile Include="Shared\Video\RotateFilter.cpp">
@ -1870,6 +1876,9 @@
<ClCompile Include="PCE\PceCdAudioPlayer.cpp">
<Filter>PCE</Filter>
</ClCompile>
<ClCompile Include="PCE\Input\PceTurboTap.cpp">
<Filter>PCE\Input</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<Filter Include="PCE">
@ -1878,5 +1887,8 @@
<Filter Include="PCE\Debugger">
<UniqueIdentifier>{71cd23d2-e1b9-4a25-a92a-34b2f64f8086}</UniqueIdentifier>
</Filter>
<Filter Include="PCE\Input">
<UniqueIdentifier>{a2cd79a6-585b-408d-b6af-c273b94092a7}</UniqueIdentifier>
</Filter>
</ItemGroup>
</Project>

View file

@ -0,0 +1,130 @@
#pragma once
#include "stdafx.h"
#include "Shared/BaseControlDevice.h"
#include "Shared/Emulator.h"
#include "Shared/EmuSettings.h"
#include "Shared/InputHud.h"
#include "Utilities/Serializer.h"
class PceAvenuePad6 : public BaseControlDevice
{
private:
uint32_t _turboSpeed = 0;
bool _disableInput = false;
bool _selectDPad = false;
bool _selectExtraButtons = false;
protected:
string GetKeyNames() override
{
return "UDLRSr123456";
}
void InternalSetStateFromInput() override
{
for(KeyMapping& keyMapping : _keyMappings) {
SetPressedState(Buttons::I, keyMapping.A);
SetPressedState(Buttons::II, keyMapping.B);
SetPressedState(Buttons::III, keyMapping.X);
SetPressedState(Buttons::IV, keyMapping.Y);
SetPressedState(Buttons::V, keyMapping.L);
SetPressedState(Buttons::VI, keyMapping.R);
SetPressedState(Buttons::Run, keyMapping.Start);
SetPressedState(Buttons::Select, keyMapping.Select);
SetPressedState(Buttons::Up, keyMapping.Up);
SetPressedState(Buttons::Down, keyMapping.Down);
SetPressedState(Buttons::Left, keyMapping.Left);
SetPressedState(Buttons::Right, keyMapping.Right);
uint8_t turboFreq = 1 << (4 - _turboSpeed);
bool turboOn = (uint8_t)(_emu->GetFrameCount() % turboFreq) < turboFreq / 2;
if(turboOn) {
SetPressedState(Buttons::I, keyMapping.TurboA);
SetPressedState(Buttons::II, keyMapping.TurboB);
SetPressedState(Buttons::III, keyMapping.TurboX);
SetPressedState(Buttons::IV, keyMapping.TurboY);
SetPressedState(Buttons::V, keyMapping.TurboL);
SetPressedState(Buttons::VI, keyMapping.TurboR);
}
}
}
void RefreshStateBuffer() override
{
}
public:
enum Buttons { Up = 0, Down, Left, Right, Select, Run, I, II, III, IV, V, VI };
PceAvenuePad6(Emulator* emu, uint8_t port, KeyMappingSet keyMappings) : BaseControlDevice(emu, ControllerType::PceAvenuePad6, port, keyMappings)
{
_turboSpeed = keyMappings.TurboSpeed;
}
uint8_t ReadRam(uint16_t addr) override
{
if(_disableInput) {
return 0;
}
uint8_t result = 0x0F;
if(_selectExtraButtons) {
if(_selectDPad) {
result = 0;
} else {
result &= ~(IsPressed(PceAvenuePad6::III) ? 0x01 : 0);
result &= ~(IsPressed(PceAvenuePad6::IV) ? 0x02 : 0);
result &= ~(IsPressed(PceAvenuePad6::V) ? 0x04 : 0);
result &= ~(IsPressed(PceAvenuePad6::VI) ? 0x08 : 0);
}
} else {
if(_selectDPad) {
result &= ~(IsPressed(PceAvenuePad6::Up) ? 0x01 : 0);
result &= ~(IsPressed(PceAvenuePad6::Right) ? 0x02 : 0);
result &= ~(IsPressed(PceAvenuePad6::Down) ? 0x04 : 0);
result &= ~(IsPressed(PceAvenuePad6::Left) ? 0x08 : 0);
} else {
result &= ~(IsPressed(PceAvenuePad6::I) ? 0x01 : 0);
result &= ~(IsPressed(PceAvenuePad6::II) ? 0x02 : 0);
result &= ~(IsPressed(PceAvenuePad6::Select) ? 0x04 : 0);
result &= ~(IsPressed(PceAvenuePad6::Run) ? 0x08 : 0);
}
}
return result;
}
void WriteRam(uint16_t addr, uint8_t value) override
{
bool disableInput = (value & 0x02) != 0;
if(disableInput && !_disableInput) {
_selectExtraButtons = !_selectExtraButtons;
}
_disableInput = disableInput;
_selectDPad = (value & 0x01) != 0;
}
void InternalDrawController(InputHud& hud) override
{
hud.DrawOutline(35, 14);
hud.DrawButton(5, 3, 3, 3, IsPressed(Buttons::Up));
hud.DrawButton(5, 9, 3, 3, IsPressed(Buttons::Down));
hud.DrawButton(2, 6, 3, 3, IsPressed(Buttons::Left));
hud.DrawButton(8, 6, 3, 3, IsPressed(Buttons::Right));
hud.DrawButton(5, 6, 3, 3, false);
hud.DrawButton(22, 8, 3, 3, IsPressed(Buttons::III));
hud.DrawButton(26, 8, 3, 3, IsPressed(Buttons::II));
hud.DrawButton(30, 8, 3, 3, IsPressed(Buttons::I));
hud.DrawButton(22, 3, 3, 3, IsPressed(Buttons::IV));
hud.DrawButton(26, 3, 3, 3, IsPressed(Buttons::V));
hud.DrawButton(30, 3, 3, 3, IsPressed(Buttons::VI));
hud.DrawButton(12, 9, 4, 2, IsPressed(Buttons::Select));
hud.DrawButton(17, 9, 4, 2, IsPressed(Buttons::Run));
hud.DrawNumber(hud.GetControllerIndex() + 1, 15, 2);
}
};

View file

@ -16,15 +16,15 @@ private:
protected:
string GetKeyNames() override
{
return "UDLRSsBA";
return "UDLRSr12";
}
void InternalSetStateFromInput() override
{
for(KeyMapping& keyMapping : _keyMappings) {
SetPressedState(Buttons::A, keyMapping.A);
SetPressedState(Buttons::B, keyMapping.B);
SetPressedState(Buttons::Start, keyMapping.Start);
SetPressedState(Buttons::I, keyMapping.A);
SetPressedState(Buttons::II, keyMapping.B);
SetPressedState(Buttons::Run, keyMapping.Start);
SetPressedState(Buttons::Select, keyMapping.Select);
SetPressedState(Buttons::Up, keyMapping.Up);
SetPressedState(Buttons::Down, keyMapping.Down);
@ -34,8 +34,8 @@ protected:
uint8_t turboFreq = 1 << (4 - _turboSpeed);
bool turboOn = (uint8_t)(_emu->GetFrameCount() % turboFreq) < turboFreq / 2;
if(turboOn) {
SetPressedState(Buttons::A, keyMapping.TurboA);
SetPressedState(Buttons::B, keyMapping.TurboB);
SetPressedState(Buttons::I, keyMapping.TurboA);
SetPressedState(Buttons::II, keyMapping.TurboB);
}
}
}
@ -45,9 +45,9 @@ protected:
}
public:
enum Buttons { Up = 0, Down, Left, Right, Start, Select, B, A };
enum Buttons { Up = 0, Down, Left, Right, Select, Run, I, II };
PceController(Emulator* emu, uint8_t port, KeyMappingSet keyMappings) : BaseControlDevice(emu, ControllerType::GameboyController, port, keyMappings)
PceController(Emulator* emu, uint8_t port, KeyMappingSet keyMappings) : BaseControlDevice(emu, ControllerType::PceController, port, keyMappings)
{
_turboSpeed = keyMappings.TurboSpeed;
}
@ -65,10 +65,10 @@ public:
result &= ~(IsPressed(PceController::Down) ? 0x04 : 0);
result &= ~(IsPressed(PceController::Left) ? 0x08 : 0);
} else {
result &= ~(IsPressed(PceController::A) ? 0x01 : 0);
result &= ~(IsPressed(PceController::B) ? 0x02 : 0);
result &= ~(IsPressed(PceController::I) ? 0x01 : 0);
result &= ~(IsPressed(PceController::II) ? 0x02 : 0);
result &= ~(IsPressed(PceController::Select) ? 0x04 : 0);
result &= ~(IsPressed(PceController::Start) ? 0x08 : 0);
result &= ~(IsPressed(PceController::Run) ? 0x08 : 0);
}
return result;
@ -90,12 +90,12 @@ public:
hud.DrawButton(8, 6, 3, 3, IsPressed(Buttons::Right));
hud.DrawButton(5, 6, 3, 3, false);
hud.DrawButton(30, 7, 3, 3, IsPressed(Buttons::A));
hud.DrawButton(25, 7, 3, 3, IsPressed(Buttons::B));
hud.DrawButton(30, 7, 3, 3, IsPressed(Buttons::I));
hud.DrawButton(25, 7, 3, 3, IsPressed(Buttons::II));
hud.DrawButton(13, 9, 4, 2, IsPressed(Buttons::Select));
hud.DrawButton(18, 9, 4, 2, IsPressed(Buttons::Start));
hud.DrawButton(18, 9, 4, 2, IsPressed(Buttons::Run));
hud.DrawNumber(_port + 1, 16, 2);
hud.DrawNumber(hud.GetControllerIndex() + 1, 16, 2);
}
};

View file

@ -0,0 +1,30 @@
#include "stdafx.h"
#include "PceTurboTap.h"
PceTurboTap::PceTurboTap(Emulator* emu, uint8_t port, ControllerConfig controllers[]) : ControllerHub(emu, ControllerType::PceTurboTap, port, controllers)
{
}
uint8_t PceTurboTap::ReadRam(uint16_t addr)
{
return _ports[_index] ? ReadPort(_index) : 0x0F;
}
void PceTurboTap::WriteRam(uint16_t addr, uint8_t value)
{
ControllerHub::WriteRam(addr, value);
bool sel = (value & 0x01) != 0;
bool prevSel = (_prevValue & 0x01) != 0;
bool clr = (value & 0x02) != 0;
bool prevClr = (_prevValue & 0x02) != 0;
if(!clr && !prevSel && sel) {
_index = (_index + 1) % 5;
}
if(sel && !prevClr && clr) {
_index = 0;
}
_prevValue = value;
}

View file

@ -0,0 +1,19 @@
#pragma once
#include "stdafx.h"
#include "Shared/BaseControlDevice.h"
#include "Shared/ControllerHub.h"
class Emulator;
class PceTurboTap : public ControllerHub<5>
{
private:
uint8_t _index = 0;
uint8_t _prevValue = 0;
public:
PceTurboTap(Emulator* emu, uint8_t port, ControllerConfig controllers[]);
uint8_t ReadRam(uint16_t addr) override;
void WriteRam(uint16_t addr, uint8_t value) override;
};

View file

@ -1,6 +1,8 @@
#include "stdafx.h"
#include "PCE/PceControlManager.h"
#include "PCE/Input/PceController.h"
#include "PCE/Input/PceTurboTap.h"
#include "PCE/Input/PceAvenuePad6.h"
#include "Shared/EmuSettings.h"
#include "Shared/BaseControlDevice.h"
#include "Shared/Emulator.h"
@ -16,9 +18,24 @@ PceControlManagerState& PceControlManager::GetState()
shared_ptr<BaseControlDevice> PceControlManager::CreateControllerDevice(ControllerType type, uint8_t port)
{
//TODO
GameboyConfig cfg = _emu->GetSettings()->GetGameboyConfig();
shared_ptr<BaseControlDevice> device(new PceController(_emu, port, cfg.Controller.Keys));
PcEngineConfig& cfg = _emu->GetSettings()->GetPcEngineConfig();
shared_ptr<BaseControlDevice> device;
switch(type) {
default:
case ControllerType::None: break;
case ControllerType::PceController: device.reset(new PceController(_emu, port, cfg.Port1.Keys)); break;
case ControllerType::PceAvenuePad6: device.reset(new PceAvenuePad6(_emu, port, cfg.Port1.Keys)); break;
case ControllerType::PceTurboTap: {
ControllerConfig controllers[5];
std::copy(cfg.Port1SubPorts, cfg.Port1SubPorts + 5, controllers);
controllers[0].Keys = cfg.Port1.Keys;
device.reset(new PceTurboTap(_emu, port, controllers));
break;
}
}
return device;
}
@ -45,7 +62,7 @@ void PceControlManager::WriteInputPort(uint8_t value)
void PceControlManager::UpdateControlDevices()
{
GameboyConfig cfg = _emu->GetSettings()->GetGameboyConfig();
PcEngineConfig& cfg = _emu->GetSettings()->GetPcEngineConfig();
if(_emu->GetSettings()->IsEqual(_prevConfig, cfg) && _controlDevices.size() > 0) {
//Do nothing if configuration is unchanged
return;
@ -55,7 +72,7 @@ void PceControlManager::UpdateControlDevices()
ClearDevices();
shared_ptr<BaseControlDevice> device(CreateControllerDevice(ControllerType::GameboyController, 0));
shared_ptr<BaseControlDevice> device(CreateControllerDevice(cfg.Port1.Type, 0));
if(device) {
RegisterControlDevice(device);
}

View file

@ -10,7 +10,7 @@ class PceControlManager : public BaseControlManager
{
private:
PceControlManagerState _state = {};
GameboyConfig _prevConfig = {};
PcEngineConfig _prevConfig = {};
public:
PceControlManager(Emulator* emu);

View file

@ -69,22 +69,20 @@ public:
memcpy(_prgRom, romData.data(), _prgRomSize);
//TODO random
memset(_workRam, 0, _workRamSize);
_emu->GetSettings()->InitializeRam(_workRam, _workRamSize);
if(_cdrom) {
_saveRam = new uint8_t[_saveRamSize];
_cdromRam = new uint8_t[_cdromRamSize];
_cardRam = new uint8_t[_cardRamSize];
//TODO random
memset(_saveRam, 0, _saveRamSize);
memset(_cdromRam, 0, _cdromRamSize);
memset(_cardRam, 0, _cardRamSize);
_emu->GetSettings()->InitializeRam(_saveRam, _saveRamSize);
_emu->GetSettings()->InitializeRam(_cdromRam, _cdromRamSize);
_emu->GetSettings()->InitializeRam(_cardRam, _cardRamSize);
_emu->RegisterMemory(MemoryType::PceSaveRam, _saveRam, _saveRamSize);
_emu->RegisterMemory(MemoryType::PceCdromRam, _cdromRam, _cdromRamSize);
_emu->RegisterMemory(MemoryType::PceCardRam, _cardRam, _cardRamSize);
//TODO improve this
_saveRam[0] = 0x48;
_saveRam[1] = 0x55;
_saveRam[2] = 0x42;

View file

@ -40,7 +40,6 @@ PcePpu::PcePpu(Emulator* emu, PceConsole* console)
_state.HorizDisplayWidth = 0x1F;
_state.VertDisplayWidth = 239;
_state.VceScanlineCount = 262;
UpdateFrameTimings();
_emu->RegisterMemory(MemoryType::PceVideoRam, _vram, 0x8000 * sizeof(uint16_t));
_emu->RegisterMemory(MemoryType::PcePaletteRam, _paletteRam, 0x200 * sizeof(uint16_t));
@ -703,7 +702,6 @@ void PcePpu::DrawScanline()
if(_state.HClock == 1365) {
uint16_t row = _state.Scanline - 14;
uint32_t width = PceConstants::GetRowWidth(_state.VceClockDivider);
if(row == 0) {
_currentOutBuffer = _currentOutBuffer == _outBuffer[0] ? _outBuffer[1] : _outBuffer[0];
@ -732,15 +730,7 @@ void PcePpu::SendFrame()
_emu->ProcessEndOfFrame();
_console->GetControlManager()->UpdateInputState();
}
void PcePpu::UpdateFrameTimings()
{
_state.DisplayStart = _state.VertDisplayStart + _state.VertSyncWidth;
_state.VerticalBlankScanline = _state.DisplayStart + _state.VertDisplayWidth + 1;
if(_state.VerticalBlankScanline > 261) {
_state.VerticalBlankScanline = 261;
}
_console->GetControlManager()->UpdateControlDevices();
}
void PcePpu::LoadReadBuffer()
@ -927,7 +917,6 @@ void PcePpu::WriteVdc(uint16_t addr, uint8_t value)
_state.HorizDisplayEnd = value & 0x7F;
} else {
_state.HorizDisplayWidth = value & 0x7F;
UpdateFrameTimings();
}
break;
@ -937,12 +926,10 @@ void PcePpu::WriteVdc(uint16_t addr, uint8_t value)
} else {
_state.VertSyncWidth = value & 0x1F;
}
UpdateFrameTimings();
break;
case 0x0D:
UpdateReg<0x1FF>(_state.VertDisplayWidth, value, msb);
UpdateFrameTimings();
break;
case 0x0E:
@ -1036,7 +1023,6 @@ void PcePpu::WriteVce(uint16_t addr, uint8_t value)
case 1: _state.VceClockDivider = 3; break;
case 2: case 3: _state.VceClockDivider = 2; break;
}
UpdateFrameTimings();
//LogDebug("[Debug] VCE Clock divider: " + HexUtilities::ToHex(_state.VceClockDivider) + " SL: " + std::to_string(_state.Scanline));
break;

View file

@ -120,13 +120,11 @@ private:
void DrawScanline();
void SendFrame();
void UpdateFrameTimings();
uint16_t DotsToClocks(int dots);
void TriggerHdsIrqs();
__declspec(noinline) void IncrementRcrCounter();
void IncScrollY();
__declspec(noinline) void IncScrollY();
__declspec(noinline) void ProcessEndOfScanline();
__declspec(noinline) void ProcessEndOfVisibleFrame();
__declspec(noinline) void ProcessSatbTransfer();
@ -135,14 +133,14 @@ private:
__declspec(noinline) void ProcessVdcEvents();
__declspec(noinline) void ProcessEvent();
void ProcessHorizontalSyncStart();
__declspec(noinline) void ProcessHorizontalSyncStart();
__forceinline uint8_t GetTilePixelColor(const uint16_t chrData[2], const uint8_t shift);
__forceinline uint8_t GetSpritePixelColor(const uint16_t chrData[4], const uint8_t shift);
void ProcessSpriteEvaluation();
void LoadBackgroundTiles();
void LoadSpriteTiles();
__declspec(noinline) void ProcessSpriteEvaluation();
__declspec(noinline) void LoadBackgroundTiles();
__declspec(noinline) void LoadSpriteTiles();
void WaitForVramAccess();
bool IsVramAccessBlocked();

View file

@ -1,6 +1,7 @@
#include "stdafx.h"
#include "PCE/PcePsg.h"
#include "Shared/Emulator.h"
#include "Shared/EmuSettings.h"
#include "Shared/MessageManager.h"
#include "Shared/Audio/SoundMixer.h"
#include "Utilities/Audio/blip_buf.h"
@ -66,6 +67,7 @@ void PcePsg::Run()
{
uint64_t clock = _emu->GetMasterClock();
uint32_t clocksToRun = clock - _lastClock;
PcEngineConfig& cfg = _emu->GetSettings()->GetPcEngineConfig();
while(clocksToRun >= 6) {
uint32_t minTimer = clocksToRun / 6;
for(int i = 0; i < 6; i++) {
@ -80,8 +82,8 @@ void PcePsg::Run()
for(int i = 0; i < 6; i++) {
PcePsgChannel& ch = _channels[i];
ch.Run(minTimer);
leftOutput += ch.GetOutput(true, _state.LeftVolume);
rightOutput += ch.GetOutput(false, _state.RightVolume);
leftOutput += (int32_t)ch.GetOutput(true, _state.LeftVolume) * (int32_t)cfg.ChannelVol[i] / 100;
rightOutput += (int32_t)ch.GetOutput(false, _state.RightVolume) * (int32_t)cfg.ChannelVol[i] / 100;
}
if(_prevLeftOutput != leftOutput) {

View file

@ -62,12 +62,7 @@ struct PcePpuState : public BaseState
uint16_t HClock;
uint16_t Scanline;
uint16_t DisplayCounter;
uint16_t VceScanlineCount;
uint16_t DisplayStart;
uint16_t VerticalBlankScanline;
uint16_t LatchScrollCycle;
uint16_t RcrTriggerCycle;
uint16_t RcrCounter;
uint8_t CurrentReg;

View file

@ -6,6 +6,7 @@
#include "SNES/Input/SnesController.h"
#include "SNES/Input/SnesMouse.h"
#include "NES/Input/NesController.h"
#include "PCE/Input/PceController.h"
#include "Utilities/Serializer.h"
#include "Utilities/StringUtilities.h"
@ -60,6 +61,10 @@ public:
case ControllerType::SnesMouse:
_ports[i].reset(new SnesMouse(emu, 0));
break;
case ControllerType::PceController:
_ports[i].reset(new PceController(emu, 0, controllers[i].Keys));
break;
}
}
@ -126,9 +131,13 @@ public:
int pos = 0;
for(int i = 0; i < HubPortCount; i++) {
if(_ports[i]) {
if(_ports[i] && pos < data.size()) {
int length = data[pos++];
if(pos + length > data.size()) {
break;
}
ControlDeviceState portState;
portState.State.insert(portState.State.begin(), data.begin() + pos, data.begin() + pos + length);
_ports[i]->SetRawState(portState);

View file

@ -169,6 +169,16 @@ GameboyConfig& EmuSettings::GetGameboyConfig()
return _gameboy;
}
void EmuSettings::SetPcEngineConfig(PcEngineConfig& config)
{
_pce = config;
}
PcEngineConfig& EmuSettings::GetPcEngineConfig()
{
return _pce;
}
void EmuSettings::SetPreferences(PreferencesConfig& config)
{
ProcessString(_saveFolder, &config.SaveFolderOverride);
@ -397,6 +407,8 @@ void EmuSettings::InitializeRam(void* data, uint32_t length)
case ConsoleType::GameboyColor:
state = _gameboy.RamPowerOnState;
break;
case ConsoleType::PcEngine: state = _pce.RamPowerOnState; break;
}
switch(state) {

View file

@ -22,6 +22,7 @@ private:
AudioPlayerConfig _audioPlayer;
SnesConfig _snes;
NesConfig _nes;
PcEngineConfig _pce;
atomic<uint32_t> _flags;
atomic<uint64_t> _debuggerFlags;
@ -70,6 +71,9 @@ public:
void SetGameboyConfig(GameboyConfig& config);
GameboyConfig& GetGameboyConfig();
void SetPcEngineConfig(PcEngineConfig& config);
PcEngineConfig& GetPcEngineConfig();
void SetPreferences(PreferencesConfig& config);
PreferencesConfig& GetPreferences();

View file

@ -206,6 +206,11 @@ enum class ControllerType
//Game Boy
GameboyController,
//PC Engine
PceController,
PceTurboTap,
PceAvenuePad6,
};
struct KeyMapping
@ -364,6 +369,16 @@ struct GameboyConfig
uint32_t WaveVol = 100;
};
struct PcEngineConfig
{
ControllerConfig Port1;
ControllerConfig Port1SubPorts[5];
RamState RamPowerOnState = RamState::Random;
uint32_t ChannelVol[6] = { 100, 100, 100, 100, 100, 100 };
};
struct SnesConfig
{
ControllerConfig Port1;

View file

@ -36,6 +36,11 @@ extern "C" {
_emu->GetSettings()->SetGameboyConfig(config);
}
DllExport void __stdcall SetPcEngineConfig(PcEngineConfig config)
{
_emu->GetSettings()->SetPcEngineConfig(config);
}
DllExport void __stdcall SetNesConfig(NesConfig config)
{
_emu->GetSettings()->SetNesConfig(config);

BIN
NewUI/Assets/PceIcon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 255 B

View file

@ -26,6 +26,7 @@ namespace Mesen.Config
[Reactive] public SnesConfig Snes { get; set; } = new();
[Reactive] public NesConfig Nes { get; set; } = new();
[Reactive] public GameboyConfig Gameboy { get; set; } = new();
[Reactive] public PcEngineConfig PcEngine { get; set; } = new();
[Reactive] public PreferencesConfig Preferences { get; set; } = new();
[Reactive] public AudioPlayerConfig AudioPlayer { get; set; } = new();
[Reactive] public DebugConfig Debug { get; set; } = new();
@ -61,6 +62,7 @@ namespace Mesen.Config
Input.ApplyConfig();
Emulation.ApplyConfig();
Gameboy.ApplyConfig();
PcEngine.ApplyConfig();
Nes.ApplyConfig();
Snes.ApplyConfig();
Preferences.ApplyConfig();

View file

@ -108,23 +108,15 @@ namespace Mesen.Config
public virtual void SetDefaultKeys(ControllerType type, KeyPresetType? preset = null)
{
switch(type) {
case ControllerType.NesController:
case ControllerType.FamicomController:
case ControllerType.FamicomControllerP2:
case ControllerType.HoriTrack:
case ControllerType.BandaiHyperShot:
case ControllerType.SnesController:
case ControllerType.GameboyController:
switch(preset) {
case KeyPresetType.WasdKeys: KeyPresets.ApplyWasdLayout(this, type); break;
case KeyPresetType.ArrowKeys: KeyPresets.ApplyArrowLayout(this, type); break;
case KeyPresetType.XboxP1: KeyPresets.ApplyXboxLayout(this, 0, type); break;
case KeyPresetType.XboxP2: KeyPresets.ApplyXboxLayout(this, 1, type); break;
case KeyPresetType.Ps4P1: KeyPresets.ApplyPs4Layout(this, 0, type); break;
case KeyPresetType.Ps4P2: KeyPresets.ApplyPs4Layout(this, 1, type); break;
}
break;
if(type.HasPresets()) {
switch(preset) {
case KeyPresetType.WasdKeys: KeyPresets.ApplyWasdLayout(this, type); break;
case KeyPresetType.ArrowKeys: KeyPresets.ApplyArrowLayout(this, type); break;
case KeyPresetType.XboxP1: KeyPresets.ApplyXboxLayout(this, 0, type); break;
case KeyPresetType.XboxP2: KeyPresets.ApplyXboxLayout(this, 1, type); break;
case KeyPresetType.Ps4P1: KeyPresets.ApplyPs4Layout(this, 0, type); break;
case KeyPresetType.Ps4P2: KeyPresets.ApplyPs4Layout(this, 1, type); break;
}
}
}
@ -301,6 +293,11 @@ namespace Mesen.Config
//Game Boy
GameboyController,
//PC Engine
PceController,
PceTurboTap,
PceAvenuePad6
}
public static class ControllerTypeExtensions
@ -313,6 +310,8 @@ namespace Mesen.Config
case ControllerType.FamicomController:
case ControllerType.FamicomControllerP2:
case ControllerType.GameboyController:
case ControllerType.PceController:
case ControllerType.PceAvenuePad6:
case ControllerType.HoriTrack:
case ControllerType.BandaiHyperShot:
return true;
@ -329,6 +328,8 @@ namespace Mesen.Config
case ControllerType.FamicomController:
case ControllerType.FamicomControllerP2:
case ControllerType.GameboyController:
case ControllerType.PceController:
case ControllerType.PceAvenuePad6:
case ControllerType.Pachinko:
case ControllerType.HoriTrack:
case ControllerType.BandaiHyperShot:
@ -357,6 +358,8 @@ namespace Mesen.Config
case ControllerType.JissenMahjong:
case ControllerType.ExcitingBoxing:
case ControllerType.GameboyController:
case ControllerType.PceController:
case ControllerType.PceAvenuePad6:
case ControllerType.HoriTrack:
case ControllerType.KonamiHyperShot:
case ControllerType.BandaiHyperShot:

View file

@ -18,6 +18,13 @@ namespace Mesen.Config
m.R = InputApi.GetKeyCode("I");
m.Select = InputApi.GetKeyCode("O");
m.Start = InputApi.GetKeyCode("L");
} else if(type == ControllerType.PceAvenuePad6) {
m.X = InputApi.GetKeyCode("H");
m.Y = InputApi.GetKeyCode("Y");
m.L = InputApi.GetKeyCode("U");
m.R = InputApi.GetKeyCode("I");
m.Select = InputApi.GetKeyCode("N");
m.Start = InputApi.GetKeyCode("M");
} else {
m.TurboA = InputApi.GetKeyCode(";");
m.TurboB = InputApi.GetKeyCode("M");
@ -42,6 +49,15 @@ namespace Mesen.Config
m.R = InputApi.GetKeyCode("W");
m.Select = InputApi.GetKeyCode("E");
m.Start = InputApi.GetKeyCode("D");
} else if(type == ControllerType.PceAvenuePad6) {
m.Y = InputApi.GetKeyCode("A");
m.L = InputApi.GetKeyCode("S");
m.R = InputApi.GetKeyCode("D");
m.A = InputApi.GetKeyCode("Z");
m.B = InputApi.GetKeyCode("X");
m.X = InputApi.GetKeyCode("C");
m.Select = InputApi.GetKeyCode("Q");
m.Start = InputApi.GetKeyCode("W");
} else {
m.TurboA = InputApi.GetKeyCode("X");
m.TurboB = InputApi.GetKeyCode("Z");
@ -59,7 +75,7 @@ namespace Mesen.Config
string prefix = "Pad" + (player + 1).ToString() + " ";
m.A = InputApi.GetKeyCode(prefix + "B");
m.B = InputApi.GetKeyCode(prefix + "A");
if(type == ControllerType.SnesController) {
if(type == ControllerType.SnesController || type == ControllerType.PceAvenuePad6) {
m.X = InputApi.GetKeyCode(prefix + "Y");
m.Y = InputApi.GetKeyCode(prefix + "X");
m.L = InputApi.GetKeyCode(prefix + "L1");
@ -81,7 +97,7 @@ namespace Mesen.Config
string prefix = "Joy" + (player + 1).ToString() + " ";
m.A = InputApi.GetKeyCode(prefix + "But3");
m.B = InputApi.GetKeyCode(prefix + "But2");
if(type == ControllerType.SnesController) {
if(type == ControllerType.SnesController || type == ControllerType.PceAvenuePad6) {
m.X = InputApi.GetKeyCode(prefix + "But4");
m.Y = InputApi.GetKeyCode(prefix + "But1");
m.L = InputApi.GetKeyCode(prefix + "But5");

View file

@ -0,0 +1,113 @@
using Mesen.Interop;
using ReactiveUI.Fody.Helpers;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
namespace Mesen.Config
{
public class PcEngineConfig : BaseConfig<PcEngineConfig>
{
[Reactive] public ControllerConfig Port1 { get; set; } = new();
[Reactive] public ControllerConfig Port1A { get; set; } = new();
[Reactive] public ControllerConfig Port1B { get; set; } = new();
[Reactive] public ControllerConfig Port1C { get; set; } = new();
[Reactive] public ControllerConfig Port1D { get; set; } = new();
[Reactive] public ControllerConfig Port1E { get; set; } = new();
[Reactive] public RamState RamPowerOnState { get; set; } = RamState.Random;
[Reactive] public UInt32 Channel1Vol { get; set; } = 100;
[Reactive] public UInt32 Channel2Vol { get; set; } = 100;
[Reactive] public UInt32 Channel3Vol { get; set; } = 100;
[Reactive] public UInt32 Channel4Vol { get; set; } = 100;
[Reactive] public UInt32 Channel5Vol { get; set; } = 100;
[Reactive] public UInt32 Channel6Vol { get; set; } = 100;
public void ApplyConfig()
{
ConfigApi.SetPcEngineConfig(new InteropPcEngineConfig() {
Port1 = Port1.ToInterop(),
Port1A = Port1A.ToInterop(),
Port1B = Port1B.ToInterop(),
Port1C = Port1C.ToInterop(),
Port1D = Port1D.ToInterop(),
Port1E = Port1E.ToInterop(),
RamPowerOnState = RamPowerOnState,
Channel1Vol = Channel1Vol,
Channel2Vol = Channel2Vol,
Channel3Vol = Channel3Vol,
Channel4Vol = Channel4Vol,
Channel5Vol = Channel5Vol,
Channel6Vol = Channel6Vol,
});
}
internal void InitializeDefaults(DefaultKeyMappingType defaultMappings)
{
List<KeyMapping> mappings = new List<KeyMapping>();
if(defaultMappings.HasFlag(DefaultKeyMappingType.Xbox)) {
KeyMapping mapping = new();
KeyPresets.ApplyXboxLayout(mapping, 0, ControllerType.PceController);
mappings.Add(mapping);
}
if(defaultMappings.HasFlag(DefaultKeyMappingType.Ps4)) {
KeyMapping mapping = new();
KeyPresets.ApplyPs4Layout(mapping, 0, ControllerType.PceController);
mappings.Add(mapping);
}
if(defaultMappings.HasFlag(DefaultKeyMappingType.WasdKeys)) {
KeyMapping mapping = new();
KeyPresets.ApplyWasdLayout(mapping, ControllerType.PceController);
mappings.Add(mapping);
}
if(defaultMappings.HasFlag(DefaultKeyMappingType.ArrowKeys)) {
KeyMapping mapping = new();
KeyPresets.ApplyArrowLayout(mapping, ControllerType.PceController);
mappings.Add(mapping);
}
Port1.Type = ControllerType.PceController;
Port1.TurboSpeed = 2;
if(mappings.Count > 0) {
Port1.Mapping1 = mappings[0];
if(mappings.Count > 1) {
Port1.Mapping2 = mappings[1];
if(mappings.Count > 2) {
Port1.Mapping3 = mappings[2];
if(mappings.Count > 3) {
Port1.Mapping4 = mappings[3];
}
}
}
}
}
}
[StructLayout(LayoutKind.Sequential)]
public struct InteropPcEngineConfig
{
public InteropControllerConfig Port1;
public InteropControllerConfig Port1A;
public InteropControllerConfig Port1B;
public InteropControllerConfig Port1C;
public InteropControllerConfig Port1D;
public InteropControllerConfig Port1E;
public RamState RamPowerOnState;
public UInt32 Channel1Vol;
public UInt32 Channel2Vol;
public UInt32 Channel3Vol;
public UInt32 Channel4Vol;
public UInt32 Channel5Vol;
public UInt32 Channel6Vol;
}
}

View file

@ -0,0 +1,23 @@
<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:DesignWidth="400" d:DesignHeight="50"
x:Name="root"
HorizontalAlignment="Stretch"
x:Class="Mesen.Controls.ControllerButton"
>
<DockPanel VerticalAlignment="Stretch" DataContext="{Binding ElementName=root}">
<TextBlock DockPanel.Dock="Top" Text="{Binding Label}" FontSize="18" HorizontalAlignment="Center" Margin="0 0 0 0" />
<c:KeyBindingButton DockPanel.Dock="Top" KeyBinding="{Binding KeyBinding}" Height="40" />
<c:KeyBindingButton DockPanel.Dock="Top" KeyBinding="{Binding TurboKeyBinding}" Height="20" IsVisible="{Binding HasTurbo}" />
<StackPanel Orientation="Horizontal" HorizontalAlignment="Center" IsVisible="{Binding HasTurbo}" VerticalAlignment="Top">
<TextBlock Text="Turbo" FontSize="14" HorizontalAlignment="Center" Margin="0 0 3 0" />
<TextBlock Text="{Binding Label}" FontSize="14" HorizontalAlignment="Center" />
</StackPanel>
</DockPanel>
</UserControl>

View file

@ -0,0 +1,50 @@
using Avalonia;
using Avalonia.Controls;
using Avalonia.Markup.Xaml;
using Avalonia.Media;
using System;
namespace Mesen.Controls
{
public class ControllerButton : UserControl
{
public static readonly StyledProperty<UInt32> KeyBindingProperty = AvaloniaProperty.Register<ControllerButton, UInt32>(nameof(KeyBinding), 0, false, Avalonia.Data.BindingMode.TwoWay);
public static readonly StyledProperty<UInt32> TurboKeyBindingProperty = AvaloniaProperty.Register<ControllerButton, UInt32>(nameof(TurboKeyBinding), 0, false, Avalonia.Data.BindingMode.TwoWay);
public static readonly StyledProperty<string> LabelProperty = AvaloniaProperty.Register<ControllerButton, string>(nameof(Label));
public static readonly StyledProperty<bool> HasTurboProperty = AvaloniaProperty.Register<ControllerButton, bool>(nameof(HasTurbo), true);
public UInt32 KeyBinding
{
get { return GetValue(KeyBindingProperty); }
set { SetValue(KeyBindingProperty, value); }
}
public UInt32 TurboKeyBinding
{
get { return GetValue(TurboKeyBindingProperty); }
set { SetValue(TurboKeyBindingProperty, value); }
}
public string Label
{
get { return GetValue(LabelProperty); }
set { SetValue(LabelProperty, value); }
}
public bool HasTurbo
{
get { return GetValue(HasTurboProperty); }
set { SetValue(HasTurboProperty, value); }
}
public ControllerButton()
{
InitializeComponent();
}
private void InitializeComponent()
{
AvaloniaXamlLoader.Load(this);
}
}
}

View file

@ -15,6 +15,7 @@
<c:ButtonWithIcon Icon="Assets/NesIcon.png" Text="NES" MinWidth="90" Click="OnClickNes" />
<c:ButtonWithIcon Icon="Assets/SnesIcon.png" Text="SNES" MinWidth="90" Click="OnClickSnes" />
<c:ButtonWithIcon Icon="Assets/GameboyIcon.png" Text="Game Boy" MinWidth="90" Click="OnClickGameboy" />
<c:ButtonWithIcon Icon="Assets/PceIcon.png" Text="PC Engine" MinWidth="90" Click="OnClickPcEngine" />
</StackPanel>
</c:OptionSection>
</UserControl>

View file

@ -49,6 +49,11 @@ namespace Mesen.Controls
NavigateTo(ConfigWindowTab.Gameboy);
}
private void OnClickPcEngine(object sender, RoutedEventArgs e)
{
NavigateTo(ConfigWindowTab.PcEngine);
}
private void NavigateTo(ConfigWindowTab console)
{
if(VisualRoot is ConfigWindow wnd && wnd.DataContext is ConfigViewModel cfg) {
@ -87,6 +92,17 @@ namespace Mesen.Controls
};
}
break;
case ConfigWindowTab.PcEngine:
if(cfg.PcEngine != null) {
cfg.PcEngine.SelectedTab = ConfigType switch {
ConfigType.Audio => PceConfigTab.Audio,
ConfigType.Emulation => PceConfigTab.Emulation,
ConfigType.Input => PceConfigTab.Input,
_ or ConfigType.Video => PceConfigTab.Video,
};
}
break;
}
}
}

View file

@ -76,7 +76,7 @@ namespace Mesen.Debugger.StatusViews
}
StackPreview = sb.ToString();
Cycle = ppu.Cycle;
Cycle = ppu.HClock;
Scanline = ppu.Scanline;
FrameCount = ppu.FrameCount;
}
@ -93,7 +93,7 @@ namespace Mesen.Debugger.StatusViews
cpu.PC = RegPC;
cpu.PS = RegPS;
ppu.Cycle = Cycle;
ppu.HClock = Cycle;
ppu.Scanline = Scanline;
DebugApi.SetCpuState(cpu, CpuType.Pce);

View file

@ -550,6 +550,8 @@ namespace Mesen.Debugger.Utilities
Snes,
[IconFile("GameboyIcon")]
Gameboy,
[IconFile("PceIcon")]
PcEngine,
[IconFile("MediaPause")]
Pause,
[IconFile("MediaStop")]

View file

@ -1139,7 +1139,7 @@ namespace Mesen.Debugger.ViewModels
List<RegEntry> entries = new List<RegEntry>() {
new RegEntry("", "State", null),
new RegEntry("", "Cycle (H)", ppu.Cycle, Format.X16),
new RegEntry("", "HClock (H)", ppu.HClock, Format.X16),
new RegEntry("", "Scanline (V)", ppu.Scanline, Format.X16),
new RegEntry("", "Frame Number", ppu.FrameCount),

View file

@ -21,6 +21,7 @@ namespace Mesen.Interop
[DllImport(DllPath)] public static extern void SetInputConfig(InteropInputConfig config);
[DllImport(DllPath)] public static extern void SetEmulationConfig(InteropEmulationConfig config);
[DllImport(DllPath)] public static extern void SetGameboyConfig(InteropGameboyConfig config);
[DllImport(DllPath)] public static extern void SetPcEngineConfig(InteropPcEngineConfig config);
[DllImport(DllPath)] public static extern void SetNesConfig(InteropNesConfig config);
[DllImport(DllPath)] public static extern void SetSnesConfig(InteropSnesConfig config);

View file

@ -1305,14 +1305,10 @@ namespace Mesen.Interop
public struct PcePpuState : BaseState
{
public UInt32 FrameCount;
public UInt16 Cycle;
public UInt16 HClock;
public UInt16 Scanline;
public UInt16 DisplayCounter;
public UInt16 VceScanlineCount;
public UInt16 DisplayStart;
public UInt16 VerticalBlankScanline;
public UInt16 LatchScrollCycle;
public UInt16 RcrTriggerCycle;
public UInt16 RcrCounter;
public byte CurrentReg;

View file

@ -23,7 +23,7 @@
<Form ID="frmLogWindow" Title="Log Window">
<Control ID="btnClose">Close</Control>
</Form>
<Form ID="AudioConfigView" Title="Audio Options">
<Form ID="AudioConfigView">
<Control ID="tpgGeneral">General</Control>
<Control ID="chkMuteSoundInBackground">Mute sound when in background</Control>
@ -58,7 +58,7 @@
<Control ID="tpgEqualizer">Equalizer</Control>
<Control ID="chkEnableEqualizer">Enable Equalizer</Control>
</Form>
<Form ID="NesInputConfigView" Title="Input Settings">
<Form ID="NesInputConfigView">
<Control ID="grpControllers">Controllers</Control>
<Control ID="grpGeneral">General</Control>
<Control ID="lblLightDetectionRadius">Light detection radius for light guns:</Control>
@ -87,7 +87,7 @@
<Control ID="lblExpansionDevice">Expansion device:</Control>
</Form>
<Form ID="SnesInputConfigView" Title="Input Settings">
<Form ID="SnesInputConfigView">
<Control ID="lblPort1">Port 1:</Control>
<Control ID="lblPort2">Port 2:</Control>
<Control ID="lblMultitap1A">Port 1:</Control>
@ -135,7 +135,7 @@
<Control ID="lblBottom">Bottom</Control>
<Control ID="lblRight">Right</Control>
</Form>
<Form ID="VideoConfigView" Title="Video Options">
<Form ID="VideoConfigView">
<Control ID="tpgGeneral">General</Control>
<Control ID="chkIntegerFpsMode">Enable integer FPS mode (e.g: run at 60 fps instead of 60.1)</Control>
<Control ID="chkVerticalSync">Enable vertical sync</Control>
@ -188,7 +188,7 @@
<Control ID="tpgAdvanced">Advanced</Control>
<Control ID="lblScreenRotation">Screen Rotation:</Control>
</Form>
<Form ID="EmulationConfigView" Title="Emulation Settings">
<Form ID="EmulationConfigView">
<Control ID="tpgGeneral">General</Control>
<Control ID="lblEmuSpeedHint">% (0 = Maximum speed)</Control>
<Control ID="lblEmulationSpeed">Emulation Speed:</Control>
@ -331,7 +331,7 @@
<Control ID="lblConsoleType">Console model:</Control>
<Control ID="chkAllowInvalidInput">Allow invalid input (e.g Down + Up or Left + Right at the same time)</Control>
</Form>
<Form ID="GameboyConfigView" Title="Game Boy Config">
<Form ID="GameboyConfigView">
<Control ID="tpgGeneral">General</Control>
<Control ID="lblModel">Model</Control>
<Control ID="chkUseSgb2">Use Super Game Boy 2 timings and behavior</Control>
@ -360,6 +360,34 @@
<Control ID="lblPlayer1">Player 1</Control>
<Control ID="btnSetup">Setup</Control>
</Form>
<Form ID="PceConfigView">
<Control ID="tpgGeneral">General</Control>
<Control ID="tpgVideo">Video</Control>
<Control ID="tpgEmulation">Emulation</Control>
<Control ID="lblDeveloperSettings">Recommended settings for developers (homebrew / ROM hacking)</Control>
<Control ID="lblRamPowerOnState">Default power on state for RAM: </Control>
<Control ID="tpgInput">Input</Control>
<Control ID="tpgAudio">Audio</Control>
<Control ID="grpVolume">Volume</Control>
</Form>
<Form ID="PceInputConfigView">
<Control ID="grpControllers">Controllers</Control>
<Control ID="lblTurboTapConfig">Turbo Tap Configuration</Control>
<Control ID="btnSetup">Setup</Control>
<Control ID="lblController">Controller:</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="lblPort1E">Port 5:</Control>
</Form>
<Form ID="ShortcutKeysTabView">
<Control ID="lblShortcutWarning">Warning: Your current configuration contains conflicting key bindings. If this is not intentional, please review and correct your key bindings.</Control>
<Control ID="colAction">Action</Control>
@ -381,6 +409,7 @@
<Control ID="tabNes">NES</Control>
<Control ID="tabSnes">SNES</Control>
<Control ID="tabGameboy">Game Boy</Control>
<Control ID="tabPcEngine">PC Engine</Control>
<Control ID="tabPreferences">Preferences</Control>
<Control ID="btnOpenMesenFolder">Open Mesen folder</Control>
@ -390,7 +419,7 @@
<Control ID="btnCancel">Cancel</Control>
</Form>
<Form ID="PreferencesConfigView" Title="Preferences">
<Form ID="PreferencesConfigView">
<Control ID="tpgGeneral">General</Control>
<Control ID="lblDisplayLanguage">Display Language:</Control>
<Control ID="chkSingleInstance">Only allow one instance of Mesen at a time</Control>
@ -1355,6 +1384,9 @@
<Value ID="BattleBox">Battle Box</Value>
<Value ID="GameboyController">Gameboy Controller</Value>
<Value ID="PceController">2-button Controller</Value>
<Value ID="PceAvenuePad6">6-button Controller</Value>
<Value ID="PceTurboTap">Turbo Tap</Value>
</Enum>
<Enum ID="NesJissenMahjongButtons">
<Value ID="A">A</Value>
@ -2232,6 +2264,7 @@
<Value ID="Nes">NES</Value>
<Value ID="Snes">SNES</Value>
<Value ID="Gameboy">Game Boy</Value>
<Value ID="PcEngine">PC Engine</Value>
<Value ID="Resume">Resume</Value>
<Value ID="Pause">Pause</Value>

View file

@ -109,6 +109,7 @@
<None Remove="Assets\NudUpArrow.png" />
<None Remove="Assets\NudUpArrowDarkTheme.png" />
<None Remove="Assets\Paste.png" />
<None Remove="Assets\PceIcon.png" />
<None Remove="Assets\Pencil.png" />
<None Remove="Assets\PerfTracker.png" />
<None Remove="Assets\Pipette.png" />
@ -194,6 +195,9 @@
<Compile Update="Controls\ButtonWithIcon.axaml.cs">
<DependentUpon>ButtonWithIcon.axaml</DependentUpon>
</Compile>
<Compile Update="Controls\ControllerButton.axaml.cs">
<DependentUpon>ControllerButton.axaml</DependentUpon>
</Compile>
<Compile Update="Debugger\Controls\CodeScrollBar.axaml.cs">
<DependentUpon>CodeScrollBar.axaml</DependentUpon>
</Compile>
@ -365,6 +369,18 @@
<Compile Update="Debugger\Windows\MemoryToolsWindow.axaml.cs">
<DependentUpon>MemoryToolsWindow.axaml</DependentUpon>
</Compile>
<Compile Update="Views\PceAvenuePad6View.axaml.cs">
<DependentUpon>PceAvenuePad6View.axaml</DependentUpon>
</Compile>
<Compile Update="Views\PceControllerView.axaml.cs">
<DependentUpon>PceControllerView.axaml</DependentUpon>
</Compile>
<Compile Update="Views\PceInputConfigView.axaml.cs">
<DependentUpon>PceInputConfigView.axaml</DependentUpon>
</Compile>
<Compile Update="Views\PceConfigView.axaml.cs">
<DependentUpon>PceConfigView.axaml</DependentUpon>
</Compile>
<Compile Update="Views\InputConfigView.axaml.cs">
<DependentUpon>InputConfigView.axaml</DependentUpon>
</Compile>
@ -559,6 +575,7 @@
<AvaloniaResource Include="Assets\NudUpArrow.png" />
<AvaloniaResource Include="Assets\NudUpArrowDarkTheme.png" />
<AvaloniaResource Include="Assets\Paste.png" />
<AvaloniaResource Include="Assets\PceIcon.png" />
<AvaloniaResource Include="Assets\Pencil.png" />
<AvaloniaResource Include="Assets\PerfTracker.png" />
<AvaloniaResource Include="Assets\Pipette.png" />

View file

@ -18,6 +18,7 @@ namespace Mesen.ViewModels
[Reactive] public SnesConfigViewModel? Snes { get; set; }
[Reactive] public NesConfigViewModel? Nes { get; set; }
[Reactive] public GameboyConfigViewModel? Gameboy { get; set; }
[Reactive] public PceConfigViewModel? PcEngine { get; set; }
[Reactive] public ConfigWindowTab SelectedIndex { get; set; }
@ -50,6 +51,7 @@ namespace Mesen.ViewModels
case ConfigWindowTab.Snes: Snes ??= AddDisposable(new SnesConfigViewModel()); break;
case ConfigWindowTab.Gameboy: Gameboy ??= AddDisposable(new GameboyConfigViewModel()); break;
case ConfigWindowTab.PcEngine: PcEngine ??= AddDisposable(new PceConfigViewModel()); break;
case ConfigWindowTab.Preferences: Preferences ??= AddDisposable(new PreferencesConfigViewModel()); break;
}
@ -72,6 +74,7 @@ namespace Mesen.ViewModels
ConfigManager.Config.Nes = Nes?.Config.Clone() ?? ConfigManager.Config.Nes;
ConfigManager.Config.Snes = Snes?.Config.Clone() ?? ConfigManager.Config.Snes;
ConfigManager.Config.Gameboy = Gameboy?.Config.Clone() ?? ConfigManager.Config.Gameboy;
ConfigManager.Config.PcEngine = PcEngine?.Config.Clone() ?? ConfigManager.Config.PcEngine;
ConfigManager.Config.ApplyConfig();
ConfigManager.Config.Save();
@ -89,7 +92,8 @@ namespace Mesen.ViewModels
Nes = 5,
Snes = 6,
Gameboy = 7,
PcEngine = 8,
//separator
Preferences = 9
Preferences = 10
}
}

View file

@ -407,6 +407,10 @@ namespace Mesen.ViewModels
ActionType = ActionType.Gameboy,
OnClick = () => OpenConfig(wnd, ConfigWindowTab.Gameboy)
},
new MainMenuAction() {
ActionType = ActionType.PcEngine,
OnClick = () => OpenConfig(wnd, ConfigWindowTab.PcEngine)
},
new ContextMenuSeparator(),

View file

@ -0,0 +1,43 @@
using Avalonia.Controls;
using Mesen.Config;
using Mesen.Utilities;
using ReactiveUI;
using ReactiveUI.Fody.Helpers;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reactive.Linq;
namespace Mesen.ViewModels
{
public class PceConfigViewModel : DisposableViewModel
{
[Reactive] public PcEngineConfig Config { get; set; }
[Reactive] public PceConfigTab SelectedTab { get; set; } = 0;
public PceInputConfigViewModel Input { get; private set; }
public PceConfigViewModel()
{
Config = ConfigManager.Config.PcEngine.Clone();
Input = new PceInputConfigViewModel(Config);
if(Design.IsDesignMode) {
return;
}
AddDisposable(Input);
AddDisposable(ReactiveHelper.RegisterRecursiveObserver(Config, (s, e) => { Config.ApplyConfig(); }));
}
}
public enum PceConfigTab
{
General,
Audio,
Emulation,
Input,
Video
}
}

View file

@ -0,0 +1,41 @@
using Avalonia.Threading;
using Mesen.Config;
using ReactiveUI;
using ReactiveUI.Fody.Helpers;
using System;
using System.Reactive.Linq;
namespace Mesen.ViewModels
{
public class PceInputConfigViewModel : DisposableViewModel
{
[Reactive] public PcEngineConfig Config { get; set; }
[Reactive] public bool HasTurboTap { get; private set; }
public Enum[] AvailableControllerTypesP1 => new Enum[] {
ControllerType.None,
ControllerType.PceController,
ControllerType.PceAvenuePad6,
ControllerType.PceTurboTap,
};
public Enum[] AvailableControllerTypesTurboTap => new Enum[] {
ControllerType.None,
ControllerType.PceController,
};
[Obsolete("For designer only")]
public PceInputConfigViewModel() : this(new PcEngineConfig()) { }
public PceInputConfigViewModel(PcEngineConfig config)
{
Config = config;
AddDisposable(this.WhenAnyValue(x => x.Config.Port1.Type).Subscribe(t => {
Dispatcher.UIThread.Post(() => {
HasTurboTap = Config.Port1.Type == ControllerType.PceTurboTap;
});
}));
}
}
}

View file

@ -25,6 +25,8 @@ namespace Mesen.Views
ControllerType.BandaiHyperShot => new NesControllerView(),
ControllerType.HoriTrack => new NesControllerView(),
ControllerType.GameboyController => new NesControllerView(),
ControllerType.PceController => new PceControllerView(),
ControllerType.PceAvenuePad6 => new PceAvenuePad6View(),
_ => new DefaultControllerView()
};
}

View file

@ -41,10 +41,19 @@
<StackPanel>
<c:GroupBox Header="Volume" HorizontalAlignment="Left">
<StackPanel Orientation="Horizontal" Height="150">
<c:MesenSlider Text="Square 1" Minimum="0" Maximum="100" Orientation="Vertical" Value="{CompiledBinding Config.Square1Vol}" />
<c:MesenSlider Text="Square 2" Minimum="0" Maximum="100" Orientation="Vertical" Value="{CompiledBinding Config.Square2Vol}" />
<c:MesenSlider Text="Wave" Minimum="0" Maximum="100" Orientation="Vertical" Value="{CompiledBinding Config.WaveVol}" />
<c:MesenSlider Text="Noise" Minimum="0" Maximum="100" Orientation="Vertical" Value="{CompiledBinding Config.NoiseVol}" />
<StackPanel.Styles>
<Style Selector="c|MesenSlider">
<Setter Property="Minimum" Value="0" />
<Setter Property="Maximum" Value="100" />
<Setter Property="Orientation" Value="Vertical" />
<Setter Property="Margin" Value="5 0" />
</Style>
</StackPanel.Styles>
<c:MesenSlider Text="Square 1" Value="{CompiledBinding Config.Square1Vol}" />
<c:MesenSlider Text="Square 2" Value="{CompiledBinding Config.Square2Vol}" />
<c:MesenSlider Text="Wave" Value="{CompiledBinding Config.WaveVol}" />
<c:MesenSlider Text="Noise" Value="{CompiledBinding Config.NoiseVol}" />
</StackPanel>
</c:GroupBox>
</StackPanel>

View file

@ -0,0 +1,86 @@
<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:vm="using:Mesen.ViewModels"
xmlns:l="using:Mesen.Localization"
xmlns:c="using:Mesen.Controls"
xmlns:cfg="using:Mesen.Config"
mc:Ignorable="d" d:DesignWidth="610" d:DesignHeight="230"
VerticalAlignment="Stretch"
HorizontalAlignment="Stretch"
x:Name="root"
x:DataType="vm:KeyMappingViewModel"
x:Class="Mesen.Views.PceAvenuePad6View"
>
<Design.DataContext>
<vm:KeyMappingViewModel />
</Design.DataContext>
<Border BorderBrush="LightGray" BorderThickness="2" Padding="3" Width="610" Height="230" HorizontalAlignment="Left" VerticalAlignment="Top">
<Canvas>
<Panel Canvas.Top="10" >
<Ellipse Width="200" Height="200" Stroke="LightGray" StrokeThickness="2" />
<Grid ColumnDefinitions="*,*" RowDefinitions="*,*,*" Width="200" Height="200">
<c:KeyBindingButton KeyBinding="{CompiledBinding Mapping.Up}" Grid.Row="0" Grid.Column="0" Grid.ColumnSpan="2" Width="80" Height="40" HorizontalAlignment="Center" />
<c:KeyBindingButton KeyBinding="{CompiledBinding Mapping.Left}" Grid.Row="1" Grid.Column="0" Width="80" Height="40" HorizontalAlignment="Center" />
<c:KeyBindingButton KeyBinding="{CompiledBinding Mapping.Right}" Grid.Row="1" Grid.Column="1" Width="80" Height="40" HorizontalAlignment="Center" />
<c:KeyBindingButton KeyBinding="{CompiledBinding Mapping.Down}" Grid.Row="2" Grid.Column="0" Grid.ColumnSpan="2" Width="80" Height="40" HorizontalAlignment="Center" />
</Grid>
</Panel>
<Panel Canvas.Top="0" Canvas.Right="5" Width="205" Height="220">
<Rectangle Fill="#F2F2F2" />
<StackPanel Orientation="Horizontal">
<c:ControllerButton
Margin="5 0 0 0"
Label="IV"
KeyBinding="{CompiledBinding Mapping.Y}"
TurboKeyBinding="{CompiledBinding Mapping.TurboY}"
/>
<c:ControllerButton
Margin="5 0 0 0"
Label="V"
KeyBinding="{CompiledBinding Mapping.L}"
TurboKeyBinding="{CompiledBinding Mapping.TurboL}"
/>
<c:ControllerButton
Margin="5 0 0 0"
Label="VI"
KeyBinding="{CompiledBinding Mapping.R}"
TurboKeyBinding="{CompiledBinding Mapping.TurboR}"
/>
</StackPanel>
<StackPanel Orientation="Horizontal" Margin="0 110 0 0">
<c:ControllerButton
Margin="5 0 0 0"
Label="III"
KeyBinding="{CompiledBinding Mapping.X}"
TurboKeyBinding="{CompiledBinding Mapping.TurboX}"
/>
<c:ControllerButton
Margin="5 0 0 0"
Label="II"
KeyBinding="{CompiledBinding Mapping.B}"
TurboKeyBinding="{CompiledBinding Mapping.TurboB}"
/>
<c:ControllerButton
Margin="5 0 0 0"
Label="I"
KeyBinding="{CompiledBinding Mapping.A}"
TurboKeyBinding="{CompiledBinding Mapping.TurboA}"
/>
</StackPanel>
</Panel>
<Grid ColumnDefinitions="*,Auto,Auto,*" RowDefinitions="Auto,Auto" Width="580" Canvas.Bottom="30">
<c:KeyBindingButton KeyBinding="{CompiledBinding Mapping.Select}" Grid.Column="1" Width="80" />
<TextBlock Grid.Column="1" Grid.Row="1" Text="Select" FontSize="14" HorizontalAlignment="Center" />
<c:KeyBindingButton KeyBinding="{CompiledBinding Mapping.Start}" Grid.Column="2" Width="80" />
<TextBlock Grid.Column="2" Grid.Row="1" Text="Run" FontSize="14" HorizontalAlignment="Center" />
</Grid>
</Canvas>
</Border>
</UserControl>

View file

@ -0,0 +1,18 @@
using Avalonia.Controls;
using Avalonia.Markup.Xaml;
namespace Mesen.Views
{
public class PceAvenuePad6View : UserControl
{
public PceAvenuePad6View()
{
InitializeComponent();
}
private void InitializeComponent()
{
AvaloniaXamlLoader.Load(this);
}
}
}

View file

@ -0,0 +1,76 @@
<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:vm="using:Mesen.ViewModels"
xmlns:c="using:Mesen.Controls"
xmlns:dc="using:Mesen.Debugger.Controls"
xmlns:cfg="using:Mesen.Config"
xmlns:v="using:Mesen.Views"
xmlns:l="using:Mesen.Localization"
mc:Ignorable="d" d:DesignWidth="400" d:DesignHeight="350"
VerticalAlignment="Stretch"
HorizontalAlignment="Stretch"
x:DataType="vm:PceConfigViewModel"
x:Class="Mesen.Views.PceConfigView"
>
<Design.DataContext>
<vm:PceConfigViewModel />
</Design.DataContext>
<UserControl.Styles>
<Style Selector="dc|PaletteSelector">
<Setter Property="SelectionMode" Value="None" />
<Setter Property="ColumnCount" Value="4" />
<Setter Property="Width" Value="80" />
<Setter Property="Height" Value="20" />
</Style>
</UserControl.Styles>
<TabControl TabStripPlacement="Top" SelectedIndex="{CompiledBinding SelectedTab}">
<TabItem Header="{l:Translate tpgGeneral}">
<StackPanel>
</StackPanel>
</TabItem>
<TabItem Header="{l:Translate tpgAudio}">
<StackPanel>
<c:GroupBox Header="Volume" HorizontalAlignment="Left">
<StackPanel Orientation="Horizontal" Height="150">
<StackPanel.Styles>
<Style Selector="c|MesenSlider">
<Setter Property="Minimum" Value="0" />
<Setter Property="Maximum" Value="100" />
<Setter Property="Orientation" Value="Vertical" />
<Setter Property="Margin" Value="5 0" />
</Style>
</StackPanel.Styles>
<c:MesenSlider Text="Channel 1" Value="{CompiledBinding Config.Channel1Vol}" />
<c:MesenSlider Text="Channel 2" Value="{CompiledBinding Config.Channel2Vol}" />
<c:MesenSlider Text="Channel 3" Value="{CompiledBinding Config.Channel3Vol}" />
<c:MesenSlider Text="Channel 4" Value="{CompiledBinding Config.Channel4Vol}" />
<c:MesenSlider Text="Channel 5" Value="{CompiledBinding Config.Channel5Vol}" />
<c:MesenSlider Text="Channel 6" Value="{CompiledBinding Config.Channel6Vol}" />
</StackPanel>
</c:GroupBox>
</StackPanel>
</TabItem>
<TabItem Header="{l:Translate tpgEmulation}">
<c:OptionSection Header="{l:Translate lblDeveloperSettings}" Margin="0">
<StackPanel Orientation="Horizontal">
<TextBlock Text="{l:Translate lblRamPowerOnState}" />
<c:EnumComboBox SelectedItem="{CompiledBinding Config.RamPowerOnState}" Width="200" />
</StackPanel>
</c:OptionSection>
</TabItem>
<TabItem Header="{l:Translate tpgInput}">
<v:PceInputConfigView DataContext="{CompiledBinding Input}" />
</TabItem>
<TabItem Header="{l:Translate tpgVideo}">
</TabItem>
</TabControl>
</UserControl>

View file

@ -0,0 +1,30 @@
#pragma warning disable CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable.
using Avalonia;
using Avalonia.Controls;
using Avalonia.Markup.Xaml;
using Mesen.Utilities;
using Mesen.Config;
using Mesen.Debugger.Controls;
using Mesen.ViewModels;
using Mesen.Windows;
using System;
using Avalonia.Media;
using System.Threading.Tasks;
using Avalonia.Interactivity;
namespace Mesen.Views
{
public class PceConfigView : UserControl
{
public PceConfigView()
{
InitializeComponent();
}
private void InitializeComponent()
{
AvaloniaXamlLoader.Load(this);
}
}
}

View file

@ -0,0 +1,60 @@
<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:vm="using:Mesen.ViewModels"
xmlns:l="using:Mesen.Localization"
xmlns:c="using:Mesen.Controls"
xmlns:cfg="using:Mesen.Config"
mc:Ignorable="d" d:DesignWidth="610" d:DesignHeight="230"
VerticalAlignment="Stretch"
HorizontalAlignment="Stretch"
x:Name="root"
x:DataType="vm:KeyMappingViewModel"
x:Class="Mesen.Views.PceControllerView"
>
<Design.DataContext>
<vm:KeyMappingViewModel />
</Design.DataContext>
<Border BorderBrush="LightGray" BorderThickness="2" Padding="3" Width="610" Height="230" HorizontalAlignment="Left" VerticalAlignment="Top">
<Canvas>
<Panel Canvas.Top="10" >
<Ellipse Width="200" Height="200" Stroke="LightGray" StrokeThickness="2" />
<Grid ColumnDefinitions="*,*" RowDefinitions="*,*,*" Width="200" Height="200">
<c:KeyBindingButton KeyBinding="{CompiledBinding Mapping.Up}" Grid.Row="0" Grid.Column="0" Grid.ColumnSpan="2" Width="80" Height="40" HorizontalAlignment="Center" />
<c:KeyBindingButton KeyBinding="{CompiledBinding Mapping.Left}" Grid.Row="1" Grid.Column="0" Width="80" Height="40" HorizontalAlignment="Center" />
<c:KeyBindingButton KeyBinding="{CompiledBinding Mapping.Right}" Grid.Row="1" Grid.Column="1" Width="80" Height="40" HorizontalAlignment="Center" />
<c:KeyBindingButton KeyBinding="{CompiledBinding Mapping.Down}" Grid.Row="2" Grid.Column="0" Grid.ColumnSpan="2" Width="80" Height="40" HorizontalAlignment="Center" />
</Grid>
</Panel>
<Panel Canvas.Top="80" Canvas.Right="5" Width="200" Height="110">
<Rectangle Fill="#F2F2F2" />
<StackPanel Orientation="Horizontal">
<c:ControllerButton
Margin="10 0 0 0"
Label="II"
KeyBinding="{CompiledBinding Mapping.B}"
TurboKeyBinding="{CompiledBinding Mapping.TurboB}"
/>
<c:ControllerButton
Margin="55 0 0 0"
Label="I"
KeyBinding="{CompiledBinding Mapping.A}"
TurboKeyBinding="{CompiledBinding Mapping.TurboA}"
/>
</StackPanel>
</Panel>
<Grid ColumnDefinitions="*,Auto,Auto,*" RowDefinitions="Auto,Auto" Width="580" Canvas.Bottom="30">
<c:KeyBindingButton KeyBinding="{CompiledBinding Mapping.Select}" Grid.Column="1" Width="80" />
<TextBlock Grid.Column="1" Grid.Row="1" Text="Select" FontSize="14" HorizontalAlignment="Center" />
<c:KeyBindingButton KeyBinding="{CompiledBinding Mapping.Start}" Grid.Column="2" Width="80" />
<TextBlock Grid.Column="2" Grid.Row="1" Text="Run" FontSize="14" HorizontalAlignment="Center" />
</Grid>
</Canvas>
</Border>
</UserControl>

View file

@ -0,0 +1,18 @@
using Avalonia.Controls;
using Avalonia.Markup.Xaml;
namespace Mesen.Views
{
public class PceControllerView : UserControl
{
public PceControllerView()
{
InitializeComponent();
}
private void InitializeComponent()
{
AvaloniaXamlLoader.Load(this);
}
}
}

View file

@ -0,0 +1,88 @@
<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:vm="using:Mesen.ViewModels"
xmlns:l="using:Mesen.Localization"
xmlns:c="using:Mesen.Controls"
xmlns:v="using:Mesen.Views"
xmlns:cfg="using:Mesen.Config"
mc:Ignorable="d" d:DesignWidth="500" d:DesignHeight="450"
VerticalAlignment="Stretch"
HorizontalAlignment="Stretch"
x:DataType="vm:PceInputConfigViewModel"
x:Class="Mesen.Views.PceInputConfigView"
>
<Design.DataContext>
<vm:PceInputConfigViewModel />
</Design.DataContext>
<ScrollViewer AllowAutoHide="False">
<StackPanel>
<c:OptionSection Header="{l:Translate grpControllers}" Margin="0">
<Grid ColumnDefinitions="Auto,*,Auto" RowDefinitions="Auto,Auto,Auto,Auto,Auto" HorizontalAlignment="Left">
<TextBlock Grid.Row="0" Grid.Column="0" Text="{l:Translate lblController}" />
<c:InputComboBox
Grid.Row="0" Grid.Column="1"
Config="{CompiledBinding Config.Port1}"
ControllerType="{CompiledBinding Config.Port1.Type}"
AvailableValues="{CompiledBinding AvailableControllerTypesP1}"
/>
<c:GroupBox
Header="{l:Translate lblTurboTapConfig}"
IsVisible="{CompiledBinding HasTurboTap}"
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,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 AvailableControllerTypesTurboTap}"
/>
<TextBlock Grid.Row="1" Grid.Column="0" Text="{l:Translate lblPort1B}" />
<c:InputComboBox
Grid.Row="1" Grid.Column="1"
Config="{CompiledBinding Config.Port1B}"
ControllerType="{CompiledBinding Config.Port1B.Type}"
AvailableValues="{CompiledBinding AvailableControllerTypesTurboTap}"
/>
<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 AvailableControllerTypesTurboTap}"
/>
<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 AvailableControllerTypesTurboTap}"
/>
<TextBlock Grid.Row="4" Grid.Column="0" Text="{l:Translate lblPort1E}" />
<c:InputComboBox
Grid.Row="4" Grid.Column="1"
Config="{CompiledBinding Config.Port1E}"
ControllerType="{CompiledBinding Config.Port1E.Type}"
AvailableValues="{CompiledBinding AvailableControllerTypesTurboTap}"
/>
</Grid>
</c:GroupBox>
</Grid>
</c:OptionSection>
</StackPanel>
</ScrollViewer>
</UserControl>

View file

@ -0,0 +1,18 @@
using Avalonia.Controls;
using Avalonia.Markup.Xaml;
namespace Mesen.Views
{
public class PceInputConfigView : UserControl
{
public PceInputConfigView()
{
InitializeComponent();
}
private void InitializeComponent()
{
AvaloniaXamlLoader.Load(this);
}
}
}

View file

@ -41,6 +41,9 @@
<DataTemplate DataType="{x:Type vm:GameboyConfigViewModel}">
<v:GameboyConfigView />
</DataTemplate>
<DataTemplate DataType="{x:Type vm:PceConfigViewModel}">
<v:PceConfigView />
</DataTemplate>
<DataTemplate DataType="{x:Type vm:PreferencesConfigViewModel}">
<v:PreferencesConfigView />
</DataTemplate>
@ -124,6 +127,15 @@
</TabItem.Header>
</TabItem>
<TabItem Content="{CompiledBinding PcEngine}">
<TabItem.Header>
<StackPanel Orientation="Horizontal">
<Image Source="/Assets/PceIcon.png" Margin="0 0 10 0" />
<TextBlock VerticalAlignment="Center" Text="{l:Translate tabPcEngine}" />
</StackPanel>
</TabItem.Header>
</TabItem>
<TabItem IsEnabled="False" MinHeight="10">
<TabItem.Header>
<Rectangle Fill="LightGray" Height="1" />