Eden Resets Its Config After Every Crash: The Non-Atomic Write in Its C++ Source
Eden’s settings kept reverting to defaults, especially after I force-stopped the emulator or it crashed on its own. GPU driver, memory layout, language and region, all reset. I assumed Android had cleared the app data at some point, so I reinstalled twice and tried assorted background-keepalive tricks. A fresh install would hold for a while, then it happened again.
The real cause had nothing to do with Android. It was in Eden’s own source.
It is not “failed to save”, it is “truncated mid-save”
The config lives at files/config/config.ini. Eden saves settings by rewriting the entire file, and the problem is the mode it opens the file with:
void Config::WriteToIni() const {
fp = fopen(config_loc.c_str(), "wb"); // "wb" truncates the file to 0 on open
// ...
config->Save(writer, false); // then writes it back; a crash here leaves it partial
}
void Config::Reload() {
ReadValues(); // read the current config
SaveValues(); // immediately write it back
}Under POSIX, the "wb" mode truncates the file to zero bytes the moment it opens, then writes from scratch. So between the open and the last write, config.ini on disk is in a state where it has been emptied but not yet rewritten. If the process is killed or crashes inside that window, what remains is a partial or empty file. On the next launch, parsing fails and Eden falls back to its built-in defaults — which looks exactly like “the config reset itself.”
There is more. Reload() reads and immediately writes back on every launch, so a write window exists at every startup; a crash does not have to be rare to hit it. The write is not atomic (there is no write-to-temp-then-rename), and that is the root cause.
flowchart TB
A["Eden starts"] --> B["ReadValues reads config"]
B --> C["SaveValues rewrites<br/>fopen(wb) truncates to 0 first"]
C --> D{"Crash or force-stop during the write"}
D -->|yes| E["config.ini left partial"]
E --> F["Next launch fails to parse<br/>falls back to defaults = reset"]
D -->|no| G["Saved normally"]Once I understood this, keepalive and reinstalling were obviously symptomatic: any crash that lands in the write window resets the config.
A misdiagnosed problem: files pushed with adb that Eden cannot write
While digging, I also hit a trap that is easy to confuse with the first. Under /sdcard/Android/data/<package>/, files pushed with adb push are owned by shell, while Eden runs as a different uid. Eden reaches those files through the group/other permission bits:
644: Eden, as other, can read but has no write bit, so settings changed in the UI are never persisted.666: group/other can write, and only then can Eden save changes.
I initially misread “I changed a setting and it reverted” as “Eden rewrites my config”, when in fact the file simply could not be written. There is a counterintuitive detail here: reads were always fine; only writes were blocked, so the config loaded correctly and just never kept a new value. Overwriting config.ini directly with adb push is also a bad idea, since it replaces the owner with shell too. To edit in place, use adb shell "cat > file", which preserves the original owner.
The fix: freeze config.ini read-only
Since the truncation at crash time cannot be changed, the move is to remove Eden’s ability to rewrite the file at all. Keep config.ini owned by shell and set its mode to 444, read-only:
flowchart TB
A["config.ini<br/>shell owner + 444"] --> B["group/other can read"]
B --> C["Eden can read the config"]
A --> D["no write permission"]
D --> E["Eden cannot rewrite/truncate/corrupt it"]Eden still reads every setting, but it no longer has permission to open for writing, truncate, or corrupt the file; WriteToIni simply fails and the file stays as-is. I verified it by force-killing Eden three times in a row and hashing config.ini each time: the SHA256 was identical all three times, and the config stopped drifting. The trade-off is explicit — changing settings inside Eden’s UI no longer takes effect, because the write cannot land. That is precisely the mechanism that stops the resets. To change config, use the manual flow below.
The cost, and when not to freeze
The cost is “UI changes don’t save”, but that runs into one real exception: cheat and mod enabled/disabled states also live in the [DisabledAddOns] section of config.ini. If the nightly build is both the main build and the one where cheats get toggled often, a read-only freeze makes the toggles impossible to switch off in the Add-ons screen. So I ended up treating the builds differently: the stable build stays frozen at 444 to prevent resets, while nightly is unlocked to 666 so it can persist toggles — at the cost of losing reset protection, backed by regular backups. It is not a question of which is better; it depends on whether a given build fears losing config or needs to change settings.
A management script
To avoid retyping a long adb invocation each time, I wrapped it in eden_config.sh:
./eden_config.sh show # show current settings (language/region/driver/owner)
./eden_config.sh backup <name> # back up the current config (plus 53 per-game configs)
./eden_config.sh restore <name> # restore and re-lock read-only (golden by default)
./eden_config.sh freeze # freeze (read-only 444)
./eden_config.sh unfreeze # unlock (writable 666)
./eden_config.sh list # list backupsThe default package name can be switched to nightly with an environment variable:
EDEN_PKG=dev.eden.eden_emulator.nightly ./eden_config.sh freezeThe restore flow is worth noting because it stitches several traps together: am force-stop Eden, chmod 644 to unlock, write in place with cat > to preserve the shell owner, then chmod 444 to lock it back read-only and restart. A golden backup also lives on the device at /sdcard/eden_backup/golden/, holding the config and every per-game config for one-command rollback.
The correct flow for changing config
Because the file is owned by shell and read-only, the flow is fixed: unfreeze to unlock, write in place with adb shell "cat > file" to keep the owner, chmod 444 to lock it back, then restart Eden. Back up first so a bad edit can be restored.
In hindsight, “the config reset itself” was two separate layers stacked together: Eden’s own non-atomic write, and Android’s uid isolation creating a permissions problem. The first only became visible in the source; the second only makes sense through the permission model; and the fix turned out to be one chmod line that takes the write opportunity away from the app entirely.