<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Android on Time Friend</title><link>https://time-friend.com/zh/tags/android/</link><description>Recent content in Android on Time Friend</description><generator>Hugo</generator><language>zh-CN</language><lastBuildDate>Sun, 20 Sep 2026 10:00:00 +0000</lastBuildDate><atom:link href="https://time-friend.com/zh/tags/android/index.xml" rel="self" type="application/rss+xml"/><item><title>Eden 稳定版和 nightly 的存档为什么对不上: 用户档案 ID 与 UID 字节逆序</title><link>https://time-friend.com/zh/archive/eden-nightly-save-migration-profile-id/</link><pubDate>Sun, 20 Sep 2026 10:00:00 +0000</pubDate><guid>https://time-friend.com/zh/archive/eden-nightly-save-migration-profile-id/</guid><description>&lt;p&gt;我把 Eden 的主力版本从稳定版换成了 nightly。它和稳定版包名不同, 可以直接共存, 游戏也都能正常打开。但进去之后, 之前所有的存档一个都读不到, 每个游戏都像第一次玩。我第一反应是 nightly 把数据目录清了, 或者存档被覆盖了, 赶紧去翻文件。&lt;/p&gt;
&lt;p&gt;文件都在。少的不是存档, 是 nightly 去错了地方找。&lt;/p&gt;
&lt;h2 id="两个构建共用游戏-但不共用用户档案"&gt;两个构建共用游戏, 但不共用用户档案&lt;/h2&gt;
&lt;p&gt;Eden 把 Switch 的存档放在数据目录的 &lt;code&gt;nand/user/save/&lt;/code&gt; 下。这条路径不是只有一层, 它的结构大致是 &lt;code&gt;nand/user/save/0000000000000000/&amp;lt;用户档案ID&amp;gt;/&amp;lt;TitleID&amp;gt;/&lt;/code&gt;, 中间那层是&lt;strong&gt;用户档案&lt;/strong&gt;。稳定版和 nightly 是两个独立安装的应用, 各自的数据目录分开, 更关键的是: &lt;strong&gt;nightly 首次启动时会自己生成一个新的用户档案 ID&lt;/strong&gt;, 和稳定版那个没有任何关系。&lt;/p&gt;
&lt;p&gt;我这边稳定版的活动档案是 &lt;code&gt;D2AE7D815565D5C93F432C7C74A7DD6A&lt;/code&gt;, nightly 自动生成的是 &lt;code&gt;173CB8FB1F16348366B4CABB88A445D9&lt;/code&gt;。存档文件都在 nightly 的数据目录里(从稳定版拷过来之后), 但 nightly 只会去它自己那个活动档案目录里找游戏存档, 于是看不到。&lt;/p&gt;
&lt;h2 id="存档目录名从哪来"&gt;存档目录名从哪来&lt;/h2&gt;
&lt;p&gt;那个看起来像随机十六进制串的档案目录名, 其实不是随机的。日志里能观察到, 它来自用户档案的 UID, 并且是 UID 的&lt;strong&gt;字节逆序&lt;/strong&gt;排列——同一段身份数据, 换个字节序就成了目录名。两个构建只要各自生成了新 UID, 目录名就必然不同, 这也是为什么换构建、甚至 nightly 自己重新初始化之后, 存档会&amp;quot;对不上&amp;quot;。理解了这一点, 就不会再把它当成文件损坏。&lt;/p&gt;
&lt;h2 id="为什么直接拷存档会失效"&gt;为什么直接拷存档会失效&lt;/h2&gt;
&lt;p&gt;最容易踩的坑是: 把稳定版的 &lt;code&gt;save/&lt;/code&gt; 整个拷到 nightly 的 &lt;code&gt;save/&lt;/code&gt;, 以为就完事了。结果 nightly 里依然读不到。原因是稳定版拷贝过去的目录名是 &lt;code&gt;D2AE7D...&lt;/code&gt;, 而 nightly 的活动档案是 &lt;code&gt;173CB8FB...&lt;/code&gt;, 内容进了库, 但没进 nightly 真正会查的那个抽屉。所以迁移必须做&lt;strong&gt;两段覆盖&lt;/strong&gt;: 先把稳定版的存档根整体带过来, 再把稳定版活动档案里的内容, 再拷进 nightly 的活动档案目录。&lt;/p&gt;</description></item><item><title>Eden 模拟器配置每次崩溃就被重置: 从 C++ 源码的非原子写入讲起</title><link>https://time-friend.com/zh/archive/eden-config-reset-non-atomic-write/</link><pubDate>Sat, 05 Sep 2026 10:00:00 +0000</pubDate><guid>https://time-friend.com/zh/archive/eden-config-reset-non-atomic-write/</guid><description>&lt;p&gt;Eden 里的设置总是莫名其妙回到默认, 尤其在我把模拟器强杀、或者它自己崩溃之后。图形驱动、内存布局、语言区域, 全部复位。我一开始以为是 Android 什么时候把应用数据清了, 先后重装过两次, 还在手机上给它开了各种后台保活。重装能好一阵, 然后又犯。&lt;/p&gt;
&lt;p&gt;真正的原因和 Android 没关系, 在 Eden 自己的源码里。&lt;/p&gt;
&lt;h2 id="不是没保存-是保存到一半被截断"&gt;不是&amp;quot;没保存&amp;quot;, 是&amp;quot;保存到一半被截断&amp;quot;&lt;/h2&gt;
&lt;p&gt;配置存在 &lt;code&gt;files/config/config.ini&lt;/code&gt;。Eden 保存设置的写法, 是把整个文件重新写一遍。问题在打开文件的模式上:&lt;/p&gt;


