-
Notifications
You must be signed in to change notification settings - Fork 2
Hardcore Compliance
This page tracks how Emutastic implements every item in RetroAchievements' hardcore-mode compliance requirements. Sections below mirror RA's own structure (A–H) and address each bullet on the official list.
Status: Code implementation and documentation complete as of v1.6.5. The eligibility timeline (Section D) gates formal application until October 14, 2026 (six-month rule). A small number of optional / verification items remain — tracked at the bottom — but nothing on the auto-fail list is outstanding.
User must be able to conveniently access the list of achievements within the emulator.
✅ The achievements list for the active game appears on the game detail card, accessible from the library by clicking any title. A dedicated Achievements tab in the main window shows account-wide stats, recent unlocks, the trophy case, library spotlight, community pulse, top-10 leaderboard, and a year-view heatmap.
Code reference: Game-detail RA section — GameDetailWindow.xaml (RASection) and GameDetailWindows.xaml.cs. Achievements tab — MainWindow.xaml (TabAchievements).
Triggers must evaluate correctly. Measured and Trigger flags must work properly.
✅ Trigger evaluation is fully delegated to the rcheevos rc_client C library, which is the upstream reference implementation. Frontend exposes the standard libretro RETRO_MEMORY_* regions, with pointers cached before rc_client_begin_identify_and_load_game so address validation succeeds during load.
Code reference: P/Invoke layer — RcheevosInterop.cs. rc_client wrapper — RetroAchievementsClient.cs (CacheMemoryRegions, OnReadMemory, LoadGame).
Both flags must be visible in the achievement list as well as displayed during gameplay when appropriate conditions are met.
✅ The detail card's "Coming up" section surfaces in-progress measured achievements with live progress text (e.g. "3 of 5") sourced from RC_CLIENT_EVENT_ACHIEVEMENT_PROGRESS_INDICATOR_UPDATE. In-game progress events are captured per session and persisted for display on the next library visit.
Code reference: Live progress capture — RetroAchievementsClient.cs (GetLiveProgressSnapshot, progress-update event handler). Detail-card display — GameDetailWindows.xaml.cs (BuildComingUp).
Rich Presence and Leaderboards: Must function correctly.
✅ Both flow through rcheevos. Rich Presence string is captured per session; leaderboard submissions fire on the standard RC_CLIENT_EVENT_LEADERBOARD_* event chain. Neither is suppressed in hardcore.
Code reference: Toast handlers and event subscription — EmulatorWindow.xaml.cs (InitRetroAchievements, _raClient.AchievementTriggered, _raClient.GameCompleted).
Unlocks created while offline must be securely cached and sync to RetroAchievements when connectivity returns.
✅ rcheevos manages the unlock queue and resync internally. The frontend does not intervene.
Code reference: Upstream rcheevos source at github.com/RetroAchievements/rcheevos. Frontend exposes no override.
Hit counts should be stored in save states (highly recommended, not strictly required).
✅ Met. The save-state save path pairs each .state file with a .cheevos side-car containing the rcheevos runtime blob from rc_client_serialize_progress_sized. On load, the side-car is read after a successful retro_unserialize and applied via rc_client_deserialize_progress_sized, restoring achievement hit counts and measured-progress trackers to where they were at save time. Older .state files without a paired side-car load successfully with current rcheevos state left untouched (no regression). End-to-end round-trip verified using Pac-Man (NES) achievement "Cherry Picker" (eat a cherry twice — HitCount-tracked in rcheevos, since NES Pac-Man maintains no cumulative cherry counter in RAM): ate first cherry, saved state, closed the application completely, relaunched, loaded the state, ate one more cherry, achievement triggered — confirming the HitCount of 1 survived the cross-launch round-trip.
Code reference: Serialize / deserialize wrappers — RetroAchievementsClient.cs (SerializeProgress, DeserializeProgress). Side-car write — EmulatorWindow.xaml.cs (FinalizeSave). Side-car read + apply — same file (RequestLoad and ExecuteLoadOnEmuThread).
If you ship a Windows version of the emulator, please investigate adding RAIntegration DLL support.
➖ Not applicable. RAIntegration is the achievement-author toolkit, used for designing and debugging new sets. Emutastic targets players, not authors. The recommendation does not affect player-facing approval.
Save files should use standard formats compatible with other emulators of the same system.
✅ SRAM is read from the libretro RETRO_MEMORY_SAVE_RAM region and written back as a plain .srm file. Save states are the per-core libretro serialized format. Both are compatible with any other libretro frontend using the same core.
Code reference: SRAM read/write — EmulatorWindow.xaml.cs (GetSaveRam, LoadSaveRam, _srmPath). Save state pipeline — same file (RequestSave, RequestLoad, _core.SaveState, _core.LoadState).
Cheats are disabled in hardcore, including: Built-in cheat engines, Mountable cheat devices (GameShark, Game Genie, etc.), External cheat files not used for minor QoL improvements.
✅ All three categories are blocked.
-
Built-in cheat engine: Emutastic's cheats system funnels every apply path through
ApplyAllCheats, which short-circuits when hardcore is active._frontendArCheatsis forced empty, making the per-frame Action Replay write path a no-op. The in-game Cheats menu button is hidden from the HUD. -
Mountable cheat devices: No GameShark / Game Genie / Pro Action Replay device emulation is shipped. Cheat codes are applied via the libretro
retro_cheat_setinterface, which the same gate blocks. -
External cheat files: Cheats are stored as per-game JSON; the gate above prevents them from being read into core RAM in hardcore. The one cheats path Emutastic cannot reach from the frontend is PPSSPP's
cheats/<DiscID>.ini, which the PPSSPP core reads directly without going throughretro_cheat_setand which has no libretro environment callback for the frontend to signal hardcore state. To stay compliant, Emutastic refuses hardcore mode on the PlayStation Portable system entirely — launching a PSP game while hardcore is enabled drops to softcore for that session with a transient message explaining why. Achievements still track, just on the softcore leaderboard. The restriction lifts when upstream PPSSPP gains a hardcore-aware behavior in libretro mode.
Code reference: PSP carve-out — EmulatorWindow.xaml.cs (InitRetroAchievements, the effectiveHardcore snapshot path that demotes PSP launches to softcore).
Code reference: Chokepoint gate — EmulatorWindow.xaml.cs (ApplyAllCheats). All callers route through it: launch-time apply, post-state-load re-apply, ExecuteCheatsApplyOnEmuThread, in-game editor save. Per-frame AR write — same file (ApplyFrontendArToRam, early-returns when _frontendArCheats is empty). Cheat-support classification — CheatSupport.cs.
Rewind is disabled in hardcore.
✅ Emutastic does not implement rewind. A RewindEnabled config field exists as a placeholder but is not wired to any runtime code — there is no rewind buffer, no rewind hotkey, no rewind UI.
Code reference: Dead field — ConfigurationModels.cs (RewindEnabled). Search the repo for RewindEnabled to confirm no consumers exist.
Slowdown and frame advance are disabled in hardcore.
✅ Emutastic implements neither slow-motion / variable-speed playback nor frame-by-frame stepping. The emulation loop runs at the core's native FPS and is audio-driven.
Code reference: Emulation loop — EmulatorWindow.xaml.cs (EmulationLoop). The frontend also always reports false to the libretro RETRO_ENVIRONMENT_GET_FASTFORWARDING query so cores can't optimize based on a non-existent state.
Loading save states is ALWAYS blocked in hardcore.
✅ Gated at every entry point:
- F7 quick-load hotkey
- Load State button in the in-game status bar (also hidden from the HUD)
- In-game Save Menu "Load" rows
- Startup pending-load (when the user launches a game from a state file outside the emulator)
All paths funnel through RequestLoad, which short-circuits with a 4-second transient message in hardcore. State creation is left functional — RA permits creating save states in hardcore, only loading is prohibited.
Code reference: Chokepoint — EmulatorWindow.xaml.cs (RequestLoad). Startup pending-load — same file (_pendingLoadStatePath block in the startup flow). Defense-in-depth on the picker — LoadStateBtn_Click. HUD button hide — ApplyHardcoreHudVisibility.
Rich Presence and Leaderboards cannot be disabled in hardcore. Disabling leaderboard popups is okay to support for either mode.
✅ Neither has a user-facing disable. Toast popups can be hidden but the underlying RA traffic is unaffected.
Code reference: No disable code exists. Toast wiring at EmulatorWindow.xaml.cs (_raClient.AchievementTriggered, _raClient.GameCompleted).
If the emulator supports a resume/quick resume feature, the resumed session must drop to Softcore.
➖ Not applicable. Emutastic does not implement "resume on launch" or "quick resume". Each launch is a fresh session.
Switching from softcore to hardcore is not allowed mid-session. This action must result in a full reset of the game. Switching from hardcore to softcore is allowed mid-session.
✅ The hardcore toggle lives in Preferences (main window), which is not reachable while a game is running — Preferences requires the user to close the emulator window first. Any toggle change therefore applies only to the next launch, which is by construction a fresh session. As an additional defense, the hardcore flag is snapshotted into _raHardcoreActive at InitRetroAchievements time and never re-read during the session, so future changes to the Preferences window cannot regress this.
Code reference: Snapshot and helper — EmulatorWindow.xaml.cs (_raHardcoreActive field, IsHardcoreActive(), snapshot assignment inside InitRetroAchievements). Toggle UI — PreferencesWindow.xaml (RAHardcoreToggle).
Users should be allowed to create save states while in hardcore for debugging purposes, but those states cannot be loadable when hardcore mode is enabled.
✅ Explicit design — save paths (F5, Save State button, in-game save menu) are untouched. Load paths (above) are blocked.
Code reference: Save chokepoint — EmulatorWindow.xaml.cs (RequestSave) — confirm absence of hardcore guard.
Memory editors, debuggers, and/or scripting/TAS/recorded input playback are strictly prohibited.
✅ Emutastic ships none of these. There is no memory editor, hex viewer, RAM watch, debugger, scripting host, frame-by-frame TAS recorder, input-replay system, or save-and-replay UI.
Code reference: Verification by negative — search the main repo for RamWatch, MemoryEditor, Debugger, TAS, InputReplay, or MovieRecord. No matches.
Format:
EmulatorName/v1.0.0 (OSName 10.0) core_name/v0.5.0
Emutastic sends, on both the rcheevos HTTP client and the public Web API client:
Emutastic/<version> (Windows 10)
The product/OS portion satisfies RA's unique-UA requirement on its own. A core_name/<core_version> suffix to identify the active libretro core per request is on the roadmap and will appear here once shipped.
Code reference: UA builder — EmutasticUserAgent.cs (Build). rcheevos HTTP client wiring — RetroAchievementsClient.cs (CreateRcheevosHttp). Web API HTTP client wiring — RetroAchievementsService.cs (BuildHttp).
Segment A (Required): The emulator name and version. Versions must be numeric and incrementing, with the exception of an optional 'v' prefix.
✅ Emutastic uses semantic versioning (1.6.5 as of this release), numeric and incrementing across every release tag.
Segment B (Optional): The operating system name and version.
✅ Reported as Windows 10 or Windows 11 based on the OS build number (build ≥ 22000 reports as Windows 11). The numeric build number is appended in parentheses.
Segment C (Optional but strongly advised): The core name and version. Versions should ideally be numeric or follow semver conventions.
✅ Per-core version is appended when the active core exposes it through retro_get_system_info().library_version. Cores without a version string are reported by name only.
Emulators discovered to have previously identified as another client (intentionally or not) must disclose this when applying for compliance.
➖ Not applicable. Emutastic has never identified as another client. The User-Agent prefix has been Emutastic/ from the first commit that wired up an HTTP path to RA.
The emulator, or the parent emulator it is forked from, must have been publicly available for at least 6 months before it will be considered for hardcore compliance approval.
⏳ Pending. Emutastic is an original codebase, not a fork. First public release: April 14, 2026. Earliest application date: October 14, 2026.
When users log in to their RetroAchievements accounts, enabling hardcore by default is recommended, but not required. If softcore is the default, enabling hardcore must be one click/tap away and clearly indicated.
✅ Met. Fresh installs default to hardcore mode (per RA's recommendation). The toggle itself is reachable in one click: Preferences → RetroAchievements → "Hardcore Mode" toggle button. The toggle is clearly labelled and explains exactly what it disables. Existing users who had previously saved softcore keep their preference — only fresh installs are affected by the default.
Code reference: Toggle — PreferencesWindow.xaml (RAHardcoreToggle). Default value — ConfigurationModels.cs (HardcoreMode = true).
Hardcore state must be visibly indicated in the UI during play.
✅ A red "HARDCORE" pill is shown in the emulator window's persistent status bar, alongside the recording indicator. The pill lives outside the fading overlay HUD, so it is on screen at all times while a hardcore session is active.
Code reference: Pill markup — EmulatorWindow.xaml (HardcoreIndicator border in the status bar). Visibility wiring — EmulatorWindow.xaml.cs (ApplyHardcoreHudVisibility, called immediately after InitRetroAchievements).
Monetization must include a features matrix (free vs paid) with exact limits (e.g., cloud-save quotas). Paid features must not undermine hardcore rules.
✅ Emutastic is fully free, open-source software. There are no paid tiers, no in-app purchases, no premium features, no subscription quotas, no cloud-save tiers. The features matrix is trivially "everything, for everyone." Source at github.com/codingncaffeine/Emutastic.
Publish a page (or otherwise have accessible) a listing of every shipped FOSS core, its license, and any relevant upstream links.
✅ Met. See the Cores wiki page — every core Emutastic surfaces is listed with its upstream repository link and license summary. The page also documents Emutastic's distribution model (Emutastic itself ships no core binaries; the in-app Core Manager downloads directly from libretro's buildbot).
Non-commercial licenses may not be shipped if there is any commercialization without written permission from rightsholders.
✅ Not applicable. Emutastic is non-commercial. Additionally, Emutastic does not redistribute any core binaries — users download cores directly from libretro's buildbot, so the non-commercial-licensed cores (Snes9x, Genesis Plus GX, FBNeo, ProSystem, SAME CDi, MAME 2003-Plus) are not redistributed by Emutastic.
GPL/LGPL/MPL/etc. obligations must be satisfied for any distributed binaries.
✅ Met. Emutastic itself is GPL-3.0; full source at github.com/codingncaffeine/Emutastic with the LICENSE.txt file at the repository root. For core binaries, Emutastic is not the distributor — see the Cores page for the distribution-model documentation. License obligations on the GPL/LGPL/MPL-licensed cores attach to libretro's buildbot at the point of binary distribution.
Privacy policy with data retention periods, server locations/countries, GDPR compliance details.
✅ Met. See the Privacy Policy wiki page. Summary: Emutastic collects, stores, transmits, and sells no personal data; operates no servers; contains no telemetry, analytics, or crash reporting. The policy itemizes every outbound network connection (RetroAchievements, ScreenScraper, ArcadeDatabase, libretro buildbot, libretro thumbnails, GitHub Releases) with what is sent and what is received in each case.
-
Loading save states in hardcore mode — ✅ Blocked. See Section B and
RequestLoadinEmulatorWindow.xaml.cs. - Allowing rewind / slo-mo / frame advance in hardcore mode — ✅ N/A. These features do not exist in the codebase.
-
Allowing gameplay-altering cheats in hardcore mode — ✅ Blocked at the
ApplyAllCheatschokepoint. See Section B. -
Ability to switch to hardcore mode without a reset — ✅ Toggle not accessible during play; flag snapshotted at session start via
IsHardcoreActive(). -
Identifying the emulator to the server with a non-unique user agent — ✅ Unique
Emutastic/<version>prefix. See Section C andEmutasticUserAgent.cs. - Undisclosed history of the emulator identifying itself to RetroAchievements using another emulator's user agent — ✅ N/A. No such history.
- Shipping non-commercial cores while any commercialization or in-app purchases exists without written permission — ✅ N/A. Emutastic is fully free; no commercialization.
- Privacy policy with placeholders/contradictions — ✅ Met. See the Privacy Policy wiki page (Section F).
Afterplay: Shipped cheat tool to hardcore players, modified to bypass suspension.
➖ Not applicable. Emutastic is not on the banned list, has no history of shipping cheat tools to hardcore players, and has not been modified to bypass any RA enforcement action.
None of the items below block any auto-fail criterion. They are noted for completeness.
Section E recommends — but does not require — hardcore by default. Emutastic ships with hardcore as the default for fresh installs (as of v1.7.0). Existing users who had previously chosen softcore are not affected.
Quick lookup for the hardcore-specific code. All links are to the live main branch — verification against the most current state.
| Concern | Location | Symbol |
|---|---|---|
| Hardcore state helper | EmulatorWindow.xaml.cs |
IsHardcoreActive() |
| Hardcore session snapshot | EmulatorWindow.xaml.cs |
_raHardcoreActive, set inside InitRetroAchievements
|
| Save state load gate (chokepoint) | EmulatorWindow.xaml.cs |
RequestLoad |
| Save state load gate (startup path) | EmulatorWindow.xaml.cs |
_pendingLoadStatePath block |
| Save state load gate (defense-in-depth on picker) | EmulatorWindow.xaml.cs |
LoadStateBtn_Click |
| Cheat apply gate (chokepoint) | EmulatorWindow.xaml.cs |
ApplyAllCheats |
| Per-frame AR-write no-op (defense) | EmulatorWindow.xaml.cs |
ApplyFrontendArToRam early-return |
| HUD button visibility | EmulatorWindow.xaml.cs |
ApplyHardcoreHudVisibility |
| HARDCORE status indicator (markup) | EmulatorWindow.xaml |
HardcoreIndicator border |
| Hardcore toggle UI | PreferencesWindow.xaml |
RAHardcoreToggle |
| User-Agent builder | EmutasticUserAgent.cs |
Build() |
| User-Agent on rcheevos HTTP | RetroAchievementsClient.cs |
CreateRcheevosHttp |
| User-Agent on Web API HTTP | RetroAchievementsService.cs |
BuildHttp |
| Cheat-support classification | CheatSupport.cs |
Lookup, CheatSupportLevel
|
Console Notes
- Nintendo 64
- Nintendo 3DS
- GameCube
- Sega Saturn
- Dreamcast
- PlayStation
- PlayStation 2
- PlayStation Portable
- TurboGrafx-CD
- Neo Geo
- Arcade
- Vectrex
- Philips CD-i
- Atari Jaguar
- Tiger Game.com
Features
- EmuTV
- Artwork & Metadata
- Cheats
- Cloud Sync
- Disc-Based Systems
- Disk Swapping
- Portable Mode
- RetroAchievements
- ROM Hacks
- Hardcore Compliance
Technical
Platforms
Legal