Skip to content

Update History

Bubbleshum edited this page Jun 4, 2026 · 17 revisions

Update History

Running changelog of WPR fixes / features / compatibility changes. Most recent entries are at the top.

04/06/2026

  • Compat: hardcoded achievement catalogues rolled out to 14 games — Zombies!!!, Plants vs Zombies, 3D Brick Breaker Revolution, Brain Challenge, Bug Village, Fruit Ninja, Fruit Ninja (2013), Max and the Magic Marker, MonstaFish, Need For Speed Undercover, Civilization Revolution, Tentacles, Tiki Towers, Tower Bloxx New York. Each has an achievements.json manifest + per-achievement icons committed under Src/Database/Achievements/<productId>/ and mirrored into the Android assets tree.
  • Compat: Fruit Ninja — in-game new achievement earns now record correctly. Achievements the game already marked earned in its own save aren't re-awarded (the game skips them), so they don't appear retroactively in WPR. That's game-save behaviour, not a WPR bug.
  • Feat: achievement catalogues are now the sole source of achievement data. XnaAchievementSeeder reconciles non-destructively at install and at app startup (for installed games): inserts new rows, updates changed name/description/score/icon, removes rows whose key is no longer in the catalogue, and never resets earned state. The runtime TrueAchievements scraper path was removed and the old IL/XML XnaAchievementCodeExtractor was deleted.
  • Fix: any XNA game that set a not-yet-present gamer property/stat during play crashed with a bogus "Please check your memory usage" dialog and then exited (seen on Fruit Ninja 2013 on an achievement unlock). Root cause: Microsoft.Xna.Framework.GamerServices.PropertyDictionary.GetProperty ignored its demandCreate flag and returned null, so the first write of a fresh property NRE'd in SetValue/SetTypedValue; the game caught it and surfaced the memory dialog. Fix: honour demandCreate by allocating a new ObjectPropertyValue when the key is absent. Shim-only — no reinstall
  • Fix: in-game gamerpic rendered blank (and crashed games that draw it). GamerProfile.GetGamerPicture returned Stream.Null when no avatar was configured, so Texture2D.FromStream produced a null texture. Fix: fall back to a bundled default avatar. Shim-only — no reinstall
  • Fix: the Windows achievement-unlock toast briefly stole window focus → SDL FOCUS_LOST/FOCUS_GAINED → FNA Game.IsActive flipped → OnActivated/OnDeactivated fired mid-tick, which some WP7 ports throw inside. Fix: new WprActivationGuard makes the SDL focus branches ignore the toast-induced focus blip so the toast no longer disrupts the game. Shim-only — no reinstall
  • Fix: the WPR Achievements page (trophy nav) was loaded once and cached, so a game catalogued/installed after the page was first viewed didn't appear until restart. Fix: the page now reloads each time it's shown. Shim-only — no reinstall

02/06/2026

  • Compat: Zombies!!! (Babaroga) added as Playable. Boots and plays end-to-end; its in-game achievement screen now populates instead of rendering empty. Root cause of the empty list: the game builds its own achievement UI from our SignedInGamer.BeginGetAchievements shim and calls Texture2D.FromStream(device, Achievement.GetPicture()) per row — with no seeded icon, GetPicture() threw and the build loop aborted right after its u.Clear(), leaving the list empty. Fixed by shipping a hardcoded achievement catalogue for the game (17 entries + real 64×64 icons), keyed to the literals the game passes to XnaGame.AwardAchievement("…") so unlocks register.
  • Feature: achievements are now sourced solely from committed per-game catalogues under Src/Database/Achievements/<productId>/ (an achievements.json manifest + one PNG per achievement). They are autofilled / reconciled into the DB non-destructively at install and at startup — new rows inserted, changed metadata updated, earned state never reset. The old install-time IL/XML achievement extractor and the runtime TrueAchievements scraper were removed. Catalogues built so far: Zombies!!!, Plants vs Zombies, Brain Challenge, Bug Village, Fruit Ninja, Max and the Magic Marker, MonstaFish, Need For Speed Undercover, Civilization Revolution, Tentacles, Tiki Towers, Tower Bloxx.