&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-cpp" data-lang="cpp"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;void&lt;/span&gt; Config&lt;span style="color:#f92672"&gt;::&lt;/span&gt;WriteToIni() &lt;span style="color:#66d9ef"&gt;const&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; fp &lt;span style="color:#f92672"&gt;=&lt;/span&gt; fopen(config_loc.c_str(), &lt;span style="color:#e6db74"&gt;&amp;#34;wb&amp;#34;&lt;/span&gt;); &lt;span style="color:#75715e"&gt;// &amp;#34;wb&amp;#34; 一打开就把文件截断为 0
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// ...
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; config&lt;span style="color:#f92672"&gt;-&amp;gt;&lt;/span&gt;Save(writer, false); &lt;span style="color:#75715e"&gt;// 再逐项写入；若此刻崩溃 → 文件残缺
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;void&lt;/span&gt; Config&lt;span style="color:#f92672"&gt;::&lt;/span&gt;Reload() {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; ReadValues(); &lt;span style="color:#75715e"&gt;// 读现有配置
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; SaveValues(); &lt;span style="color:#75715e"&gt;// 立刻又写回去
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;&lt;code&gt;&amp;quot;wb&amp;quot;&lt;/code&gt; 这个模式在 POSIX 里的语义是: 打开即把文件截断成 0 字节, 然后从零开始写。也就是说, 从打开到写完的这段时间里, 磁盘上的 &lt;code&gt;config.ini&lt;/code&gt; 处于&amp;quot;已经被清空、还没写回来&amp;quot;的状态。如果在这段窗口里进程被强杀或崩溃, 留下的就是一个残缺或空白的文件。下一次启动去解析它, 解析失败, Eden 就回退到内置默认值——看到的现象就是&amp;quot;配置被重置&amp;quot;。&lt;/p&gt;
&lt;p&gt;这还没完。&lt;code&gt;Reload()&lt;/code&gt; 每次启动都会先读、再立刻写回去。也就是说每次启动都有一个这样的写入窗口, 崩溃并不需要多罕见。写配置本身不是原子的(没有先写临时文件再 &lt;code&gt;rename&lt;/code&gt;), 这是根因。&lt;/p&gt;</description></item></channel></rss>