Pre-built release
Use this path if you downloaded a packaged NEShim release and want to configure it for your game without recompiling.
1. Rename the executable
If you want your game to appear as MyGame.exe rather than NEShim.exe, rename only the exe:
NEShim.exe → MyGame.exe
The app host has the assembly name (NEShim) baked into it as a binary string at publish time. It always looks for NEShim.dll, NEShim.runtimeconfig.json, and NEShim.deps.json by that fixed name regardless of what the exe file is called. Renaming those files will prevent the app from launching.
If you want the underlying assembly name to change as well (so that NEShim.dll itself is renamed), that requires a source build with <AssemblyName> changed in the csproj — see Building from source.
The exe icon in a pre-built release is fixed at whatever was embedded when the binary was built. If you need a custom exe icon, use Building from source instead. The taskbar and window icon at runtime are set correctly by the binary regardless.
The .pdb files are debug symbols and can be omitted from distribution builds entirely.
2. Set the window title
In config.json, set windowTitle to your game’s name:
{
"windowTitle": "My Game Title"
}
3. Configure Steam App ID
- Register your game in the Steamworks partner dashboard and obtain your App ID.
- Replace the contents of
steam_appid.txt(in the output directory, next to the exe) with your App ID — a plain integer, no trailing newline:
1234560
4. steam_api64.dll
steam_api64.dll is not included in the NEShim release package. Before uploading to Steam, copy it from the Steamworks.NET 2025.163.0 release zip into the output directory alongside the exe. Use the copy bundled with the wrapper — it is matched to the wrapper version. The current build targets Steamworks.NET 2025.163.0.
Include it in your Steam depot when uploading; Valve does not inject it automatically. Once it is in your depot, Steam distributes it to players as part of the normal game install.
If you ever need to upgrade to a newer Steamworks.NET version, use the copy bundled inside the Steamworks.NET release zip — it is pre-matched to the wrapper version.
5. Configure Steam Auto-Cloud
NEShim reads and writes save files to the local filesystem only. Cloud sync is handled entirely by Steam Auto-Cloud configured in the Steamworks partner dashboard — no code changes are required.
Files to sync
| Path pattern | Contents |
|---|---|
saves\* | Manual save states (slot0.state … slot7.state), slot metadata (.meta), and the auto-save (autosave.state) |
game.srm | Battery-backed RAM — the cartridge save for games like Zelda and Metroid |
Do not sync config.json. Settings like windowMode and volume are machine-specific; syncing them will overwrite a player’s preferences on every machine they use.
Steamworks dashboard setup
- Navigate to your app and open Cloud → Cloud Settings.
- Set the Quota to at least 10 MB (NES states are typically 10–50 KB each).
- Under Root Overrides, add two entries with root
GameInstall: one forsaves\*and one forgame.srm. - Publish the cloud configuration.
Limitations
- Conflict resolution is opaque. Steam uses last-write-wins. There is no in-game conflict UI.
- The auto-save is not crash-safe. It is written only on graceful exit. See Auto-save in the configuration reference.
- Manual slot saves are immediately safe. Each manual save writes synchronously; Steam picks it up on the next sync.
6. Rename the Steam Input VDF
In the output directory, rename game_actions_0.vdf to game_actions_<YourAppID>.vdf. The file contents do not change — only the filename.
7. Configure Steam Input
Upload the renamed VDF and the default controller bindings to the Steamworks partner dashboard.
Upload the action definition file
- Open the Steamworks partner dashboard for your app.
- Go to Steam Input → Default Configuration.
- Upload
game_actions_<YourAppID>.vdfas the Game Actions file.
The VDF defines two action sets — Gameplay and Menu — that NEShim switches between automatically. Optionally customise the localization block with your game’s terminology.
Upload default controller bindings
The controller_bindings/ directory contains a pre-built default configuration for each supported controller type. Upload each file in the Steamworks dashboard as the Default Configuration for its controller type:
| File | Controller type |
|---|---|
xbox360.vdf | Xbox 360 |
xboxone.vdf | Xbox One / Xbox Series X|S / Xbox One Elite |
neptune.vdf | Steam Deck |
ps4.vdf | PlayStation 4 DualShock 4 |
ps5.vdf | PlayStation 5 DualSense |
switch_pro.vdf | Nintendo Switch Pro Controller |
steam_controller.vdf | Valve Steam Controller |
Without these defaults, players must configure their controller bindings manually from the Steam overlay. With them, supported controllers work immediately at first launch.
8. Set up achievements in Steamworks
Before achievements can fire in-game, they must be registered in the Steamworks partner dashboard:
- Navigate to Achievements for your app.
- Create each achievement with an API Name (e.g.
ACH_FIRST_WIN). This name is thesteamIdfield inachievements.json. - Add a name, description, and icon for each achievement.
- Publish the achievements from the dashboard.
9. Author and seal achievements.json
- Create
achievements.jsonin the output directory (alongside the exe). - Compute your ROM’s SHA1 hash (see Finding the ROM SHA1 hash).
- Author the achievement definitions. See Achievement system for the full field reference.
Example:
{
"A1B2C3D4E5F60718293A4B5C6D7E8F90A1B2C3D4": {
"memoryDomain": "System Bus",
"achievements": [
{
"steamId": "ACH_FIRST_WIN",
"address": 255,
"bytes": 1,
"encoding": "binary",
"comparison": "equals",
"value": 1
}
]
}
}
- Seal the file:
seal-achievements achievements.json
Verify all definitions are listed as [sealed] in the output. Never edit achievements.json after sealing without re-sealing — any changed definition will fail signature verification and silently stop firing.
Note: A pre-built release ships with a publicly known HMAC key. Sealed achievements will verify correctly, but anyone with the key can forge signatures. If this matters for your project, use Building from source and rotate the key.
10. Prepare artwork and audio assets
All artwork paths in config.json are relative to the executable directory.
| Config field | Purpose | Notes |
|---|---|---|
mainMenuBackgroundPath | Full-screen background on the pre-game menu | Any common image format. Stretched/filled to the window size. |
sidebarLeftPath | Image in the left letterbox bar during gameplay | Drawn at 1:1 pixel resolution, centered, cropped to bar width. |
sidebarRightPath | Image in the right letterbox bar during gameplay | Same rules as left sidebar. |
mainMenuMusicPath | Looping audio for the pre-game menu | MP3 or WAV recommended. Plays with fade-in/fade-out transitions. |
11. Verify audio settings
| Setting | Recommendation |
|---|---|
volume | Set a comfortable default (e.g. 80) so the game doesn’t start at maximum volume. |
soundScrubberEnabled | Test both settings. On high-quality speakers the scrubber mode (true) is warmer. On laptop or TV speakers the default NES filter (false) may be fine. |
12. Test
Before uploading to Steam:
- Copy the entire output directory to a machine without .NET installed to verify the self-contained runtime works.
- Launch through Steam (not directly from Explorer) to verify:
- Steam overlay appears when Shift+Tab is pressed.
- Gamepad input works via Steam Input if configured.
- Achievements fire when conditions are met.
- The game icon appears correctly in the Steam library.
- Verify the auto-save and save state slots work (save, quit, reload).
- Verify battery RAM persistence if the game uses it.
Release checklist
NEShim.exerenamed toMyGame.exe(only the exe; all otherNEShim.*files stay as-is)windowTitleset inconfig.jsonsteam_appid.txtupdated with your production App IDsteam_api64.dllcopied from Steamworks.NET release zip into the output directory and included in your Steam depot- Steam Auto-Cloud configured in the Steamworks dashboard (
saves\*andgame.srmunderGameInstallroot;config.jsonexcluded) game_actions_0.vdfrenamed togame_actions_<appid>.vdf- Renamed VDF uploaded to Steamworks dashboard under Steam Input → Default Configuration
- Each
controller_bindings/*.vdfuploaded as Default Configuration for its controller type - All achievements created in the Steamworks dashboard with matching API names
achievements.jsonauthored and sealed withseal-achievements- Artwork and music assets in place and referenced in
config.json - Audio defaults verified in
config.json - Release passes local smoke test (saves, Steam overlay, achievements)
Deployed file layout
MyGame/
├── MyGame.exe ← renamed from NEShim.exe (only the exe can be renamed)
├── NEShim.dll ← must keep this name; baked into the app host
├── NEShim.deps.json ← must keep this name
├── NEShim.runtimeconfig.json ← must keep this name
├── NEShim.AchievementSigning.dll
├── BizHawk.dll
├── steam_api64.dll ← from Steamworks.NET release zip; must be included in your depot
├── steam_appid.txt
├── game_actions_1234560.vdf
├── controller_bindings/
│ ├── xbox360.vdf
│ ├── xboxone.vdf
│ ├── neptune.vdf
│ ├── ps4.vdf
│ ├── ps5.vdf
│ ├── switch_pro.vdf
│ └── steam_controller.vdf
├── config.json
├── achievements.json
├── game.nes
├── saves/ ← created automatically on first save
├── game.srm ← created automatically if game uses battery RAM
├── art/
│ ├── menu_bg.png
│ ├── sidebar_left.png
│ └── sidebar_right.png
├── audio/
│ └── menu_theme.mp3
└── [.NET runtime files...]