24/05/2026

  • Compat: Mirror's Edge promoted from Broken → Playable after the GamerServicesDispatcher.WindowHandle fix below
  • Fix: Mirror's Edge booted to a flat grey screen (RGB 84,84,84) and never drew anything else — LoadContent was never invoked, so the game cleared to its background colour every frame and sat there. Root cause: Mirror's Edge's Game.Initialize() override sets GamerServicesDispatcher.WindowHandle, wrapped in a try/catch that only swallows GamerServicesNotAvailableException, NotSupportedException, and InvalidOperationException. Our shim's WindowHandle property threw NotImplementedException on both get and set, which escaped the game's narrow catch, propagated up into FNA's Game.DoInitialize, and was swallowed there — leaving the game in a half-initialised state where LoadContent and the rest of the boot sequence never ran. Fix: in Src/Core/Microsoft.Xna.Framework.GamerServices/GamerServicesDispatcher.cs, changed WindowHandle to a no-op auto-property (public static IntPtr WindowHandle { get; set; }). We have no Live UI tied to a host window handle anyway, so the stored value is never read back by our stack. Shim-only — no reinstall
  • Compat: Need For Speed Undercover promoted from Legacy → Partial (first-hand verification on this fork). Java-midp port wrapped in XNA (entry point midp.JavaLibGame in nf9_xna.dll). Engine hardcodes a landscape-right rotation for a phone held physically tilted: midp.Display.ctor unconditionally sets orientation = LANDSCAPE_RIGHT and backBuffer.isLandscape = true; midp.Graphics.lanscapeTransform is RotZ(π/2) × Translate(width, 0) keyed off PresentationParameters.BackBufferWidth; midp.Graphics.clipRect hardcodes scissor clamps at 480 × 800 portrait logical bounds. Symptom: on a desktop landscape window (no physical tilt) the engine renders portrait UI content rotated 90° CW, so text reads top-to-bottom and the scene appears rotated 90° from what a desktop user expects. Currently launches in a portrait 480 × 800 window with content rotated 90° (sideways-readable); playable in that state if the user tilts their head or rotates their monitor. Investigated four host-side workarounds (per-game rotation setting + RT trampoline; PreparingDeviceSettings swap to landscape PP dims; PP swap + scissor rotation transform; PP swap + scissor rotation + SpriteBatch transform override with letterbox fit) — each either failed to progress past the EA splash, hit the engine's hardcoded portrait scissor clamps, left engine-rotated text sideways, or misaligned non-UI draws (cars, background, EA splash) with the letterbox region. All reverted. Net: no clean desktop-landscape interpretation without per-game asset/engine patches; likely affects other Java-midp ports too. No net code changes landed

