19 KiB
Zune HD on-device parity audit — Dorado-HD vs firmware disassembly
Date: 2026-09-10
Method: systematic comparison of the reconstructed Zune HD firmware corpus
(zune-hd-disassembly/ — 109 ARM32 PE modules, 66,598 functions, 4,169 named
exports, 22,479 strings) against the Dorado-HD Android app source.
Scope: on-device parity — every behavior the Zune HD itself had. Dorado-HD's
additions beyond the device (scrobble, lyrics, DSP, Dynamic Mix, cloud, LAN sync,
Now Playing widget) are not scored here; they are post-device extensions
(canon §10).
Provenance. Device evidence is cited as module.dll@<VA> (virtual address in
the reconstructed PE) or as a recovered string. HD evidence is path:line. Raw
decompiled C, symbol dumps and strings stay in the external corpus and are never
committed; this document is synthesized analysis only.
Companion: zune-hd-parity-gaps.json is the
machine-readable gap register.
1. On-device parity scorecard
| Domain | On-device parity | Notes |
|---|---|---|
| A. Shell & navigation | ~78% | Core surfaces match; a set of device scenes is absent (audiobooks, share, queue, inbox…). |
| B. Now Playing & transport | ~75% | Music/video/radio work; no scrubber, queue list, or picture NP. |
| C. Motion & kinetics | ~60% | Shape matches; the integrator mathematics diverges (see §4). |
| D. HUD / always-on | ~45% | Transport overlay + shade; no dimmer/power-off, PIN lock, status OSD. |
| E. Library & indexing | ~80% | Pivots + A–Z rail; no search UI, no built-in playlists, no letter/grid thresholds. |
| F. Playback engine | ~70% | Playback/shuffle/repeat/hearts; no EQ, queue engine, RDS, WMA/DRM. |
| G. Marketplace & commerce | ~30% | Frozen 62-entry catalog; no client/search/cart. |
| H. Social / inbox / identity | ~30% | Feed mock; no inbox, user card, Passport. |
| I. Settings & device | ~70% | Most pages; no PIN/screen-lock, Wi-Fi info, TV-out. |
| J. Tokens / fonts / strings | ~70% | Palette/scale canon-derived; label vocabulary diverges. |
Weighted on-device parity: ~68%. (The device is 2009 hardware; several device subsystems — marketplace, DRM, Passport, MTPZ, TV-out, telemetry — are deliberately out of scope; see §12.)
2. Shell & navigation (gemstone.exe — 74 Gem*Scene identifiers)
Full device scene list recovered from gemstone.exe.strings.json. Status:
✅ parity · 🟡 partial · ❌ missing · N/A out of scope.
| Device scene | Dorado-HD | Status |
|---|---|---|
GemStartScene / GemStartListScene |
ui/screens/HomeScreens.kt:68,111 |
✅ |
GemTiltScene (Quickplay) |
ui/screens/HomeScreens.kt:147 |
✅ |
GemPivotScene |
design/components/CrossbarBar.kt |
✅ |
GemOverlayScene |
ui/screens/NowPlayingScreen.kt:477 |
✅ |
GemSyncScene |
ui/screens/DeviceScreen.kt |
✅ |
GemLibrarySongScene / LibraryListScene |
ui/screens/MusicScreen.kt:257 |
✅ |
GemLibraryArtistScene (+About/Related) |
ui/screens/DetailScreens.kt:169,225,320 |
✅ |
GemLibraryPodcastScene / Video |
ui/screens/Podcasts.kt:142; MediaScreens.kt:76 |
✅ |
GemLibraryLetterPickerScene |
design/components/KineticList.kt:106 |
✅ |
GemNowPlayingRadioPresetListScene |
ui/screens/Radio.kt:110 |
✅ |
GemMarketplaceScene / Games |
ui/screens/MarketplaceScreens.kt:56,205 |
✅ |
GemSettingScene / About |
ui/screens/SettingsScreen.kt:43,254 |
✅ |
GemStartGridScene |
— (list only) | ❌ |
GemStartExtrasScene |
Marketplace apps/games pivots | 🟡 |
GemQueueListScene / GemNowPlayingMusicListScene |
— (enqueue exists, no queue UI) |
❌ |
GemLibraryAudiobookPartScene |
— (no audiobook model) | ❌ |
GemLibrarySendComposeScene / …SendFriendsListScene |
— (no ACTION_SEND) |
❌ |
GemPictureTouchClientScene / GemZoomTouch |
MediaScreens.kt:657 static Fit |
❌ |
GemLibraryArtistPhotoGridContentScene |
single image DetailScreens.kt:275 |
🟡 |
GemLibraryCategoryScene |
DetailScreens.kt:484 (genre only) |
🟡 |
GemLibraryGameScene |
Marketplace games pivot | 🟡 |
GemNowPlayingPicturesScene / …MainScene |
no picture NP mode | 🟡 |
GemNowPlayingRadioScene / …MainScene |
unified music NP reused | 🟡 |
GemMarketplaceSearchScene |
— (frozen catalog) | ❌ |
GemMarketplaceMusicTopScene / …GenresContentScene |
"featured" only MarketplaceScreens.kt:256 |
❌ |
GemMarketplaceDetailsScene (album/artist) |
DetailScreens.kt:63 |
✅ |
GemMarketplaceGamesDetailsScene (+Screens) |
catalog rows only | ❌ |
GemCartScene / GemCartConfirmScene |
— | ❌ (N-A candidate) |
GemSocialScene |
MediaScreens.kt:632 (mock) |
✅/mock |
GemUserCardScene |
— | ❌ |
GemInboxListContentScene / GemInboxDetailsScene |
ui/apps/mocks/Mocks.kt:90 (mock) |
❌ |
GemSettingPinLockScene / …ScreenLockScene |
LockShade only (no PIN) |
❌ |
GemSettingWifiInfoScene |
— | ❌ |
GemSettingStorageScene |
SettingsScreen.kt:124, DeviceScreen.kt:333 |
🟡 |
GemSettingLegalScene |
footnote SettingsScreen.kt:261 |
🟡 |
GemRefreshScene |
Settings/library refresh (no scene) | 🟡 |
GemTvOutputSelectorScene |
— | ❌ (hardware N-A) |
GemAcsFWScene |
cloud/CloudUpdateService.kt (different path) |
🟡 |
Widgets (GemContextMenu, GemWaitCursor, GemTimeline, GemTunerBar, …) |
Android/Compose runtime | N/A |
Highest-value missing surfaces
- Queue surface (
GemQueueListScene/GemNowPlayingMusicListScene) —PlaybackController.enqueue(media/PlaybackController.kt:153) already exists; only the list UI is missing. - Picture pinch-zoom/pan (
GemPictureTouchClientScene/GemZoomTouch) — device-signature viewer; HD is static. - Sharing (
GemLibrarySendComposeScene) — scoped as roadmap M5. - Audiobooks (
GemLibraryAudiobookPartScene) — whole media type absent. - Marketplace discovery (search/top/genres) — makes the frozen catalog navigable.
3. Motion, touch & kinetics (resolved)
3.1 The device integrator (authoritative)
Recovered from xuidll.dll@0x41841D58 (scroll tick), @0x4184C310 (drag
velocity), @0x41848B98 (timer), @0x41848FC4 (rubber-band),
@0x4184C660 (default touch settings).
- Position:
pos += (dt_ms / 1000) · v, wheredt_ms = max(33.333, now − lastTick)(30 fps floor; literalsxuidll.dll@0x41801920 = 1000.0,@0x41803ce4 = 33.333). - Velocity (active/drag):
v' = v · (1 + c[+0xB0]), clamped to±( step[+0x88] · c[+0xB4] ). - Stop: when
posreaches the target[+0xA0], the timer is killed and the shell snap callbacks fire; velocity zero-compare is0.0. - Tick:
XuiSetTimer(elem, 1, 0x10)→ 16 ms = 62.5 Hz (@0x41848B98). - Drag→velocity:
(Δpx/Δt) · scale, hard-capped at 32.0 (@0x4184C310, literal@0x41804168 = 32). - Rubber-band: explicit damped spring
-(pos−target)·k[+0xD8] − v[+0x50]·d[+0xD4] + v[+0x50](@0x41848FC4). - Touch properties (the ~20-float
XuiTouchSettingsvocabulary recovered from thexuidllstring table0x41803F74–0x41804150):HorzScroll,VertScroll,HorizontalFling,VerticalFling,HorizontalScrub,VerticalScrub,HorizontalRubberBand,VerticalRubberBand,FadeIn,FadeOut,PositionOffset,CenterSmallCanvas,EnableBandBreak,KeyScrollDistance,SmoothScroll,ScrollPaddingBefore/After,ZoomTarget,Hollow,Direction.
3.2 Correction: the 0.95 retention provenance is false
The previous docs (zune-hd-touch-settings.md §6, canon §6) attributed HD's
per-frame retention to XuiTouchSettings[0x1C] = 0.95. This is not supported:
- the integrator reads the element's in-place block at
+0xA8fields+0xB0/+0xB4= settings[0x08]/[0x0C]—xuidlldefaults 1.9 / 1.5, shell 1.9 / 1.2; [0x1C]defaults to 0.7 and is never read by@0x41841D58;- the update
v·(1+c)is multiplicative-with-clamp, notv·retention.
3.3 HD state and gaps
| Aspect | Device | Dorado-HD | Gap |
|---|---|---|---|
| Tick | 62.5 Hz (@0x41848B98) |
KINETIC_FRAME_HZ = 60f (design/DoradoMotion.kt:30) |
4% friction-conversion drift |
| Integrator | dt-scaled, 30 fps floor, step-clamped | continuous animateDecay (design/components/ZuneFling.kt:31) |
model differs |
| Retention | (1+c) clamp, no [0x1C] |
0.95/0.94 (DoradoMotion.kt:29,38) |
empirical, provenance wrong |
| Rubber-band | explicit spring (@0x41848FC4) |
platform overscroll; "no springs" invariant | under-models device |
| Axis hysteresis | XuiTouchEnableAxisHysteresis (xuidll@0x418331CC) |
implicit Compose latch | not tunable |
| Velocity cap | 32.0 | none | absent |
| Item snap / SmoothScroll | XuiTouchSnapToTarget (@0x41833134) |
scrollToItem for A–Z only |
absent |
| Navigation easing | per-element timelines (TransFrom/To, EnterTransIndex/LeaveTransIndex) |
one bezier 380/420/340 ms | different model |
Recommendation: re-derive the HD fling friction from the dt-scaled device model
(or explicitly label 0.95/0.94 an empirical approximation), and stop citing
[0x1C]. Add the velocity cap and (optionally) item snapping.
4. HUD / always-on (zhud_serv.dll, 1,167 fns, 5 exports)
Device HUD scenes recovered: HudMediaController{Music,Radio,Video}Scene,
HudScreenLockScene, HudPinLockScene, HudDimmerScene, HudOffSwitchScene,
HudMessageBoxScene, HudWaitMessageBoxScene, HudKeyboardScene,
HudNetwork{Connect,List}Scene, HudAutoConnectScene, HudWaitCursorScene,
HudDebugOverlayScene, + root scenes (ScreenLockRootScene, PinLockRootScene,
MessageBoxRootScene, DimmerRootScene, …).
Inline HUD dwells are recoverable from XuiSetTimer call sites (contradicts
zune-hd-touch-settings.md §5): 7000 ms dim (@0x419CD420), 5000 ms saver,
3000 ms notifications, 30000 ms background (@0x419C0584), 12000 ms constant.
| Device HUD | Dorado-HD | Status |
|---|---|---|
Transport overlay (HudMediaController*) |
TransportOverlay ui/screens/NowPlayingScreen.kt:477 |
✅ |
| Play/Pause/Next/Prev | PlaybackController.kt:160-171; NowPlayingScreen.kt:563-592 |
✅ |
| Volume + mute | swipe volume only; no mute | 🟡 |
ProgressSlider + PlayPosition/Elapsed/Remaining |
no scrubber in NP | ❌ |
Ffwd |
— | ❌ |
Lock/pin (HudScreenLock/HudPinLock) |
LockShade (no PIN) design/components/LockShade.kt |
❌/🟡 |
Dimmer / power-off (HudDimmer/HudOffSwitch) |
— | ❌ |
| Message boxes | not modelled | ❌ |
Status OSD (batteryIcon,wifiIcon,Clock,Ambient) |
— | ❌ |
| Screensaver idle | IDLE_SCREENSAVER_MS=5000 design/DoradoTokens.kt:68 |
✅ |
| Wait cursor | Compose loading | 🟡 |
5. Library & indexing (zcontent_serv.dll + zconfig_serv.dll)
Device substrate: zunedb.dat, zconfig.dat, drmStore.dat, AlbumArt,
mtp:DatabaseGenerationNumber, CloudModifiedItems; zconfig_serv exposes
LibraryLetterThreshold / AlbumGridThreshold.
| Device behavior | Dorado-HD | Status |
|---|---|---|
| Indexed library (tracks/albums/artists/genres/playlists/pictures) | data/repo/LibraryRepository.kt:35-48 |
✅ |
| A–Z letter picker | design/components/KineticList.kt:66,176 |
✅ |
| Indexed search | Daos.kt:68 search() defined, no caller |
❌ |
| Letter/grid thresholds (settings) | — | ❌ |
Built-in playlists (BuiltIn-FavoriteTracks, …MostPlayedArtists, …RecentTracks) |
Quickplay history/pins + Top Played HomeScreens.kt:311 |
🟡 |
Sort keys (article-strip, MMM yyyy) |
plain tags | 🟡 |
| Album-art cache/thumbnails | per-track resolve | 🟡 |
6. Playback engine (zmedia_serv.dll)
Device: CMediaQueueBase/CProgressivePlay/CTrackListQueue, queue
capacities/history, ZMediaQueue/MediaItemPositioned|Stopped|Paused|Playing;
EQ (Software\Microsoft\Zune\Equalizer, Preset%02d); FFTGRABBER/ZMedia/Viz;
SmartDJStore.dat; RDS (Radio_RDS_Capture.dat); marketplace image/related
endpoints; DRM (CeDRM_MGR_ParsePlayOPL).
| Device behavior | Dorado-HD | Status |
|---|---|---|
| Play/shuffle/repeat/seek/hearts | media/PlaybackController.kt:142-245 |
✅ |
| Queue engine (capacity/history/position events) | thin Media3 wrapper | ❌ |
| Equalizer presets | — (no AudioEffect) |
❌ |
| FFT visualizer bound to playback | procedural bars; real FFT exists separately analysis/FeatureMath.kt |
🟡 |
| WMA/WMV + WMDRM/PlayReady | data/model/MediaFormats.kt:17-23 drops wma; no DRM |
❌ |
| RDS / FM tuner | HTTP-stream presets only Radio.kt |
❌ |
| Smart DJ store | PlaybackController.smartShuffleOrder:245 |
🟡 |
7. Marketplace & commerce (gemstone + znet_serv.dll)
Device: GemMarketplace*, GemCart*; znet_serv endpoints
(album?q=%s, /albums?orderby=ReleaseDate, /recommendations/artists,
chart/zune/tracks), Passport sign-in (SignInRequest/Response,
urn:passport:compact), usage reporting (usageReport, SEID_Stats).
| Device | Dorado-HD | Status |
|---|---|---|
| Marketplace catalog/search/artwork client | frozen 62-entry data/official/OfficialCatalog.kt; MarketplaceScreens.kt:322 |
❌ |
| Music genres / top charts | "featured" only | ❌ |
| Game detail + screenshots | catalog rows | ❌ |
| Cart / purchase / confirm | — | ❌ (N-A candidate) |
| Sign-in / subscriptions | Last.fm + cloud OAuth only | ❌ |
| Usage/telemetry | — | ❌ (privacy: N-A) |
8. Social / inbox / identity
| Device | Dorado-HD | Status |
|---|---|---|
| Social feed | MediaScreens.kt:632 (mock); cloud social service exists |
✅/mock |
Inbox list/details (GemInbox*) |
ui/apps/mocks/Mocks.kt:90 |
❌ |
User card (GemUserCardScene) |
— | ❌ |
WMA streaming / DRM (ZWmtStreamer.dll, XDRM*) |
not supported | ❌ (N-A) |
Credentials/DRM store (zcredentials_serv) |
no keyvault | ❌ (N-A) |
MTPZ/UPnP identity (/Device.xml, urn:…mtpz:1) |
custom LAN sync (sync/) |
🟡 |
9. Settings & device (gemstone GemSetting* + zconfig_serv)
| Device | Dorado-HD | Status |
|---|---|---|
| Settings root / About / Storage | SettingsScreen.kt:43,254,124 |
✅/🟡 |
| Legal | footnote SettingsScreen.kt:261 |
🟡 |
| Screen lock / PIN lock | LockShade only |
❌ |
| Set time / lock-on | OS-managed / wake shade | N-A |
Wi-Fi info (GemSettingWifiInfoScene) |
— | ❌ |
| TV-out selector | — | ❌ (hardware) |
Config store (zconfig_serv: SwapEffect, ImageProduction, …) |
N/A (Android) | N/A |
Cloud/OTA (UPDATE, DColdReboot) |
cloud/CloudUpdateService.kt |
🟡 |
10. Tokens, typography, strings, assets
- No color hexes or numeric point sizes exist in the UI binaries. XUI carries
color/size as property names only (
TextColor,FillColor,PointSize,Font,LineHeight,TextScale). HD's palette/scale are canon/review-derived, not byte-citable — the audit cannot verify them against the binaries. - Font posture is correct: HD bundles only OFL Selawik (
design/DoradoTheme.kt:16-20); the device's six Zegoe UI faces live only in the external corpus. Gap: the docs promise "import your own Zegoe" (zune-hd-ui-canon.md:20,DoradoTheme.kt:13) but no font-import path exists. Also Selawik has no Black(900)/Medium(500), soFontWeight.Medium(widget/NowPlayingWidget.kt:94) snaps. - String parity drift (device vocabulary HD could adopt):
"Various Artist"(gemstone@0x119A4) vs HD"unknown artist"(data/scan/MediaLibraryScanner.kt:64);" by "composition (gemstone@0x15A60) vs HD em-dash (ui/components/Common.kt:291);- empty-state keys
Empty,noItems/noItems_local/noItems_online(gemstone@0x12B7C,0x14160/0x148BC/0x14840) vs bespoke HD copy; - transport/status words HD lacks:
Ffwd,Mute(gemstone@0x19D18/0x19D2C;zhud_serv@0x419B4C70/84),Disconnected,Refresh,Wishlist.
- Docs↔code naming drift (values equal):
SurfaceTileHover→tilePressed,SurfaceBorderSubtle→border, accent labels;TYPE_HEADER_CROP_VISIBLE=48undocumented (design/DoradoTokens.kt:25).
11. Deliberately out of scope (device features we do not intend to reproduce)
These are device-era subsystems whose servers/hardware are dead or proprietary; they are recorded as N-A, not gaps:
- Marketplace backend + Passport/Microsoft account + subscriptions.
- DRM license acquisition (
CeDRM_Mgr_*), PlayReady/WMDRM, secure clock. - WMA/WMV playback and
ZWmtStreamerHTTP streaming. - MTPZ/UPnP device identity and Wi-Fi config (
wzcsapi). - TV-out selector; per-app power/display/suspend timeouts (ZAM).
- Usage/telemetry reporting (
usageReport,SEID_Stats). - ACS touch-controller firmware update (
GemAcsFWScene).
12. Prioritized gap register (summary)
Full machine-readable register: zune-hd-parity-gaps.json.
| # | Gap | Axis | Effort | Status |
|---|---|---|---|---|
| 1 | Queue surface (GemQueueListScene) |
device | S | ❌ |
| 2 | Picture pinch-zoom/pan (GemPictureTouchClientScene/GemZoomTouch) |
device | M | ❌ |
| 3 | Now Playing scrubber + elapsed/remaining | device | S | ❌ |
| 4 | Kinetic model re-derivation (dt-scaled; fix 0.95 provenance) |
device | M | 🟡 |
| 5 | Library search UI (wire existing search()) |
device | S | ❌ |
| 6 | HUD dimmer/power-off + status OSD | device | M | ❌ |
| 7 | EQ presets | device | M | ❌ |
| 8 | Share / ACTION_SEND + Zune-Card export |
device | M | ❌ (M5) |
| 9 | Audiobooks | device | L | ❌ |
| 10 | Marketplace search/top/genres | device | L | ❌ |
| 11 | Built-in playlists | device | M | 🟡 |
| 12 | Inbox + user card | device | M | ❌ |
| 13 | PIN/screen-lock + Wi-Fi info | device | M | ❌ |
| 14 | String parity (Various Artist, by, noItems, Ffwd, Mute) |
device | XS | 🟡 |
| 15 | Font-import path (or drop the claim) | doc/device | S | ❌ |
| 16 | Velocity cap + item snap | device | S | ❌ |
13. Corrections to existing docs
zune-hd-touch-settings.md: replace the[0x1C]=0.95retention provenance with the resolved integrator model (§3); add the recoverable HUD timer constants; add the XUI touch property-name vocabulary; fixXuiProcessMultiTouchMessageVA to0x4181F100; correct §5 "not recoverable" claims (dwell timers and field names are now recoverable).zune-hd-ui-canon.md§6: cite the device tick (62.5 Hz) and thepos += (dt/1000)·vmodel; do not cite[0x1C].design/DoradoMotion.kt: mark0.95/0.94as empirical approximations of the device glide, not device constants.- Label parity: canon-cite
"Various Artist", thebyseparator, and thenoItems/Emptyempty-state vocabulary before changing display copy.
14. Roadmap delta
Add to parity-roadmap.md: queue surface, picture touch/zoom, NP scrubber,
library-search UI, HUD dimmer/status, built-in playlists, inbox/user-card,
PIN/Wi-Fi settings, string-parity pass, font-import decision; and annotate
marketplace/DRM/Passport/MTPZ as documented N-A (§11).