-
Notifications
You must be signed in to change notification settings - Fork 1
Architecture
WPR runs a Windows Phone game's original assemblies, IL-rewritten at install time to redirect WP / Silverlight / XNA API calls to in-tree shims. This page covers the pipeline, the shim project layout, and the reinstall-vs-rebuild rule that trips up most contributors at least once.
.xap / XNA folder
│
▼ LibraryScanner discovers packages
▼ ApplicationInstaller unpacks to %LocalAppData%\WPR\Apps\<ProductId>
▼ ApplicationPatcher Cecil-rewrites every .dll; keeps .dll.original
▼ XnaAchievementSeeder populates SQLite achievements DB
│
▼ (user clicks Run)
▼ XnaLauncher → ApplicationLaunch.Start XNA via FNA
▼ SilverlightLauncher.LaunchAsync Silverlight XAPs
The pipeline runs once per .xap at install. Steps 1–4 happen the first
time you click Add; thereafter the user assembly on disk has already been
rewritten and a <game>.dll.original sibling sits next to it.
%LocalAppData%\WPR\
├── Apps\
│ └── <ProductId>\
│ ├── <Game>.dll ← Cecil-rewritten user assembly
│ ├── <Game>.dll.original ← untouched copy of the original
│ ├── AppManifest.xaml
│ ├── WMAppManifest.xml
│ ├── Content\…
│ └── wpr_game_debug.log ← per-game debug trace (DEBUG builds)
└── Database\
├── achievements.db ← shared SQLite DB across all installs
└── applications.db
wpr_game_debug.log is the first place to look when a game silently
crashes or shows a black screen — see Troubleshooting.
ApplicationPatcher.PatchDll walks every *.dll in the per-game install
directory with Mono.Cecil and rewrites two kinds of references:
| Table | What it redirects |
|---|---|
Patches |
Type references — e.g. System.Windows.Controls.Image → WPR.SilverlightCompability.Image, Microsoft.Xna.Framework.GamerServices.SignedInGamer → WPR.Core...SignedInGamer. |
MemberPatches |
Specific method/property references that need rerouting independent of their declaring type. |
The original DLL is preserved as <name>.dll.original so the patcher can be
re-run without having lost the source.
| Change | Action needed |
|---|---|
| Shim implementation change (any .cs under WPR.*Compability, Microsoft.*, System.*, FNA, GamerServices, etc.) |
Rebuild only. Installed games reference the shim assembly, not a snapshot of it — next launch picks up the new behaviour. |
| Patcher table change (new entries in ApplicationPatcher.cs's Patches / MemberPatches) |
Rebuild and reinstall the affected games. The IL was rewritten at install time; new redirects don't apply retroactively to already-rewritten .dlls. |
Common gotcha: "I added a shim type but the game still NREs." Did you also
add a Patches entry? If yes — did you reinstall the game?
Src/:
| Project | Role |
|---|---|
Core/WPR |
Install / launch / patch pipeline, EF Core models, DB |
Core/WPR.Common |
Logging, paths, configuration |
Core/WPR.SilverlightCompability |
Silverlight 4 / WP XAML re-impl on Avalonia |
Core/WPR.WindowsCompability |
System.Windows.* shims (Application, BitmapImage, IsolatedStorage, …) |
Core/WPR.StandardCompability |
System.ServiceModel / WCF-lite shims |
Core/WPR.XnaCompabilityPatch |
XNA-side shims layered on top of FNA |
Core/Microsoft.Phone |
Microsoft.Phone.* (Shell, Tasks, Marketplace, Scheduler, …) |
Core/Microsoft.Xna.Framework.GamerServices |
Gamer profile, achievements, leaderboards |
Core/Microsoft.Device.Sensors |
Accelerometer / Compass |
Core/System.Device |
System.Device.Location |
UI/WPR.UI |
Shared Avalonia UI (views, view-models, launchers) |
UI/WPR.UI.Desktop |
Windows entry point (net8.0-windows10.0.17763.0) |
UI/WPR.UI.Android |
Android entry point |
ThirdParty/fna |
FNA — XNA reimplementation, lightly forked for diagnostics |
ThirdParty/Icons.Avalonia |
Vendored Projektanker icons, patched for Avalonia 11.3.9 |
This project mirrors the upstream Silverlight / Windows Phone namespace hierarchy as directories — one C# class per file, file path matches where the type lives upstream:
| Upstream type | File path |
|---|---|
System.Windows.Shapes.Rectangle |
System/Windows/Shapes/Rectangle.cs |
System.Windows.Controls.Primitives.Popup |
System/Windows/Controls/Primitives/Popup.cs |
System.Windows.Media.Animation.Storyboard |
System/Windows/Media/Animation/Storyboard.cs |
Microsoft.Phone.Shell.PhoneApplicationService |
Microsoft/Phone/Shell/PhoneApplicationService.cs |
The C# namespace declaration in every file stays
WPR.SilverlightCompability — the directory structure is pure organisation,
the assembly is one flat DLL. The patcher target paths (NewNamespace in
ApplicationPatcher.cs) refer to that flat namespace.
Keep the doc comment /// Shim for <c>System.X.Y.TypeName</c>. on each
shim file — it's the canonical record of which upstream type the file
shadows, and tooling can grep for it.
Files at the project root are not type shims — they're WPR-internal runtime / helper code (renderers, the pointer-to-gesture bridge, XAML helpers, hosting glue, theme constants).
WPR.WindowsCompability and WPR.XnaCompability are still flat — the
mirror-tree convention has only been applied to WPR.SilverlightCompability
so far.
ApplicationType.cs recognises three .xap flavours:
| Type | Status | Notes |
|---|---|---|
XNA |
Working | Main path; runs on FNA via WPR.XnaCompability. |
Silverlight |
Experimental | Boots a small set of XAPs through the in-tree WPR.SilverlightCompability Avalonia re-impl. |
ModernNative |
Not supported | C++/CX + WinRT apps ship as native PE binaries — out of scope. |
Two paths populate the per-game achievements DB at install time:
-
XnaAchievementCodeExtractorscans the install folder for known patterns:-
Source D — hardcoded catalogue keyed by ProductId, for games that
stash their achievement keys in an inline static array (e.g. PvZ's
Sexy.Achievements.ACHIEVEMENT_KEYS). -
Source A — XNA XML content catalogue (
Content/xml/socialnetworks.xml.xnb). -
Source B — IL
ldstrliterals nearAwardAchievement*callsites. -
Source C —
Content/Achievements/*.xnbfilenames.
First source that yields entries wins.
-
Source D — hardcoded catalogue keyed by ProductId, for games that
stash their achievement keys in an inline static array (e.g. PvZ's
-
TrueAchievements scrape (best-effort, used to backfill descriptions and icons when the extractor didn't recover them).
At runtime, SignedInGamer.BeginGetAchievements reads from the DB and
returns the AchievementCollection the game expects.
Two output channels:
| Channel | Path | When |
|---|---|---|
| Host log | UI console / standard Trace listener |
Always |
| Per-game debug log | %LocalAppData%\WPR\Apps\<ProductId>\wpr_game_debug.log |
DEBUG builds only |
The per-game log is gated by the WprDebugTrace wrapper — Release builds
elide the trace formatting and file listener entirely (no log spam, no
per-frame cost). All [wpr-trace] / [wpr-ex] / [wpr-heartbeat] /
[wpr-content] lines flow through it.
-
CLAUDE.mdin the main repo — the canonical project conventions, build-flag pitfalls, and rules-of-the-road that this page summarises. - Building WPR — how to compile.
- Contributing — coding guidelines, PR workflow.