23/05/2026

  • Compat: Fable Coin Golf promoted to Playable — plays end-to-end and closes cleanly after the two fixes below
  • Fix: Fable Coin Golf aborted during initial content load with a ContentLoadException wrapping FileNotFoundException for Content/data/Brightwall_Wall_Rock_Snow__0.xnb. Root cause: the XAP ships without that chapter-2 texture, but the obstacle list still references it, and the reference is embedded as an external reference inside the BasicEffect XNB rather than being read via a direct OpenStream call. FNA's TitleContainer.OpenStream correctly rethrows FileNotFoundException (as required by Castlevania Puzzle's localized-asset try/catch fallback — see the 22/05 entry), so the missing asset surfaced as a fatal load failure rather than degrading. Fix: in Src/ThirdParty/fna/src/Content/ContentReader.cs, ReadExternalReference<T> now catches ContentLoadException whose inner cause is FileNotFoundException and returns default(T). Embedded external references inside other XNBs degrade gracefully; direct OpenStream callers (Castlevania Puzzle's localization fallback) still see the exception and can run their own catch. Shim-only — no reinstall
  • Fix: Fable Coin Golf crashed on window close with a fatal AccessViolationException raised from native FNA3D — uncatchable, took the host process down. Root cause: ResetWprSingletons in Src/Core/WPR/ApplicationLaunch.cs had a broad sweep that walked every type in every Default-ALC sibling DLL and called f.GetValue(null) on its static fields, which implicitly triggered the static constructor of any untouched type. Fable's CIwAttractor.cctor builds a VertexBuffer via the native FNA3D path; at shutdown the GraphicsDevice was already torn down, so the native call dereferenced freed device state and raised the AV. Fix: removed the broad sweep entirely. Sibling-DLL touches are now restricted to the hand-curated _PerLaunchFlagsToReset allow-list (currently just PressPlay.FFWD.Application.quitNextUpdate for Tentacles, which Fable doesn't use), so untouched types never get cctor-triggered at shutdown. Shim-only — no reinstall
  • Compat: Asteroids Deluxe promoted from Broken → Playable after the Type2.GetType fix below — clears the Krome.GameRoom.UI.Screens.InGameScreenData.set_Machine crash recorded in the 22/05 entry
  • Fix: Asteroids Deluxe (and any Krome-based title where a sibling library DLL calls Type.GetType("X, MainUserAssembly")) crashed during the initial screen-state machine transition with a FileLoadException wrapping NotSupportedException: A non-collectible assembly may not reference a collectible assembly. Root cause: Krome.dll (sibling library, loaded into the Default AssemblyLoadContext by design to satisfy the non-collectible-cannot-reference-collectible rule) called Type.GetType("AsteroidsDeluxe, ..."), which our patcher routes through WPR.WindowsCompability.Type2.GetType in Src/Core/WPR.WindowsCompability/Type2.cs. That shim previously delegated straight to the framework Type.GetType(string), whose binder resolves assemblies through the Default ALC — but AsteroidsDeluxe.dll lives in the per-launch collectible user ALC, so the CLR's cross-ALC collectibility check tripped and the bind failed. Fix: Type2.GetType now searches AssemblyLoadContext.All for an assembly whose simple name matches the type's assembly qualifier and resolves the type via Assembly.GetType directly, bypassing the framework binder entirely. The new path is a pure managed lookup with no cross-ALC collectibility validation, so the binding succeeds. Shim-only — no reinstall
  • Compat: Tentacles confirmed Playable end-to-end including clean relaunch after the fix below (first launch was already fine; second launch was silently quitting at frame 0)
  • Fix: Tentacles silently closed its XNA window immediately on the second launch in a WPR session — no error, no log line, just an instant quit after the first frame. First launch was fine, and the symptom only appeared if the user backed out via the main-menu Back button (which routes MainMenu.OnDoQuit → PressPlay.FFWD.Application.Quit) on launch one. Root cause: Tentacles is built on the PressPlay.FFWD mini-engine (PressPlay.FFWD.dll), which ships as a sibling DLL of the main Tentacles.dll. WPR's assembly resolver routes sibling DLLs into the non-collectible Default AssemblyLoadContext (to satisfy the "non-collectible assemblies may not reference collectible assemblies" rule), so FFWD's managed statics survive across launches. PressPlay.FFWD.Application.quitNextUpdate is a private static bool that Application.Quit() flips to true; the next frame Application.Update reads it and calls Game.Exit(). On launch one this works correctly. On launch two, FFWD was already loaded in Default ALC, the new Application instance read the still-true flag at frame 0 and immediately called Game.Exit(). Fix: in Src/Core/WPR/ApplicationLaunch.cs, track sibling DLLs that the Default-ALC resolver pulls from the user install folder (new _DefaultAlcUserSiblings list) and extend ResetWprSingletons to reset per-launch state on those siblings between launches with a deliberately narrow policy: clear all IList/IDictionary static fields in place (mutation, not SetValue, so it works through static readonly), and reset specific known per-launch flag fields by name via an allow-list (_PerLaunchFlagsToReset), currently PressPlay.FFWD.Application.quitNextUpdate. Adding fields to the allow-list requires manual confirmation that the field is a plain managed bool/int, not [ThreadStatic] or JIT-inlined — an earlier blanket SetValue on every writable value-type static crashed with a fatal 0x80131506 COR_E_EXECUTIONENGINE. Shim-only — no reinstall
  • Compat: Tiki Towers promoted from Legacy → Playable (first-hand verification on this fork)

