-
Notifications
You must be signed in to change notification settings - Fork 6
Build and Publish
Build, publish, and release reference for PadForge.
PadForge.sln
├── PadForge.Engine/ Class library (net10.0-windows)
│ ├── Common/ SDL3 P/Invoke, input types, device wrappers
│ ├── Data/ PadSetting, UserDevice, UserSetting
│ └── Properties/ AssemblyInfo.cs
│
├── PadForge.App/ WPF application (net10.0-windows10.0.26100.0)
│ ├── Common/ SettingsManager, DriverInstaller, InputManager pipeline
│ ├── Views/ XAML pages and code-behind
│ ├── ViewModels/ MVVM ViewModels
│ ├── Services/ InputService, SettingsService, DeviceService, etc.
│ ├── WebAssets/ HTML/CSS/JS for browser virtual controller
│ ├── Models3D/ 3D model classes + OBJ meshes (3DModels/)
│ ├── Models2D/ 2D overlay layout classes
│ ├── 2DModels/ PNG overlay images (DS4/, XBOX360/)
│ ├── Converter/ WPF value converters
│ ├── Controls/ Custom controls (RangeSlider)
│ ├── Resources/ Icons, SDL3 DLL, embedded driver installers, localization
│ └── Properties/ AssemblyInfo.cs
│
├── nuget-local/ Local NuGet source (MIDI Services SDK)
├── nuget.config Registers nuget.org + nuget-local/ as package sources
│
└── tools/
├── DsuDiag/ DSU/Cemuhook diagnostic client
├── Ds4InputDump/ DS4 raw HID input dump (Sony Report 0x01 passthrough debug)
├── vJoy/ Legacy v2 vJoy SDK / test utilities (kept for reference, unused by v3)
├── deploy.ps1 Build + deploy to C:\PadForge
├── deploy_and_restart.ps1 Deploy + restart running instance
├── capture_screenshots.ps1 Automated wiki screenshot capture
└── (~50 ad-hoc diagnostic / UI-automation scripts, mostly v2 vJoy holdovers)
| Requirement | Version | Notes |
|---|---|---|
| .NET SDK | 10.0+ |
net10.0-windows10.0.26100.0 target framework |
| Windows SDK | 10.0.26100.57 | Set via WindowsSdkPackageVersion in App csproj |
| Windows | 10/11 x64 | WPF + Windows-specific P/Invoke |
| Visual Studio (optional) | 2022+ | Not required; dotnet CLI suffices |
Minimum supported OS: Windows 10 1809 (build 17763), set via SupportedOSPlatformVersion in the App csproj.
All native DLLs, driver installers, and model assets are included in the repository. No external downloads needed.
dotnet build -c DebugOutput: PadForge.App/bin/Debug/net10.0-windows10.0.26100.0/win-x64/
Suitable for development and debugging. Not deployable. Produces loose DLLs and requires the .NET runtime.
dotnet build -c ReleaseOutput: PadForge.App/bin/Release/net10.0-windows10.0.26100.0/win-x64/
Not deployable. Use dotnet publish for deployment.
dotnet publish -c Release
# or explicitly:
dotnet publish PadForge.App/PadForge.App.csproj -c ReleaseOutput: PadForge.App/bin/Release/net10.0-windows10.0.26100.0/win-x64/publish/
| File | Description |
|---|---|
PadForge.exe |
~102 MB, single-file self-contained |
SDL3.dll |
SDL3 native library (content item) |
libusb-1.0.dll |
WinUSB for Switch 2 Pro Controller (content item) |
gamecontrollerdb_padforge.txt |
Custom SDL gamepad mappings (content item) |
Critical: Always use
dotnet publishfor deployment, neverdotnet build. The project is configured for single-file self-contained publish.dotnet buildproduces non-functional multi-file output.
The App csproj defines these publish properties:
<!-- Single-file portable publish: dotnet publish -c Release -->
<PropertyGroup>
<RuntimeIdentifier>win-x64</RuntimeIdentifier>
<PublishSingleFile>true</PublishSingleFile>
<SelfContained>true</SelfContained>
<IncludeNativeLibrariesForSelfExtract>true</IncludeNativeLibrariesForSelfExtract>
<EnableCompressionInSingleFile>true</EnableCompressionInSingleFile>
<DebugType>embedded</DebugType>
</PropertyGroup>| Property | Value | Effect |
|---|---|---|
RuntimeIdentifier |
win-x64 |
Targets 64-bit Windows only |
PublishSingleFile |
true |
Bundles all managed assemblies into one exe |
SelfContained |
true |
Embeds .NET 10 runtime (no install needed on target) |
IncludeNativeLibrariesForSelfExtract |
true |
Packs native DLLs into the exe; extracted to temp dir at runtime |
EnableCompressionInSingleFile |
true |
Compresses the bundle to reduce exe size |
DebugType |
embedded |
Embeds debug symbols in assemblies (no .pdb files); preserves stack traces for crash diagnostics |
Note: RuntimeIdentifier is set unconditionally (not only during publish), so dotnet build also targets win-x64 and produces a RID-specific output folder.
<PropertyGroup>
<OutputType>WinExe</OutputType>
<TargetFramework>net10.0-windows10.0.26100.0</TargetFramework>
<SupportedOSPlatformVersion>10.0.17763.0</SupportedOSPlatformVersion>
<WindowsSdkPackageVersion>10.0.26100.57</WindowsSdkPackageVersion>
<RootNamespace>PadForge</RootNamespace>
<AssemblyName>PadForge</AssemblyName>
<UseWPF>true</UseWPF>
<UseWindowsForms>true</UseWindowsForms> <!-- For NotifyIcon system tray -->
<LangVersion>latest</LangVersion>
<Nullable>disable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
<GenerateAssemblyInfo>false</GenerateAssemblyInfo> <!-- Manual AssemblyInfo.cs -->
</PropertyGroup>Key settings:
| Property | Value | Purpose |
|---|---|---|
TargetFramework |
net10.0-windows10.0.26100.0 |
.NET 10 + Windows SDK 26100 (Win 11 24H2). Enables WinRT APIs. |
SupportedOSPlatformVersion |
10.0.17763.0 |
Minimum OS: Windows 10 1809 |
WindowsSdkPackageVersion |
10.0.26100.57 |
Pins Windows SDK NuGet package version |
UseWindowsForms |
true |
NotifyIcon system tray only (not WinForms UI) |
GenerateAssemblyInfo |
false |
Uses manual Properties/AssemblyInfo.cs
|
Nullable |
disable |
Nullable reference types not used |
WinForms implicit using removal. Prevents System.Drawing / System.Windows.Forms ambiguity with WPF types:
<ItemGroup>
<Using Remove="System.Drawing" />
<Using Remove="System.Windows.Forms" />
</ItemGroup><PropertyGroup>
<TargetFramework>net10.0-windows</TargetFramework>
<RootNamespace>PadForge.Engine</RootNamespace>
<AssemblyName>PadForge.Engine</AssemblyName>
<LangVersion>latest</LangVersion>
<Nullable>disable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
<DebugType>embedded</DebugType>
</PropertyGroup>The Engine targets net10.0-windows (no specific SDK version needed). Zero NuGet dependencies. All SDL3 and system interop uses raw [DllImport] P/Invoke.
Versions are set manually in Properties/AssemblyInfo.cs (both projects set GenerateAssemblyInfo to false).
| Project | File | Current Version |
|---|---|---|
| PadForge.App | PadForge.App/Properties/AssemblyInfo.cs |
2.1.0.0 |
| PadForge.Engine | PadForge.Engine/Properties/AssemblyInfo.cs |
2.0.0.0 |
App and Engine versions are independent. App tracks user-facing releases, Engine tracks breaking API changes. Both AssemblyInfo files also include metadata attributes (AssemblyTitle, AssemblyCompany, AssemblyCopyright, Guid, etc.).
Important: Do not bump versions unless preparing a release. GitHub Releases use git tag names (e.g., v2.0.0-beta3) as the version identifier, not AssemblyVersion.
| Package | Version | Source | Purpose |
|---|---|---|---|
| ModernWpfUI | 0.9.6 | nuget.org | Fluent Design theme for WPF (dark mode, NavigationView, controls) |
| HelixToolkit.Core.Wpf | 2.27.3 | nuget.org | 3D viewport rendering (OBJ model loading, camera, lighting) |
| CommunityToolkit.Mvvm | 8.2.2 | nuget.org | MVVM: ObservableObject, RelayCommand, [ObservableProperty]
|
| Nefarius.HIDMaestro | 1.21.256 | nuget.org | HIDMaestro virtual Xbox and PlayStation controller client API |
| NAudio.Wasapi | 2.2.1 | nuget.org | WASAPI loopback audio capture for bass-driven rumble detection |
| Microsoft.Windows.Devices.Midi2 | 1.0.16-rc.3.7 | nuget-local/ | Windows MIDI Services SDK for virtual MIDI device creation |
No NuGet dependencies. All SDL3 and system interop is via raw P/Invoke.
The Windows MIDI Services SDK is not on nuget.org. The .nupkg is stored locally:
nuget-local/Microsoft.Windows.Devices.Midi2.1.0.16-rc.3.7.nupkg
nuget.config at the solution root registers this folder as a package source:
<configuration>
<packageSources>
<add key="nuget.org" value="https://api.nuget.org/v3/index.json" />
<add key="local" value="nuget-local" />
</packageSources>
</configuration>Both nuget.config and nuget-local/ must be present alongside PadForge.sln for dotnet restore to succeed.
Copied alongside PadForge.exe in the output via Content + CopyToOutputDirectory. Not embedded inside the single-file exe.
<Content Include="Resources\SDL3\x64\SDL3.dll" Link="SDL3.dll"
Condition="Exists('Resources\SDL3\x64\SDL3.dll')">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="Resources\SDL3\x64\libusb-1.0.dll" Link="libusb-1.0.dll"
Condition="Exists('Resources\SDL3\x64\libusb-1.0.dll')">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>-
Link="SDL3.dll". Flattens to output root (next toPadForge.exe) instead of preserving the subdirectory path. -
Condition="Exists(...)". Build succeeds even if the DLL is absent (e.g., fresh clone). -
SDL3.dll is a custom fork built from
SDL3-build/SDL/with WinUSB support for Switch 2 Pro Controller. See SDL3 Integration for build instructions. - libusb-1.0.dll provides WinUSB access for Switch 2 Pro Controller communication.
Source location: PadForge.App/Resources/SDL3/x64/
<Content Include="gamecontrollerdb_padforge.txt">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>Custom SDL gamepad mapping database with PadForge-specific entries (e.g., DualShock 3 via DsHidMini SDF). Loaded by SDL3 at runtime. Community mappings can be submitted via the GitHub issue template.
Source location: PadForge.App/gamecontrollerdb_padforge.txt
Compiled into the assembly. Not visible as separate files in the output.
<EmbeddedResource Include="Resources\HidHide_1.5.230_x64.exe" />| Resource | Purpose |
|---|---|
HidHide_1.5.230_x64.exe |
HidHide installer. Hides physical controllers from other applications |
The HIDMaestro user-mode driver is installed by a separate bundled installer that DriverInstaller runs on first launch. The driver is not embedded in the EXE. The OpenXInput shim (xinput1_4.dll only) ships as <Content> and is bundled into the single-file EXE via IncludeNativeLibrariesForSelfExtract. devobj.dll is deliberately not shipped — see Driver Installation Internals for why a bundled stub would crash HID class enumeration. The Windows MIDI Services SDK is installed on demand from the Microsoft Store or installer when the user requests MIDI VC support.
PadForge v2's vJoy and ViGEmBus installers are no longer bundled. v3 detects either driver on first launch and offers to uninstall via the legacy driver cleanup dialog. See Driver Installation Internals.
<!-- 3D controller model assets (adapted from Handheld Companion, CC BY-NC-SA 4.0) -->
<EmbeddedResource Include="3DModels\**\*.obj" />OBJ mesh files for 3D controller visualization. Loaded at runtime via ControllerModelBase.LoadModel().
| Directory | Contents |
|---|---|
3DModels/XBOX360/ |
31 OBJ files (Xbox 360 controller parts) |
3DModels/DS4/ |
36 OBJ files (DualShock 4 controller parts) |
<!-- Web controller frontend assets (embedded, served by WebControllerServer) -->
<EmbeddedResource Include="WebAssets\**\*" />HTML/CSS/JS for the browser-based virtual controller. Served by the embedded Kestrel web server at runtime.
| File | Purpose |
|---|---|
WebAssets/index.html |
Landing page |
WebAssets/controller.html |
Virtual gamepad UI |
WebAssets/css/controller.css |
Gamepad styling |
WebAssets/js/controller_client.js |
WebSocket client logic |
WebAssets/js/nipplejs.min.js |
Virtual joystick library |
<EmbeddedResource Update="Resources\Strings\Strings.resx">
<Generator>PublicResXFileCodeGenerator</Generator>
<LastGenOutput>Strings.Designer.cs</LastGenOutput>
</EmbeddedResource>Resource files for multilingual UI support:
| File | Language |
|---|---|
Strings.resx |
English (default/fallback) |
Strings.de.resx |
German |
Strings.es.resx |
Spanish |
Strings.fr.resx |
French |
Strings.it.resx |
Italian |
Strings.ja.resx |
Japanese |
Strings.ko.resx |
Korean |
Strings.nl.resx |
Dutch |
Strings.pt-BR.resx |
Brazilian Portuguese |
Strings.zh-Hans.resx |
Simplified Chinese |
The default Strings.resx uses PublicResXFileCodeGenerator to produce Strings.Designer.cs for strongly-typed access via Strings.PropertyName. Satellite .resx files follow standard .NET localization conventions and compile into satellite assemblies automatically.
<!-- 2D controller model assets (Gamepad-Asset-Pack by AL2009man, MIT license) -->
<Resource Include="2DModels\**\*.png" />PNG overlay images for 2D controller schematic view (DS4/ and XBOX360/ parts). Uses Resource (not EmbeddedResource) so they load as WPF pack URIs (pack://application:,,,/2DModels/...).
<Resource Include="Resources\Xbox Series Controller - Front.png" />
<Resource Include="Resources\Xbox Series Controller - Top.png" />
<Content Include="Resources\PadForge.ico" />| Resource | Purpose |
|---|---|
| Controller PNGs | Mapping UI images |
PadForge.ico |
Application icon (via <ApplicationIcon> in csproj) |
Resources/ControllerIcons.xaml |
XAML vector icon definitions (resource dictionary) |
Workflow: .github/workflows/build.yml
on:
push:
branches: [v2-dev]
pull_request:
branches: [v2-dev]
workflow_dispatch:Runs on every push/PR to v2-dev and on manual trigger.
-
Checkout.
actions/checkout@v4,fetch-depth: 0(full history for commit counting) -
Setup .NET.
actions/setup-dotnet@v4,dotnet-version: 10.x -
Publish.
dotnet publish PadForge.App/PadForge.App.csproj -c Release -
Upload artifact. Publish directory as
PadForge_r{COMMIT_COUNT}@{COMMIT_SHORT}
On push (not PR), the workflow creates two GitHub releases:
| Release | Behavior |
|---|---|
archive-v2-dev |
Accumulates every build as PadForge_r{N}@{SHA}.zip. Uses --clobber for latest upload; preserves tag across builds. |
latest-v2-dev |
Recreated on every push (old release deleted first). Contains PadForge.zip with the most recent build. The "always current" download link. |
Both are marked --prerelease and cross-link to each other in their notes.
Format: PadForge_r{COMMIT_COUNT}@{7-char COMMIT_SHA} (e.g., PadForge_r342@f35bb36)
env:
DOTNET_NOLOGO: true
DOTNET_CLI_TELEMETRY_OPTOUT: trueCreated manually via gh release create with a version tag (e.g., v2.0.0, v2.1.0-beta1). See Release Workflow below.
PadForge is portable. No installer required.
Copy the publish output to any folder:
cp PadForge.App/bin/Release/net10.0-windows10.0.26100.0/win-x64/publish/PadForge.exe C:\PadForge\PadForge.exeRequired companion files (placed automatically by dotnet publish):
| File | Required | Purpose |
|---|---|---|
SDL3.dll |
Yes | SDL3 native library |
libusb-1.0.dll |
Yes | WinUSB for Switch 2 Pro Controller |
gamecontrollerdb_padforge.txt |
Optional | SDL3 gamepad mappings (enhances device recognition) |
tools/deploy.ps1 # Build, publish, copy to C:\PadForge
tools/deploy_and_restart.ps1 # Deploy + kill running instance + restartPadForge creates PadForge.xml alongside the executable to store settings, mappings, and profiles.
PadForge always requests administrator privileges on startup (declared in app.manifest as requireAdministrator). HIDMaestro / HidHide / MIDI Services management runs inside the already-elevated process.
-
PadForge.App/Properties/AssemblyInfo.cs.AssemblyVersion+AssemblyFileVersion -
PadForge.Engine/Properties/AssemblyInfo.cs. Only if Engine API changed
dotnet publish -c Releasecp PadForge.App/bin/Release/net10.0-windows10.0.26100.0/win-x64/publish/PadForge.exe C:\PadForge\PadForge.exeRun C:\PadForge\PadForge.exe and verify functionality.
git add -A
git commit -m "Release vX.Y.Z"
git pushcd PadForge.App/bin/Release/net10.0-windows10.0.26100.0/win-x64/publish
zip -r PadForge-vX.Y.Z-win-x64.zip .gh release create vX.Y.Z --title "PadForge vX.Y.Z" --notes "Release notes here"
gh release upload vX.Y.Z PadForge-vX.Y.Z-win-x64.zipUse --prerelease for beta/RC releases. Use --latest for the default download.
Standalone diagnostic utilities and development scripts.
| Tool | Command | Framework | Dependencies | Purpose |
|---|---|---|---|---|
| DsuDiag | cd tools/DsuDiag && dotnet run |
net8.0 | None | Real-time DSU/Cemuhook UDP client for verifying gyro/accel axis mapping |
| Ds4InputDump | cd tools/Ds4InputDump && dotnet run |
net8.0-windows | None (raw HID) | Dumps DS4 raw HID input frames. Used to validate Sony Report 0x01 passthrough on PlayStation virtual controllers |
The v2 vJoy/Test and vJoy/FfbTest utilities are still in the repo under tools/vJoy/ for historical reference. They target the deprecated vJoy stack and are not part of the v3 toolchain.
| Script | Purpose |
|---|---|
deploy.ps1 |
Build + publish + copy to C:\PadForge
|
deploy_and_restart.ps1 |
Deploy + kill running instance + restart |
capture_screenshots.ps1 |
Automated wiki screenshot capture |
capture_pads.ps1 |
Screenshot the Pads/mapping page |
capture_macros.ps1 |
Screenshot the Macros page |
capture_3d_2d.ps1 |
Screenshot 3D/2D controller views |
A handful of *vjoy* scripts (cleanup_vjoy.ps1, check_vjoy_state.ps1, etc.) are v2 holdovers kept for diagnosing legacy installs the cleanup dialog encounters. The remaining ~50 scripts are ad-hoc UI automation and screenshot utilities.
| Issue | Cause | Fix |
|---|---|---|
SDL3.dll not found at runtime |
DLL not in Resources\SDL3\x64\
|
Download from SDL3 releases or build from fork |
| MIDI package not found |
nuget.config missing or nuget-local/ folder missing |
Ensure both are present at solution root |
| Missing WPF types | Using dotnet build instead of dotnet publish for deployment |
Always use dotnet publish -c Release for deployment |
System.Drawing ambiguity |
WinForms implicit usings conflict with WPF | Ensure <Using Remove="System.Drawing" /> is in csproj |
| HelixToolkit errors | Package restore failed | Run dotnet restore first |
| Large exe size (~102 MB) | Expected. Self-contained with bundled .NET runtime |
EnableCompressionInSingleFile already enabled |
| CI build fails | .NET 10 SDK not available in runner | Check actions/setup-dotnet version supports .NET 10 preview |
HIDMaestro.Core reference fails to resolve |
Resources/HIDMaestro/HIDMaestro.Core.dll missing or wrong build |
Drop a Release-build HIDMaestro.Core.dll from a tagged HIDMaestro release into Resources/HIDMaestro/
|
- Architecture Overview: Solution structure, dependencies, design philosophy
- Engine Library: Shared data types, SDL3 P/Invoke, device wrappers
- Settings and Serialization: XML persistence, data models
- SDL3 Integration: Custom SDL3 fork, build instructions
- Driver Installation Internals: Driver lifecycle (HIDMaestro, HidHide, MIDI Services) and legacy v2 cleanup
- HIDMaestro Deep Dive: HM SDK surface, OpenXInput shim, three-surface filtering architecture
-
DSU Protocol Implementation:
DsuDiagdiagnostic tool intools/