Data Sources
Single source of truth: the app consumes a generated JSON catalog API (
CatalogApiService). This is the samecatalog.jsonthat builds the Emulation Revival website, so the desktop app and the site never drift apart.
Emulation Revival Catalog API
CatalogApiService fetches a single generated JSON document (HTTP GET, 30s timeout):
https://emulationrevival.github.io/api/catalog.json
On success, items are parsed, classified, and written to disk cache (see Catalog Cache).
catalog.json structure
Top-level envelope (CatalogApiResponse):
{
"schemaVersion": 1,
"generatedAt": "2026-06-20T08:00:00Z",
"items": [ /* CatalogApiItem[] */ ]
}
Each item (CatalogApiItem):
| Field | Type | Notes |
|---|---|---|
id |
string | Stable identifier |
title |
string | Display name |
description |
string | Summary text |
category / categorySlug |
string | e.g. Emulator / emulator |
version |
string | Latest version |
releaseDate |
string? | Release date |
compatibility |
string | Console compatibility note |
isExperimental |
bool | Flags experimental apps |
imageUrl / pageUrl |
string? | Card image, source page |
downloadUrl |
string? | Fallback primary download |
sourceCodeUrl / setupGuideUrl / tutorialUrl / releaseNotesUrl |
string? | External links |
requirements |
string[] | Listed requirements |
features |
string[] | Listed features |
contributors |
object | Developers / porters / maintainers / mod authors / prebuilt-by |
downloads |
array | Download assets (see below) |
Download classification
Each entry in downloads[] is { url, label, assetId }. CatalogApiService.ClassifyDownloads tags each as main, dependency, or external so the installer knows what to upload to the console:
- Dependency — URL/label matches the dependency regex (e.g.
VCLibs, framework packages). - External — not an installable package (
.appx/.msix/.zip/.msixbundle/.appxbundle); e.g. mod links, ModDB, or non-release GitHub pages. - Main — the first remaining installable package.
The primary downloadUrl resolves to the first non-dependency asset, falling back to the item’s downloadUrl field. See Package Installation Flow for how these feed the installer.
Catalog Cache
Parsed results are cached to disk so the app starts instantly and works offline:
%APPDATA%\XBVault\cache\catalog-api.json
Cache envelope (CatalogCache): { fetchedAt, source, data }, where data is the full catalog.json payload.
| Property | Value |
|---|---|
| TTL | 6 hours (CacheTtlHours = 6) |
| Location | %APPDATA%\XBVault\cache\catalog-api.json |
| Stale fallback | Used (TTL ignored) when the API is unreachable |
| Manual refresh | CatalogApiService.ClearCache() / force-refresh in the UI |
Fetch flow
flowchart TD
Start["FetchCatalogAsync()"] --> Force{forceRefresh?}
Force -->|No| Cache{"Cache fresh?<br/>age ≤ 6h"}
Cache -->|Yes| ReturnCache["Return cached items"]
Cache -->|No| Fetch
Force -->|Yes| Fetch["GET catalog.json"]
Fetch --> Ok{"HTTP 200<br/>+ parsed?"}
Ok -->|Yes| Save["Save to cache<br/>(fetchedAt = now)"]
Save --> ReturnFresh["Return fresh items"]
Ok -->|No| Stale{"Stale cache<br/>exists?"}
Stale -->|Yes| ReturnStale["Return stale items<br/>(TTL ignored)"]
Stale -->|No| Fail["Return empty<br/>+ error"]
style Start fill:#447F3E,stroke:#9ACA3C,color:#fff
style ReturnCache fill:#9ACA3C,stroke:#447F3E,color:#000
style ReturnFresh fill:#9ACA3C,stroke:#447F3E,color:#000
style ReturnStale fill:#FF9900,stroke:#447F3E,color:#000
style Fail fill:#CC3333,stroke:#447F3E,color:#fff
style Force fill:#2A2D33,stroke:#447F3E,color:#9ACA3C
style Cache fill:#2A2D33,stroke:#447F3E,color:#9ACA3C
style Fetch fill:#2A2D33,stroke:#447F3E,color:#9ACA3C
style Ok fill:#2A2D33,stroke:#447F3E,color:#9ACA3C
style Stale fill:#2A2D33,stroke:#447F3E,color:#9ACA3C
style Save fill:#2A2D33,stroke:#447F3E,color:#9ACA3C
Package Cache
Distinct from the Catalog Cache above (catalog metadata in
%APPDATA%). This cache holds the downloaded package files in%TEMP%.
Downloaded packages are stored in %TEMP%/XBVault/cache/:
graph TD
root["%TEMP%/XBVault/cache/"]
dolphin["dolphin/"]
retroarch["retroarch/"]
pcsx2["pcsx2/"]
root --> dolphin
root --> retroarch
root --> pcsx2
dolphin --> d1["DolphinWinRT_1.1.9.0_x64.msix"]
dolphin --> dm["manifest.json"]
retroarch --> r1["RetroArch-SeriesConsoles.appx"]
retroarch --> r2["Microsoft.VCLibs.x64.14.00.appx"]
retroarch --> rm["manifest.json"]
pcsx2 --> p1["pcsx2-v1.0.0-xbox.msix"]
pcsx2 --> pm["manifest.json"]
style root fill:#447F3E,stroke:#9ACA3C,color:#fff
style dolphin fill:#2A2D33,stroke:#447F3E,color:#9ACA3C
style retroarch fill:#2A2D33,stroke:#447F3E,color:#9ACA3C
style pcsx2 fill:#2A2D33,stroke:#447F3E,color:#9ACA3C
style d1 fill:#9ACA3C,stroke:#447F3E,color:#000
style dm fill:#9ACA3C,stroke:#447F3E,color:#000
style r1 fill:#9ACA3C,stroke:#447F3E,color:#000
style r2 fill:#9ACA3C,stroke:#447F3E,color:#000
style rm fill:#9ACA3C,stroke:#447F3E,color:#000
style p1 fill:#9ACA3C,stroke:#447F3E,color:#000
style pm fill:#9ACA3C,stroke:#447F3E,color:#000
manifest.json stores parsed metadata and dependency info so reinstalls don’t need re-download:
{
"name": "RetroArch",
"version": "1.16.0",
"category": "Emulator",
"packageFile": "RetroArch-SeriesConsoles.appx",
"dependencies": ["Microsoft.VCLibs.x64.14.00.appx"],
"sourceUrl": "https://emulationrevival.github.io/..."
}
Related:
- Package Installation Flow — how cached files and dependencies feed the installer
- API Reference — Device Portal endpoints
- Architecture — where
CatalogApiServicesits in the service layer