Skip to content

Commit

Permalink
WASM C64 joystick configuration UI.
Browse files Browse the repository at this point in the history
  • Loading branch information
highbyte committed Oct 22, 2023
1 parent 706ddbb commit 0feb08b
Show file tree
Hide file tree
Showing 19 changed files with 462 additions and 223 deletions.
10 changes: 0 additions & 10 deletions src/apps/Highbyte.DotNet6502.App.SkiaNative/EmulatorConfig.cs
Original file line number Diff line number Diff line change
Expand Up @@ -28,14 +28,4 @@ public void Validate(SystemList<SkiaRenderContext, SilkNetInputHandlerContext, N
throw new Exception($"Setting {nameof(DefaultEmulator)} value {DefaultEmulator} is not supported. Valid values are: {string.Join(',', systemList.Systems)}");
Monitor.Validate();
}

//public IEmulatorSystemConfig GetEmulatorSystemConfig(string systemName)
//{
// return systemName switch
// {
// C64.SystemName => new C64SilkNetConfig(),
// GenericComputer.SystemName => new GenericComputerSilkNetConfig (),
// _ => throw new Exception($"System {systemName} is not supported.")
// };
//}
}
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
</ItemGroup>

<ItemGroup>
<PackageReference Include="AutoMapper" Version="12.0.1" />
<PackageReference Include="Blazored.LocalStorage" Version="4.4.0" />
<PackageReference Include="Blazored.Modal" Version="7.1.0" />
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly" Version="7.0.11" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,188 +40,241 @@ else
{
<p>@LoadedROMCount/@_maxC64AllowedRomFiles ROMs loaded</p>
@if (!string.IsNullOrEmpty(_validationMessage))
{
<p>@_validationMessage</p>
}
{
<p>@_validationMessage</p>
}
<ul>
@foreach (var romName in C64Config.RequiredROMs)
{
@if (GetLoadedRoms().ContainsKey(romName))
{
<li>@romName @GetLoadedRoms()[romName].Length bytes</li>
}
else
{
<li>@romName - not loaded</li>
@if (GetLoadedRoms().ContainsKey(romName))
{
<li>@romName @GetLoadedRoms()[romName].Length bytes</li>
}
else
{
<li>@romName - not loaded</li>
}
}
}
</ul>
}

<p></p>
<h2>General settings</h2>
<div class="table">
<div class="table-row">
<div class="table-cell twocol">Audio enabled (experimental):</div>
<div class="table-cell twocol"><input @bind="C64Config.AudioEnabled" @bind:event="oninput" type="checkbox" id="audioEnabled" title="Enable audio"/></div>

<div class="row">
<div class="col">
<h2>General settings</h2>
<div class="table">
<div class="table-row">
<div class="table-cell twocol">Audio enabled (experimental):</div>
<div class="table-cell twocol"><input @bind="C64Config.AudioEnabled" @bind:event="oninput" type="checkbox" id="audioEnabled" title="Enable audio" /></div>
</div>
</div>
</div>
</div>