22/05/2026

  • Compat: Storm in a Teacup tested on this fork — marked Partial. Boots and is playable end-to-end but exhibits major graphics issues during gameplay
  • Compat: Asteroids Deluxe marked Broken on this fork — crashes during the initial screen-state machine transition (Krome.GameRoom.UI.Screens.InGameScreenData.set_Machine) before gameplay starts, surfacing as FileLoadException wrapping NotSupportedException: A non-collectible assembly may not reference a collectible assembly. Root cause: InGameScreenData calls Type.GetType("AsteroidsDeluxe, ...") to reflect into its own user assembly, which is redirected through WPR.WindowsCompability.Type2.GetType at Src/Core/WPR.WindowsCompability/Type2.cs. Type2.GetType delegates straight to the framework Type.GetType(string), which resolves assemblies via the default AssemblyLoadContext — but ApplicationLaunch loads every user/game assembly into a per-launch collectible ALC (_CurrentUserAlc in Src/Core/WPR/ApplicationLaunch.cs) so launches can unload cleanly. The CLR refuses to bind a non-collectible static reference (WPR.WindowsCompability) to a collectible assembly (AsteroidsDeluxe.dll) and throws. Likely fix is shim-only: route Type2.GetType through the active user ALC — either via the Type.GetType(string, Func<AssemblyName, Assembly>, …) overload with a resolver that delegates to _CurrentUserAlc.LoadFromAssemblyName, or by splitting the type name and resolving the assembly explicitly. Not yet landed
  • Compat: Guitar Hero 5 Mobile promoted from Legacy → Playable (first-hand verification on this fork)
  • Compat: Final Fantasy 3 demoted from Legacy → Broken — first-hand verification on this fork. Square Enix's NDS → WP7 port ships its own in-process Nitro/G3X-to-XNA renderer inside syrcusW.dll, and the NDS background tile loaders (GXS_LoadBG0Char, GXS_LoadBG0Scr, GXS_LoadBGPltt, GXS_LoadOAM, GX_LoadTexPltt, GX_SetCapture, and ~25 siblings) ship as empty-bodied stubs. Game.Tick ticks normally, presents happen at 800x480 and no exceptions are thrown, but with the BG palette/char/screen upload path no-op'd the DS background tiles never become XNA textures — maps and menus render as a black backdrop with only the foreground sprite layer and font glyphs visible (~3-4 draw calls/frame). Not fixable shim-side: WPR's existing patcher entries and FNA shims are sufficient, the bug is inside the game's own port code. Would require binary-patching syrcusW.dll to translate NDS BG palette/char/screen memory layout into XNA texture data — multi-day RE job, not a shim addition
  • Compat: The Sims Medieval promoted from Legacy → Playable (first-hand verification on this fork)
  • Compat: Angry Birds promoted from Legacy Broken → Playable (first-hand verification on this fork)
  • Compat: Max and the Magic Marker promoted from Legacy → Playable (end-to-end after the three fixes below)
  • Compat: Pac-Man promoted from Legacy → Playable (first-hand verification on this fork)
  • Fix: Max and the Magic Marker crashed on window close with an AccessViolationException originating in FNA3D_AddDisposeTexture from ~Texture(). Root cause: the Texture finalizer ran after GraphicsDevice.Dispose had already destroyed the FNA3D device, so the native dispose call dereferenced freed device state. Fix: guard the native dispose call in Src/ThirdParty/fna/src/Graphics/Texture.cs on !GraphicsDevice.IsDisposed. Shim-only — no reinstall
  • Fix: Real touchscreen input never registered in Max and the Magic Marker (and any other XNA game polling SDL touch). Root cause: SDL2's touch device IDs are 64-bit (SDL_TouchID), but TouchPanel.LastActiveTouchId was declared int, so the value was truncated as it round-tripped through TouchPanel and the polling path called SDL_GetNumTouchFingers(bogusId) — which always returned 0. Widened the field to long in Src/ThirdParty/fna/src/Input/Touch/TouchPanel.cs and dropped the (int) casts in Src/ThirdParty/fna/src/FNAPlatform/SDL2_FNAPlatform.cs. Shim-only — no reinstall
  • Fix: Single-finger drawing in Max and the Magic Marker refused to start a stroke — one real finger was being reported as two simultaneous touches. Root cause: SDL synthesises mouse events from touch events by default (SDL_HINT_TOUCH_MOUSE_EVENTS = 1); we already filtered the synthetic events in our event pump, but the mouse-as-touch poll still read the polluted SDL_GetMouseState, writing a phantom finger into slot 7 alongside the real one. Max's drawing logic treats a 2-touch frame as a multi-finger gesture and refuses to start a stroke. Fix: set SDL_HINT_TOUCH_MOUSE_EVENTS = "0" in the Windows init block of Src/ThirdParty/fna/src/FNAPlatform/SDL2_FNAPlatform.cs so SDL stops synthesising mouse events from touch entirely. Shim-only — no reinstall
  • Compat: Z0MB1ES (on teh ph0ne) confirmed Playable
  • Compat: I Dig It promoted from Broken → Playable (end-to-end after the two fixes below)
  • Fix: I Dig It crashed at frame 1 of IDigItApp.Update with FileLoadException(0x80131515) wrapping NotSupportedException: A non-collectible assembly may not reference a collectible assembly on its first reference to its bundled Chipmunk.dll physics wrapper. Root cause: the AssemblyLoadContext.Default.Resolving handler in ApplicationLaunch loaded every sibling DLL it found in the install dir into the collectible user ALC and handed the resulting assembly back to its Default-ALC requestor. The CLR then refused to bind a non-collectible static reference to that collectible assembly, so the JIT failed at the IDigItApp.Update call site. Fix: split the handler's two cases — the main game DLL still routes into the user ALC (FNA's ContentTypeReaderManager.Type.GetType legitimately needs the collectible type back, and loading it into Default would re-trigger the launch-1-statics contamination bug guarded by the existing comment), but sibling DLLs now load into the Default ALC so non-collectible static refs bind cleanly. Trade-off: those siblings persist across launches; for self-contained native-wrapper libs like Chipmunk that's harmless. Added a _CurrentMainAssemblyName field to drive the discrimination. Shim-only — no reinstall
  • Fix: After clearing the Chipmunk crash, I Dig It got past the splash but still wedged just before the menu rendered, this time with MissingMethodException: LeaderboardReader.get_TotalLeaderboardSize(). Our shim had TotalLeaderboardSize declared as a method; XNA exposes it as an int property. Adding only that surfaced the next missing member (get_PageStart), so the full property surface (CanPageDown, CanPageUp, IsDisposed, IsSynchronizedWithLiveServer, Leaderboard, PageSize, PageStart, TotalLeaderboardSize) plus EndPageDown/EndPageUp/EndRead
    • PageDown/PageUp + IDisposable.Dispose was filled in in one pass. All stubs report "no leaderboard available" (false / 0 / empty / fresh reader), so the game treats the leaderboard screen as empty rather than crashing — appropriate given there's no live Xbox LIVE backend. Shim-only — no reinstall
  • Compat: Castlevania Puzzle promoted from Partial → Playable (story / arcade mode now start; previously tapping a save slot silently no-op'd)
  • Fix: Castlevania Puzzle's save-slot screen rendered correctly but tapping a slot to start the game did nothing. The slot tap fires DashResourceProvider.getLocalizedBinary, which uses the standard WP7 localization-fallback idiom try { OpenStream("dat\\data\\items.en-GB") } catch { OpenStream("dat\\data\\items") }. Our FNA TitleContainer.OpenStream was catching FileNotFoundException (and every other exception), logging it to [wpr-ex], and returning null instead of rethrowing. The game's catch therefore never fired, the fallback to the unsuffixed file never ran, and the subsequent new BinaryReader(null) threw ArgumentNullException inside Game.Update — which Game.Tick swallows. The save-slot UI stayed visible, so the user perceived the "Start Game" button as missing. Fix: rethrow at the end of both catch blocks in TitleContainer.OpenStream after the Content/Scenes/ fallback has been given a chance to run. [wpr-ex] diagnostics still log on the way out. Likely had silent secondary effects in any other game using the same localization-fallback pattern. Shim-only — no reinstall
  • Compat: Assassin's Creed: Altaïr's Chronicles promoted from Partial → Playable (end-to-end after SignedInGamer.SignedIn deferred-invoke fix below)
  • Fix: Assassin's Creed crashed in XNAGame..ctor with a NullReferenceException inside its own SignedIn handler. Root cause: SignedInGamer.SignedIn's add accessor took two paths — a deferred 2s Task.Delay for the very first subscriber in the session, and a synchronous invoke for any later subscriber (gated by a static FirstSignInSessionDone flag). Two compounding problems: (1) SignedInGamer.Reset() was called after Activator.CreateInstance(mainType!) in ApplicationLaunch.Start, so the flag set by a previous game launch in the same WPR session carried into the new game's ctor; (2) the synchronous path then fired the just-attached handler during +=, while the game's ctor was still mid-construction, and the immediate-fire branch had no try/catch wrapper (unlike the deferred branch). Assassin's XNAGame.a accessed a this-field its ctor hadn't initialised yet, NRE'd, and the exception escaped up through Activator.CreateInstance. Fix: collapse the two paths into one — every subscription dispatches on Task.Run (delay 0 for late subscribers, 2s for the first), goes through the same _SignInGate semaphore + try/catch, and never runs synchronously from +=. Also moved SignedInGamer.Reset() to run before Activator.CreateInstance. Shim-only — no reinstall
  • Compat: Plants vs Zombies promoted from Broken → Playable (end-to-end after achievement-seed + SpriteBatch tolerance fixes below)
  • Fix: PvZ launched to a black screen because Lawn.AchievementsWidget.Draw NRE'd inside Achievements.GetAchievementItem — the game's static gAchievementList was empty, so the lookup returned null and the NRE escaped mid-SpriteBatch.Begin, wedging every subsequent frame on "Begin has been called before calling End". Root cause: PvZ stores its 18 achievement keys in an inline Achievements.ACHIEVEMENT_KEYS[] static array (built in the cctor), so none of XnaAchievementCodeExtractor's three sources — Content/xml/socialnetworks.xml.xnb, IL ldstr near AwardAchievement* callsites, or Content/Achievements/*.xnb filenames — recovered any keys at install time. The seeder wrote 0 rows; BeginGetAchievements returned 0 rows; the game's GetAchievementsCallback added 0 items to gAchievementList. Added a Source D (KnownProductCatalogues) keyed by ProductId that returns hardcoded keys recovered via decompilation, threaded productId through XnaAchievementCodeExtractor.ExtractRich and XnaAchievementSeeder.SeedAsync. Install-time change — affected games need reinstalling. Now seeds 18 rows and BeginGetAchievements: 18 rows confirms it
  • Fix: After the achievement seed worked the game flickered on main menu and in level — Lawn.GameSelector.DrawOverlay still NRE'd every frame on some other null reference, but the deeper problem was that PvZ's Sexy.Graphics layer tracks its own spritebatchBegan flag separately from FNA's beginCalled, and the two desync once any Draw exception leaves the game's flag stale. The result was a within-frame double-Begin via SetupDrawMode → EndFrame → EndDrawImageTransformed → BeginFrame, throwing InvalidOperationException and dropping every other frame. We can't reach the game's private flag from the shim. Made FNA's SpriteBatch.Begin / End tolerant of out-of-order calls: a second Begin without an intervening End soft-resets and starts a fresh batch (drops the queued sprites of the discarded batch); a stray End without a matching Begin no-ops. First 5 of each are logged so we can spot if it's firing in production. Shim-only — no reinstall
  • Feat: Diagnostic [wpr-trace] BeginGetAchievements: N rows for <ProductId> log in SignedInGamer.BeginGetAchievements confirms whether the install- time seed populated for a given game on first launch
  • Feat: Keyboard accelerometer simulator. New Controls page in the desktop sidebar binds four keys (defaults WASD) to tilt directions; readings flow through the existing Microsoft.Devices.Sensors.Accelerometer so games see them without any per-game shim work. Sensitivity slider, master toggle, in-game tilt-overlay (Avalonia for Silverlight host, FNA DrawableGameComponent for XNA), and a live-preview dial on the Controls page. Orientation-aware: the screen-relative key intent gets rotated into the device-portrait frame the WP7 sensor contract expects, so a landscape game (W = "tilt up the screen") produces the correct device-X tilt the game interprets as steer-left. Desktop orientation is inferred from the back-buffer aspect because FNA's Window.CurrentOrientation only updates from SDL display-rotation events that never fire on the desktop. Accelerometer.CurrentValue / IsDataValid / TimeBetweenUpdates were also added so games that poll instead of subscribing to ReadingChanged see live readings too
  • Compat: Hydro Thunder GO promoted from Partial → Playable (steerable via the keyboard accelerometer)
  • Compat: Uno confirmed Playable
  • Fix: Sonic 4 Episode I self-paused on every Update tick. The game's AppMain.isForeground flag is flipped true only by its Game.Activated handler, and Activated never fired on WPR because INTERNAL_isActive was initialised true (suppressing the BeforeLoop setter's no-op transition to avoid Asphalt 5's pre-Initialize KeyNotFoundException). Net effect: isForeground stayed false, the game's pause condition (!isForeground ORed into the trigger) re-armed every frame, and pause reasserted itself immediately after dismissal. Fix: defer a one-shot OnActivated to the end of the first Game.Tick, where every game's per-Update state (including Asphalt 5's) is already populated
  • Fix: Window background/foreground transitions now fire Deactivated/Activated correctly on desktop and Android. Restored the SDL_WINDOWEVENT_FOCUS_LOST → IsActive=false branch in SDL2_FNAPlatform.PollEvents that had been commented out under a //RnD marker. Symmetric FOCUS_GAINED was already wired
  • Feat: Multi-touch input is now additive with the mouse-as-touch shim. UpdateTouchPanelState previously branched either-or — if TouchPanel.MouseAsTouch was on (the default for XNA games on WPR) the real-finger poll loop was skipped entirely, capping all input at a single touch even on multi-touch hardware. New shape: real fingers fill slots 0..MAX_TOUCHES-2 from SDL_GetTouchFinger; when MouseAsTouch is on the mouse takes the last slot with synthetic finger ID int.MaxValue so it can never collide with a real finger's ID. Sonic 4's "hold D-pad + tap jump" gameplay now works
  • Fix: Asphalt 5 splash → menu tap-to-continue now registers. The per-Game.Tick FrameworkDispatcher.Update() call added on 21/05 was redundant — stock FNA's Game.Update already pumps the dispatcher at its end. Pumping twice per tick made TouchPanel.Update run twice in close succession; the second run promoted touches[0] from Pressed to Moved before CGame1.g() could read it, so h2.b (press handler) never recorded the finger and h2.d's if (i > 0) release-guard always failed. Splash-state lt.b() waiting on be.ey.fm != 0 was the surfaced case. Removed the redundant pump in FNA Game.Tick
  • Compat: Asphalt 5 promoted from Partial → Playable
  • Compat: Sonic 4 Episode I confirmed Playable (pause loop fixed, multi-touch routed)
  • Compat: Tentacles promoted from Broken → Playable (end-to-end after the fixes below)
  • Fix: FNA's ContentTypeReaderManager couldn't resolve ReflectiveReader<PressPlay.FFWD.Scene> because Type.GetType() doesn't see types in the user game's collectible ALC, and the existing string.Split(',') fallback broke on the generic argument [[PressPlay.FFWD.Scene, PressPlay.FFWD, …]] (the comma between the inner type and its assembly hint was indistinguishable from the outer delimiter). Replaced with Type.GetType's resolver-callback overload that walks AppDomain.CurrentDomain.GetAssemblies() — which returns every loaded assembly across every ALC — so generic readers parameterised by user-ALC types resolve correctly. Tentacles' Preloader scene XNB now deserialises
  • Fix: FFWD-based games call Application.LoadLevel("X") with bare scene names even though every level XNB ships under Content/Scenes/. ContentManager constructed Content/X.xnb, the file wasn't there, AssetHelper.Load<T> silently swallowed the failure and returned default(T), and the loading screen hung forever waiting on loadingProgress == 1.0f. TitleContainer.OpenStream now retries Content/<name>.xnb as Content/Scenes/<name>.xnb when the original is missing and has no subdirectory hint. Safe for non-FFWD games (fallback file simply won't exist either)
  • Fix: Microsoft.Phone.Shell.StandardTileData.Count was int in our shim but the WP7 SDK uses int?. Tentacles' live-tile updater calls set_Count(int?) every frame from a component Update and was tripping MissingMethodException ~once per tick
  • Fix: FNA.Game.RunLoop now wraps the final OnExiting(this, EventArgs.Empty) in try/catch + log. Tentacles' MetricsSender.CreateTearDownExtendedKeys NREs on GlobalManager.Instance.currentProfile when the user closes during early boot (currentProfile is null until the preloader finishes loading it). Can't fix the game code, but the host no longer surfaces its "unexpected error" dialog on close
  • Fix: SignedInGamer.SignedIn handler invocations now serialise behind a SemaphoreSlim. Tentacles' Game1.Initialize registers the same callback twice; both invocations were firing in parallel via separate Task.Delay.ContinueWiths, racing on the shared AchievementContext.Current and tripping EF Core's ConcurrencyDetector inside Gamer.GetProfile. Handlers still get their independent ~2 s delay; only the synchronous Invoke runs single-file
  • Fix: Mouse-as-touch clicks now produce GestureType.Tap gestures on desktop. SDL_MOUSEMOTION was forwarded to INTERNAL_onTouchEvent unconditionally, including hover (no button held). Hover motion ran GestureDetector.OnMoved which set activeFingerId = 1; the next MOUSEBUTTONDOWN's OnPressed(1) then saw activeFingerId != NO_FINGER and routed into pinch-init (state = PINCHING). The matching MOUSEBUTTONUP ran OnReleased_Pinch instead of Tap detection — so Tap-gated screens (Tentacles' LemmyTravelScreen, "tap to continue" prompts) never advanced from mouse but worked fine from real touch (no hover). Fix: SDL2_FNAPlatform skips the synthesised Moved event when evt.motion.state == 0. Drag (button-held motion) still goes through, so swipe/pinch/drag from mouse continue to work
  • Feat: Periodic FFWD loading-state heartbeat in FNA Game.Tick ([wpr-heartbeat], fires ~every 2 s past the first-30-ticks verbose trace cap). Reflects PressPlay.FFWD.Application and PressPlay.Tentacles.Scripts.LevelHandler static fields plus the active screen stack and the current level identity — turned a "stuck on loading screen" symptom into a precise gate-by-gate diagnosis. Best-effort, swallows all reflection errors, [Conditional("DEBUG")]-gated
  • Feat: ContentManager.Load<T> now logs the underlying exception with type/HResult/inner/stack before rethrowing ([wpr-content]). FFWD's AssetHelper.Load silently catches ContentManager.Load failures and returns default(T); without the trace, a missing ContentTypeReader or malformed XNB was invisible. Trace lets the caller's swallow stand — only adds visibility
  • Feat: Assembly-resolver diagnostics in ApplicationLaunch[wpr-resolve-user] / [wpr-resolve-default] lines log every Resolving probe with full exception details when LoadFromAssemblyPath fails