<h2>Joystick</h2>
@{
var gampadToJoystickMap = C64AspNetGamepad.AspNetGamePadToC64JoystickMap;
int joystick = 2;
}
<div class="table">
<div class="table-row">
<div class="table-cell twocol">Joystick @joystick action</div>
<div class="table-cell twocol">Gampad button</div>
</div>
@{
foreach (var mapKey in gampadToJoystickMap)
{
<div class="row">
<div class="column">
<h2>Joystick</h2>
@{
var gampadToJoystickMap = C64HostConfig.InputConfig.GamePadToC64JoystickMap[C64HostConfig.InputConfig.CurrentJoystick];
}
<div class="table">
<div class="table-row">
<div class="table-cell twocol">Select current joystick</div>
<div class="table-cell twocol">
<select value="@C64HostConfig.InputConfig.CurrentJoystick" @onchange="OnSelectJoystickChanged">
@foreach (var joystick in C64HostConfig.InputConfig.AvailableJoysticks.ToArray())
{
<option value="@joystick"> @joystick </option>
}
</select>
</div>
</div>
<div class="table-row">
<div class="table-cell twocol">@string.Join(",", mapKey.Value)</div>
<div class="table-cell twocol">@string.Join(",", mapKey.Key)</div>
<div class="table-cell twocol">Joystick @C64HostConfig.InputConfig.CurrentJoystick action</div>
<div class="table-cell twocol">Gampad button</div>
</div>
@{
foreach (var mapKey in gampadToJoystickMap)
{
<div class="table-row">
<div class="table-cell twocol">@string.Join(",", mapKey.Value)</div>
<div class="table-cell twocol">@string.Join(",", mapKey.Key)</div>
</div>
}
}
</div>
</div>

<div class="column">

<h2>Joystick keyboard</h2>
@{
var keyToJoystickMap = C64Config.KeyboardJoystickMap;
}
}
</div>
<div class="table">
<div class="table-row">
<div class="table-cell twocol">Enabled</div>
<div class="table-cell twocol"><input @bind="C64Config.KeyboardJoystickEnabled" @bind:event="oninput" type="checkbox" id="keyboardJoystickEnabled" title="Enable Joystick Keyboard" /></div>
</div>

<h2>Joystick keyboard</h2>
@{
var keyToJoystickMap = C64Config.KeyboardJoystickMap;
}
<div class="table">
<div class="table-row">
<div class="table-cell twocol">Enabled</div>
<div class="table-cell twocol"><input @bind="C64Config.KeyboardJoystickEnabled" @bind:event="oninput" type="checkbox" id="keyboardJoystickEnabled" title="Enable Joystick Keyboard" /></div>
</div>
<div class="table-row">
<div class="table-cell twocol">Joystick @joystick action</div>
<div class="table-cell twocol">Key</div>
</div>
@{
foreach (var mapKey in keyToJoystickMap.GetMap(joystick))
{
<div class="table-row">
<div class="table-cell twocol">@string.Join(",", mapKey.Value)</div>
<div class="table-cell twocol">@string.Join(",", mapKey.Key)</div>
<div class="table-cell twocol">Select current keyboard joystick</div>
<div class="table-cell twocol">
<select value="@C64Config.KeyboardJoystick" @onchange="OnSelectKeyboardJoystickChanged">
@foreach (var joystick in C64HostConfig.InputConfig.AvailableJoysticks.ToArray())
{
<option value="@joystick"> @joystick </option>
}
</select>
</div>
</div>
}
}

<div class="table-row">
<div class="table-cell twocol">Joystick @C64Config.KeyboardJoystick action</div>
<div class="table-cell twocol">Key</div>
</div>
@{
foreach (var mapKey in keyToJoystickMap.GetMap(C64Config.KeyboardJoystick))
{
<div class="table-row">
<div class="table-cell twocol">@string.Join(",", mapKey.Value)</div>
<div class="table-cell twocol">@string.Join(",", mapKey.Key)</div>
</div>
}
}
</div>
</div>

</div>


<p></p>
<button @onclick="Ok">Ok</button>

@code {

[CascadingParameter] BlazoredModalInstance BlazoredModal { get; set; } = default!;
[CascadingParameter] BlazoredModalInstance BlazoredModal { get; set; } = default!;

[Parameter] public ISystemConfig SystemConfig { get; set; }
[Parameter] public ISystemConfig SystemConfig { get; set; }

public C64Config C64Config
{
get
[Parameter] public IHostSystemConfig HostSystemConfig { get; set; }

public C64Config C64Config
{
get
{
return (C64Config)SystemConfig;
}
}

public C64HostConfig C64HostConfig
{
get
{
return (C64HostConfig)HostSystemConfig;
}
}
}

protected override void OnInitialized() => BlazoredModal.SetTitle("C64 config");
protected override void OnInitialized() => BlazoredModal.SetTitle("C64 config");


private async Task UnloadROMs() => C64Config.ROMs = new List<ROM>();
private async Task UnloadROMs() => C64Config.ROMs = new List<ROM>();


private async Task Ok() => await BlazoredModal.CloseAsync(ModalResult.Ok(SystemConfig));
private async Task Ok() => await BlazoredModal.CloseAsync(ModalResult.Ok((SystemConfig, HostSystemConfig)));

private bool _isLoadingC64Roms;
private long _maxC64RomFileSize = 1024 * 8;
private int _maxC64AllowedRomFiles = 3;
private bool _isLoadingC64Roms;
private long _maxC64RomFileSize = 1024 * 8;
private int _maxC64AllowedRomFiles = 3;

private Dictionary<string, byte[]> GetLoadedRoms()
{
var dict = new Dictionary<string, byte[]>();
foreach (var rom in C64Config.ROMs)
private Dictionary<string, byte[]> GetLoadedRoms()
{
dict[rom.Name] = rom.Data == null ? new byte[]{} : rom.Data;
var dict = new Dictionary<string, byte[]>();
foreach (var rom in C64Config.ROMs)
{
dict[rom.Name] = rom.Data == null ? new byte[] { } : rom.Data;
}
return dict;
}
return dict;
}

private int LoadedROMCount => GetLoadedRoms().Count;
private int LoadedROMCount => GetLoadedRoms().Count;

private string _validationMessage = "";
private string _validationMessage = "";

private async Task OnC64RomFilePickerChange(InputFileChangeEventArgs e)
{
if (C64Config == null)
return;
private async Task OnC64RomFilePickerChange(InputFileChangeEventArgs e)
{
if (C64Config == null)
return;

_isLoadingC64Roms = true;
_validationMessage = "";
_isLoadingC64Roms = true;
_validationMessage = "";

foreach (var file in e.GetMultipleFiles(_maxC64AllowedRomFiles))
{
try
foreach (var file in e.GetMultipleFiles(_maxC64AllowedRomFiles))
{
if (file.Size > _maxC64RomFileSize)
try
{
_isLoadingC64Roms = false;
_validationMessage += $"File {file.Name} size {file.Size} is more than limit {_maxC64RomFileSize}. ";
continue;
}
if (file.Size > _maxC64RomFileSize)
{
_isLoadingC64Roms = false;
_validationMessage += $"File {file.Name} size {file.Size} is more than limit {_maxC64RomFileSize}. ";
continue;
}

bool isKernal = file.Name.Contains("kern", StringComparison.InvariantCultureIgnoreCase);
bool isBasic = file.Name.Contains("bas", StringComparison.InvariantCultureIgnoreCase);
bool isChargen = file.Name.Contains("char", StringComparison.InvariantCultureIgnoreCase);
if (!isKernal && !isBasic && !isChargen)
{
_isLoadingC64Roms = false;
_validationMessage = $"File name {file.Name} does not contain one of the following string: kern, bas, char ";
continue;
}

bool isKernal = file.Name.Contains("kern", StringComparison.InvariantCultureIgnoreCase);
bool isBasic = file.Name.Contains("bas", StringComparison.InvariantCultureIgnoreCase);
bool isChargen = file.Name.Contains("char", StringComparison.InvariantCultureIgnoreCase);
if (!isKernal && !isBasic && !isChargen)
var fileBuffer = new byte[file.Size];
//var fileStream = e.File.OpenReadStream(file.Size);
await file.OpenReadStream().ReadAsync(fileBuffer);
var fileSize = fileBuffer.Length;

if (isKernal)
SetROM(C64Config.KERNAL_ROM_NAME, fileBuffer);
else if (isBasic)
SetROM(C64Config.BASIC_ROM_NAME, fileBuffer);
else if (isChargen)
SetROM(C64Config.CHARGEN_ROM_NAME, fileBuffer);
}
catch (Exception ex)
{
_isLoadingC64Roms = false;
_validationMessage = $"File name {file.Name} does not contain one of the following string: kern, bas, char ";
continue;
//Logger.LogError("File: {Filename} Error: {Error}",
// file.Name, ex.Message);
}
}
_isLoadingC64Roms = false;
this.StateHasChanged();
}

var fileBuffer = new byte[file.Size];
//var fileStream = e.File.OpenReadStream(file.Size);
await file.OpenReadStream().ReadAsync(fileBuffer);
var fileSize = fileBuffer.Length;

if (isKernal)
SetROM(C64Config.KERNAL_ROM_NAME, fileBuffer);
else if (isBasic)
SetROM(C64Config.BASIC_ROM_NAME, fileBuffer);
else if (isChargen)
SetROM(C64Config.CHARGEN_ROM_NAME, fileBuffer);
private void SetROM(string romName, byte[] data)
{
ROM rom;
if (!C64Config.ROMs.Exists(x => x.Name == romName))
{
rom = new()
{
Name = romName
};
C64Config.ROMs.Add(rom);
}
catch (Exception ex)
else
{
//Logger.LogError("File: {Filename} Error: {Error}",
// file.Name, ex.Message);
rom = C64Config.ROMs.Single(x => x.Name == romName);
}
rom.Data = data;
}
_isLoadingC64Roms = false;
this.StateHasChanged();
}

private void SetROM(string romName, byte[] data)
{
ROM rom;
if (!C64Config.ROMs.Exists(x => x.Name == romName))
{
rom = new()
{
Name = romName
};
C64Config.ROMs.Add(rom);
}
else
{
rom = C64Config.ROMs.Single(x => x.Name == romName);
}
rom.Data = data;
}
private async Task OnSelectJoystickChanged(ChangeEventArgs e)
=> C64HostConfig.InputConfig.CurrentJoystick = int.Parse(e.Value.ToString());

private async Task OnSelectKeyboardJoystickChanged(ChangeEventArgs e)
=> C64Config.KeyboardJoystick = int.Parse(e.Value.ToString());

}
Loading

0 comments on commit 0feb08b

Please sign in to comment.