21/05/2026

  • Fix: Asphalt 5 sat on a blank loading screen — StartupMode enum integer values now match the WP7 SDK (Launch=1, Activate=2), and FNA's Game.IsActive no longer fires a spurious Activated event at first frame
  • Fix: Tentacles crashed at exit on a null ApplicationCurrentMemoryUsageMicrosoft.Phone.Info.DeviceExtendedProperties now returns the WP7 memory counters (ApplicationCurrentMemoryUsage, ApplicationPeakMemoryUsage, ApplicationMemoryUsageLimit)
  • Fix: XNA games that wait on MediaPlayer / song-finished callbacks before advancing past their splash now progress — Game.Tick auto-pumps FrameworkDispatcher.Update() once per update, matching WP7 XNA 4.0 behaviour
  • Feat: Per-game wpr_game_debug.log and the in-engine [wpr-trace] output are now #if DEBUG-gated via WprDebugTrace — Release builds elide the trace formatting and file listener entirely (no log spam, no per-frame cost)
  • Feat: Richer assembly-resolver diagnostics in ApplicationLaunchResolving failures log the underlying exception type, HResult and inner exception instead of the opaque "Operation is not supported" surface error
  • Compat: Asphalt 5 promoted from Broken → Partial (loads splash, gameplay TBD)
  • Compat: Final Fantasy promoted to Playable
  • Compat: Fruit Ninja confirmed Playable (no longer blocked on GamerProfile.GetGamerPicture)

17/05/2026

  • Fix: Small bug on second launch of XNA games, where resources arent released fully
  • Feat: Added Windows based notifications
  • Feat: Added game icon as window icon for XNA

Clone this wiki locally