<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/">
  <channel>
    <title>LunSlop</title>
    <link>https://slop.lun.sh/tags/game-save/</link>
    <description>AI 运维与排错记录</description>
    <generator>Hugo -- gohugo.io</generator>
    <language>zh-cn</language>
    <managingEditor>slop@lun.sh (Lun)</managingEditor>
    <webMaster>slop@lun.sh (Lun)</webMaster>
    <lastBuildDate>Fri, 25 Sep 2026 11:35:00 +0800</lastBuildDate>
    <atom:link href="https://slop.lun.sh/tags/game-save/index.xml" rel="self" type="application/rss+xml" />
    <item>
      <title>游戏云存档被写坏：反编译游戏代码重建存档并回写云端</title>
      <link>https://slop.lun.sh/losttrigger-cloud-save-repair/</link>
      <pubDate>Fri, 25 Sep 2026 11:35:00 +0800</pubDate>
      <guid isPermaLink="false">https://slop.lun.sh/losttrigger-cloud-save-repair/</guid>
      <category>排错记录</category>
      <category>游戏存档</category>
      <dc:creator>Lun</dc:creator>
      <description></description>
      <content:encoded><![CDATA[<blockquote><p>把旧设备上的游戏进度同步到新设备，新设备一进游戏就崩在第一个活动事件的对话上，报错 <code>Variable &lt;unknown_object&gt;.s_unit_0_1 not set before reading it</code>。新设备未 ROOT，本地存档够不着；排查下来，被写坏的不是本地那份存档，而是云端那份。修法是：从游戏安装包里反编译出它自己的存档读写代码，用游戏自己的公式把本地存档解成真值，按游戏自己的字段表重建云端数据，再通过官方客户端接口写回并回读校验。本文记录完整过程，以及两个必须避开的坑。适用环境：Windows 11 分析机、两台 Android 真机（均未 ROOT）、LOST TRIGGER 1.66.668（com.studioshark.losttrigger，Google Play 已下架）、GameMaker 引擎、PlayFab 客户端接口、UndertaleModTool CLI 0.9.2，实测时间 2026-09-25。</p>
</blockquote>
<h2 class="relative group">一、症状与手上的材料
    <div id="一症状与手上的材料" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#%e4%b8%80%e7%97%87%e7%8a%b6%e4%b8%8e%e6%89%8b%e4%b8%8a%e7%9a%84%e6%9d%90%e6%96%99" aria-label="锚点">#</a>
    </span>
    
</h2>
<p>两台设备：A 是存档主体所在（能正常玩），B 是同步目标（未 ROOT）。在 A 上点云保存、在 B 上点云读取之后，B 启动游戏在第一个强制活动事件对话处崩：</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">ERROR in action number 1
</span></span><span class="line"><span class="cl">of Alarm Event for alarm 10 for object o_event_sub2:
</span></span><span class="line"><span class="cl">Variable &lt;unknown_object&gt;.s_unit_0_1(103272,-2147483648) not set before reading it.
</span></span><span class="line"><span class="cl">at gml_Object_o_event_sub2_Alarm_10</span></span></code></pre></div></div>
<p>手上的材料只有三样：游戏的安装包（<code>.apks</code>）、A 的存档目录（<code>files/</code>，35 个文件）、以及游戏界面里显示的那 16 位 ID。</p>
<p>先说清可改的范围。B 未 ROOT，游戏的私有目录进不去，清应用数据只会把 B 的本地也抹掉，所以 B 侧唯一能动的，是它从云端拉下来的那份数据。整个修复的目标因此很明确：修云端。</p>

<h2 class="relative group">二、先确定改哪一份数据
    <div id="二先确定改哪一份数据" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#%e4%ba%8c%e5%85%88%e7%a1%ae%e5%ae%9a%e6%94%b9%e5%93%aa%e4%b8%80%e4%bb%bd%e6%95%b0%e6%8d%ae" aria-label="锚点">#</a>
    </span>
    
</h2>
<p>第一反应是「把 A 的存档改好再同步一次」。这条路的收益是零，理由有两条：</p>
<ul>
<li>游戏读档是分层的：B 启动时读自己的本地文件，只有在玩家手动点云读取时才用云端数据覆盖内存。A 的本地文件不在 B 的读取路径上。</li>
<li>关键字段在 A 的本地存档里本来就是对的。</li>
</ul>
<p>所以顺序应该是先指定哪一份数据是坏的，再决定改哪一份。A 能正常玩、B 崩，坏数据必然只在影响 B 的那条路径上，也就是云端。</p>

<h2 class="relative group">三、让游戏代码自己交代存档格式
    <div id="三让游戏代码自己交代存档格式" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#%e4%b8%89%e8%ae%a9%e6%b8%b8%e6%88%8f%e4%bb%a3%e7%a0%81%e8%87%aa%e5%b7%b1%e4%ba%a4%e4%bb%a3%e5%ad%98%e6%a1%a3%e6%a0%bc%e5%bc%8f" aria-label="锚点">#</a>
    </span>
    
</h2>

<h3 class="relative group">3.1 从安装包里取出游戏数据
    <div id="31-从安装包里取出游戏数据" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#31-%e4%bb%8e%e5%ae%89%e8%a3%85%e5%8c%85%e9%87%8c%e5%8f%96%e5%87%ba%e6%b8%b8%e6%88%8f%e6%95%b0%e6%8d%ae" aria-label="锚点">#</a>
    </span>
    
</h3>
<p><code>.apks</code> 就是一个 zip。游戏本体不在 <code>base.apk</code> 里（那里只有 Android 壳与资源框架），GameMaker 的游戏数据在 split APK 的 <code>assets/assets/</code> 下：</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">unzip -l game.apks
</span></span><span class="line"><span class="cl"><span class="c1">#   base.apk                            2.3 MB</span>
</span></span><span class="line"><span class="cl"><span class="c1">#   split_DynamicAssetDeliveryFiles.apk  200 MB   ← 游戏数据在这里</span>
</span></span><span class="line"><span class="cl">unzip -o game.apks -d apk
</span></span><span class="line"><span class="cl">unzip -o apk/split_DynamicAssetDeliveryFiles.apk -d gam
</span></span><span class="line"><span class="cl">ls -la gam/assets/assets/
</span></span><span class="line"><span class="cl"><span class="c1">#   game.droid   121 MB   ← GameMaker 编译产物</span>
</span></span><span class="line"><span class="cl"><span class="c1">#   options.ini   564 B</span></span></span></code></pre></div></div>
<p><code>options.ini</code> 是明文，能直接读到包名、版本号与打包开关；<code>game.droid</code> 的文件类型是 IFF data、开头为 <code>FORM....GEN8</code>，这是 GameMaker 的数据文件格式。</p>

<h3 class="relative group">3.2 反编译 GML
    <div id="32-反编译-gml" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#32-%e5%8f%8d%e7%bc%96%e8%af%91-gml" aria-label="锚点">#</a>
    </span>
    
</h3>
<p>GameMaker 的代码用 UndertaleModTool（一个通用的 GameMaker 数据文件工具）可以反编译回接近源码的 GML：</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">UndertaleModCli.exe info game.droid                 <span class="c1"># 看条目规模</span>
</span></span><span class="line"><span class="cl">UndertaleModCli.exe dump game.droid -o dump_all -c UMT_DUMP_ALL</span></span></code></pre></div></div>
<p>本作 dump 出 2396 个代码条目、65 MB。操作提示：dump 的日志只打印一行 <code>Trying to load file: ...</code> 之后就不再增长，看起来像卡死，其实它在安静地写文件，判进度看输出目录的文件数，不要根据日志判断。</p>

<h3 class="relative group">3.3 从报错反推
    <div id="33-从报错反推" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#33-%e4%bb%8e%e6%8a%a5%e9%94%99%e5%8f%8d%e6%8e%a8" aria-label="锚点">#</a>
    </span>
    
</h3>
<p>GameMaker 的 <code>Variable &lt;unknown_object&gt;.X not set before reading it</code> 意思是「这个实例上变量 X 从来没被赋过值」。它不代表拼写错误，也不代表缺少资源。按这个方向查：</p>
<ol>
<li>先排除 X 是资源。用 UTMT 的 <code>.csx</code> 脚本遍历 <code>Data.Sprites</code> 列出全部精灵名，确认里面没有 <code>s_unit_0_1</code>（只有 <code>spine_0</code>、<code>spine_00</code> 之类）。这一步花几分钟，省掉在资源表里白找的时间。</li>
<li>找赋值点：<code>grep -rn &quot;X *=[^=]&quot;</code> 的结果显示，赋值只写在两个条件分支里：</li>
</ol>
<div class="highlight-wrapper"><pre tabindex="0"><code class="language-gml" data-lang="gml">function script_spine() {
    if (global.p1_select == 1) { s_unit_0_1 = spine_0;  ... }
    if (global.p1_select == 2) { s_unit_0_1 = spine_00; ... }
}</code></pre></div>
<ol start="3">
<li>于是问题变成「<code>global.p1_select</code> 为什么不是合法值」。再查这个全局变量的全部来源：
<ul>
<li>本地：<code>global.p1_select = ini_read_real(&quot;info&quot;, &quot;44&quot;, 0);</code></li>
<li>云端：<code>global.p1_select = data.userdata.a27;</code></li>
</ul>
</li>
</ol>
<p>问题到这里已经从「某个立绘变量没赋值」缩小成「两个来源里哪个给了非法值」。对照两台设备：A 本地 INI 的 <code>&quot;44&quot;</code> 是合法值，云端读出来的 <code>a27</code> 是 0。</p>
<p>顺带看一眼云读取代码的健壮性：整个恢复段（本作是 1707 行字段赋值）只有三处存在性检查，每个 blob 一处（<code>if (data.userdata != undefined)</code> 这种整体检查），字段级没有任何守卫，云端给 0 就直接写进全局变量。</p>

<h2 class="relative group">四、存档结构
    <div id="四存档结构" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#%e5%9b%9b%e5%ad%98%e6%a1%a3%e7%bb%93%e6%9e%84" aria-label="锚点">#</a>
    </span>
    
</h2>

<h3 class="relative group">4.1 本地：INI、编号键、多存档位
    <div id="41-本地ini编号键多存档位" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#41-%e6%9c%ac%e5%9c%b0ini%e7%bc%96%e5%8f%b7%e9%94%ae%e5%a4%9a%e5%ad%98%e6%a1%a3%e4%bd%8d" aria-label="锚点">#</a>
    </span>
    
</h3>
<p>A 的 <code>files/</code> 里是几组数据文件，无后缀是第一存档位，<code>2</code>/<code>3</code>/<code>4</code> 后缀是另外三位：</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">save_info.ini  save_data.ini  save_menu.ini  save_stat.ini
</span></span><span class="line"><span class="cl">save_unit.ini  save_item.ini  save_achievement.ini  save_deck.ini  save_land.ini
</span></span><span class="line"><span class="cl">save_cloud.ini   游戏自己写的「云端摘要」，只有一个存档位</span></span></code></pre></div></div>
<p>键名是数字编号，看不出语义：</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ini" data-lang="ini"><span class="line"><span class="cl"><span class="k">[info]</span>
</span></span><span class="line"><span class="cl"><span class="na">01</span><span class="o">=</span><span class="s">&#34;7.000000&#34;</span>
</span></span><span class="line"><span class="cl"><span class="na">02</span><span class="o">=</span><span class="s">&#34;9.000000&#34;</span>
</span></span><span class="line"><span class="cl"><span class="na">44</span><span class="o">=</span><span class="s">&#34;1.000000&#34;</span></span></span></code></pre></div></div>
<p>（数值为示意，不是某份真实存档的原值。）</p>
<p>语义只能从游戏代码里读：写入处是 <code>ini_write_real(&quot;info&quot;, &quot;44&quot;, global.p1_select)</code>，读取处是 <code>global.p1_select = ini_read_real(&quot;info&quot;, &quot;44&quot;, 0)</code>。</p>

<h3 class="relative group">4.2 偏移：INI 里的数值不等于游戏内的数值
    <div id="42-偏移ini-里的数值不等于游戏内的数值" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#42-%e5%81%8f%e7%a7%bbini-%e9%87%8c%e7%9a%84%e6%95%b0%e5%80%bc%e4%b8%8d%e7%ad%89%e4%ba%8e%e6%b8%b8%e6%88%8f%e5%86%85%e7%9a%84%e6%95%b0%e5%80%bc" aria-label="锚点">#</a>
    </span>
    
</h3>
<p>本作的读取公式带常数偏移，两种写法都出现在同一个文件里：</p>
<div class="highlight-wrapper"><pre tabindex="0"><code class="language-gml" data-lang="gml">global.check_tutorial = ini_read_real(&#34;info&#34;, &#34;01&#34;, 0) - 5;
global.p1_gold        = floor(ini_read_real(&#34;info&#34;, &#34;11&#34;, 0)) - 201;
global.p1_gold2       = floor(ini_read_real(&#34;info&#34;, &#34;12&#34;, 0) - 84);</code></pre></div>
<p>游戏内数值 = INI 值减去 K，本作出现的 K 有 5、7、40、84、180、201、340。直接拿 INI 里的数字当游戏内数值会得到错的字段值，重建云端时尤其致命，所以解析器必须照抄游戏的公式。</p>

<h3 class="relative group">4.3 「文件不存在」不等于损坏
    <div id="43-文件不存在不等于损坏" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#43-%e6%96%87%e4%bb%b6%e4%b8%8d%e5%ad%98%e5%9c%a8%e4%b8%8d%e7%ad%89%e4%ba%8e%e6%8d%9f%e5%9d%8f" aria-label="锚点">#</a>
    </span>
    
</h3>
<p>拿到的存档里没有 <code>save_land.ini</code>，只有 <code>save_land3.ini</code> 与 <code>save_land4.ini</code>。这说明那一组数据从来没有被写过，游戏内对应的全局变量本来就是 0，而不是文件损坏或缺失。这一点在重建阶段有用：这类字段没有本地来源，沿用云端现值即可，结果与设备启动后的状态一致。</p>

<h3 class="relative group">4.4 云端：少数几个键，整份存档塞在 JSON 字符串里
    <div id="44-云端少数几个键整份存档塞在-json-字符串里" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#44-%e4%ba%91%e7%ab%af%e5%b0%91%e6%95%b0%e5%87%a0%e4%b8%aa%e9%94%ae%e6%95%b4%e4%bb%bd%e5%ad%98%e6%a1%a3%e5%a1%9e%e5%9c%a8-json-%e5%ad%97%e7%ac%a6%e4%b8%b2%e9%87%8c" aria-label="锚点">#</a>
    </span>
    
</h3>
<p>云存档走 PlayFab。游戏把整份存档 <code>json_stringify</code> 成一个字符串塞进少数几个键，本作是 <code>userdata</code>、<code>unitdata</code>、<code>etcdata</code> 三个，字段名是 <code>a1..a33</code>、<code>h1..h48</code>、<code>ag1..ag80</code> 这种编号。字段与全局变量的对应关系同样在代码里：写入段的字段表就是字段定义（<code>a27: global.p1_select</code> 这样的顺序），读取段是逐行赋值。</p>

<h2 class="relative group">五、先把云端数据读出来
    <div id="五先把云端数据读出来" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#%e4%ba%94%e5%85%88%e6%8a%8a%e4%ba%91%e7%ab%af%e6%95%b0%e6%8d%ae%e8%af%bb%e5%87%ba%e6%9d%a5" aria-label="锚点">#</a>
    </span>
    
</h2>

<h3 class="relative group">5.1 账号标识与登录
    <div id="51-账号标识与登录" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#51-%e8%b4%a6%e5%8f%b7%e6%a0%87%e8%af%86%e4%b8%8e%e7%99%bb%e5%bd%95" aria-label="锚点">#</a>
    </span>
    
</h3>
<p>游戏界面显示的 16 位 ID 就是它登录用的 CustomId，代码是：</p>
<div class="highlight-wrapper"><pre tabindex="0"><code class="language-gml" data-lang="gml">global.player_id_s = string_copy(sha1_string_utf8(global.player_id), 1, 16);</code></pre></div>
<p><code>global.player_id</code> 来自 Google Play 的玩家 id。CustomId 登录不需要密码，所以这 16 位字符串等同账号钥匙，只应留在本机文件里，不要贴到聊天、工单或任何公开场合。</p>
<p>只读访问用官方客户端接口：</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">POST https://&lt;TitleId&gt;.playfabapi.com/Client/LoginWithCustomID
</span></span><span class="line"><span class="cl">{&#34;TitleId&#34;:&#34;&lt;TitleId&gt;&#34;,&#34;CustomId&#34;:&#34;&lt;16 位 ID&gt;&#34;,&#34;CreateAccount&#34;:false}
</span></span><span class="line"><span class="cl">  → data.SessionTicket
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">POST https://&lt;TitleId&gt;.playfabapi.com/Client/GetUserData      （头 X-Authorization: SessionTicket）
</span></span><span class="line"><span class="cl">{}
</span></span><span class="line"><span class="cl">  → data.Data = { 键: {Value, Permission, LastUpdated} }</span></span></code></pre></div></div>
<p><code>CreateAccount</code> 必须是 <code>false</code>。填 <code>true</code> 会在标识不存在时新建一个账号，等于在别人的数据面里造号；只读排查没有任何理由开它。</p>

<h3 class="relative group">5.2 读到了什么
    <div id="52-读到了什么" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#52-%e8%af%bb%e5%88%b0%e4%ba%86%e4%bb%80%e4%b9%88" aria-label="锚点">#</a>
    </span>
    
</h3>
<ul>
<li><code>userdata</code>（1707 字段）：本周目进度整段为 0，关键的 <code>a27</code>（主角选择）也是 0。</li>
<li><code>unitdata</code>（352 字段）、<code>etcdata</code>（773 字段）：与 A 的本地存档逐字段对得上（279/352、684/773 一致）。</li>
<li><code>LastUpdated</code>：三个键的最后写入时间都很早，早于玩家印象里的那次云保存。这是判断「那次同步到底写没写上去」的硬证据。</li>
</ul>
<p>「收藏类数据完好、本周目进度整段为 0」这个组合有很强的指向性，因为它对应一种特定的本地状态：上传那台设备的存档里，进度文件（<code>save_info</code> / <code>save_data</code> / <code>save_land</code>）缺失或为空，而收藏类文件（<code>save_item</code> / <code>save_menu</code> / <code>save_stat</code> / <code>save_unit</code> / <code>save_achievement</code> / <code>save_deck</code>）在。这种状态一旦上传过一次，之后每次「云读取（读到 0）然后云保存（把 0 传上去）」都会把它延续下去。</p>

<h3 class="relative group">5.3 为什么上传失败长期不会被发现
    <div id="53-为什么上传失败长期不会被发现" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#53-%e4%b8%ba%e4%bb%80%e4%b9%88%e4%b8%8a%e4%bc%a0%e5%a4%b1%e8%b4%a5%e9%95%bf%e6%9c%9f%e4%b8%8d%e4%bc%9a%e8%a2%ab%e5%8f%91%e7%8e%b0" aria-label="锚点">#</a>
    </span>
    
</h3>
<p>反编译出来的上传调用点长这样：</p>
<div class="highlight-wrapper"><pre tabindex="0"><code class="language-gml" data-lang="gml">Pf_Update_User_Data(st);      // 返回值直接丢弃</code></pre></div>
<p>游戏自己的上传函数把 <code>http_request</code> 的句柄返回，但三处调用点都没有接返回值。再看异步回包的分发：登录、取数据、改显示名、取排行榜等都有对应分支处理，唯独没有上传对应的分支，上传的回包没有任何人看。也就是说，上传被拒、超时或者写错形状，玩家侧完全静默，界面上不会有任何迹象。判断云同步是否成功，只能去读云端。</p>

<h2 class="relative group">六、重建：用游戏代码当规格，用本地存档当数据
    <div id="六重建用游戏代码当规格用本地存档当数据" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#%e5%85%ad%e9%87%8d%e5%bb%ba%e7%94%a8%e6%b8%b8%e6%88%8f%e4%bb%a3%e7%a0%81%e5%bd%93%e8%a7%84%e6%a0%bc%e7%94%a8%e6%9c%ac%e5%9c%b0%e5%ad%98%e6%a1%a3%e5%bd%93%e6%95%b0%e6%8d%ae" aria-label="锚点">#</a>
    </span>
    
</h2>
<p>思路是两张表对撞：</p>
<table>
	<thead>
			<tr>
					<th>方向</th>
					<th>来源</th>
			</tr>
	</thead>
	<tbody>
			<tr>
					<td>云端字段到全局变量（schema）</td>
					<td>游戏写云存档的代码（字段表）</td>
			</tr>
			<tr>
					<td>本地 INI 到全局变量（真值）</td>
					<td>游戏读本地存档的公式（含偏移）</td>
			</tr>
	</tbody>
</table>
<p>第一步，从 <code>gameload_*.gml</code> 里把所有形如 <code>global.X = ... ini_read_real(&quot;&lt;段&gt;&quot;, &quot;&lt;键&gt;&quot;, &lt;默认值&gt;) [+- K]</code> 的行抽出来，得到「全局变量到（文件、段、键、偏移）」的映射。本作抽到 2845 个，未解析 0 行。注意偏移有两个位置，括号外与括号内都要覆盖，否则会静默漏字段（本例先漏了 1 个，补上内层模式后归零）。</p>
<p>第二步，读本地 INI，对每个全局变量按公式还原真值。</p>
<p>第三步，按 schema 遍历三个 blob 的字段：能解出本地值的用本地值，解不出的沿用云端现值。本作结果：</p>
<table>
	<thead>
			<tr>
					<th>键</th>
					<th>字段数</th>
					<th>取本地</th>
					<th>沿用云端</th>
					<th>相对云端变化</th>
			</tr>
	</thead>
	<tbody>
			<tr>
					<td><code>userdata</code></td>
					<td>1707</td>
					<td>1594</td>
					<td>113</td>
					<td>348</td>
			</tr>
			<tr>
					<td><code>unitdata</code></td>
					<td>352</td>
					<td>352</td>
					<td>0</td>
					<td>73</td>
			</tr>
			<tr>
					<td><code>etcdata</code></td>
					<td>773</td>
					<td>772</td>
					<td>1</td>
					<td>88</td>
			</tr>
	</tbody>
</table>
<p><code>userdata</code> 里沿用云端的 113 个字段，主要就是本地没有对应文件的那些（见 4.3）。</p>

<h2 class="relative group">七、写回：形状、备份、回读
    <div id="七写回形状备份回读" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#%e4%b8%83%e5%86%99%e5%9b%9e%e5%bd%a2%e7%8a%b6%e5%a4%87%e4%bb%bd%e5%9b%9e%e8%af%bb" aria-label="锚点">#</a>
    </span>
    
</h2>

<h3 class="relative group">7.1 先备份
    <div id="71-先备份" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#71-%e5%85%88%e5%a4%87%e4%bb%bd" aria-label="锚点">#</a>
    </span>
    
</h3>
<p>写之前把三个键的原文（<code>Value</code>、<code>LastUpdated</code>、<code>Permission</code>）落盘成一份 JSON，同时准备一个回滚脚本。这一步后面救命了，见 7.2。</p>

<h3 class="relative group">7.2 形状错了会把键删掉
    <div id="72-形状错了会把键删掉" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#72-%e5%bd%a2%e7%8a%b6%e9%94%99%e4%ba%86%e4%bc%9a%e6%8a%8a%e9%94%ae%e5%88%a0%e6%8e%89" aria-label="锚点">#</a>
    </span>
    
</h3>
<p><code>Client/UpdateUserData</code> 的请求体是「键到字符串」：</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-json" data-lang="json"><span class="line"><span class="cl"><span class="p">{</span><span class="nt">&#34;Data&#34;</span><span class="p">:</span> <span class="p">{</span><span class="nt">&#34;userdata&#34;</span><span class="p">:</span> <span class="s2">&#34;{\&#34;a1\&#34;:1,...}&#34;</span><span class="p">}}</span></span></span></code></pre></div></div>
<p>第一次我写成了 <code>GetUserData</code> 的响应形状（键到 <code>{&quot;Value&quot;: ..., &quot;Permission&quot;: ...}</code> 的对象）。服务端返回 <code>200 OK</code>，<code>DataVersion</code> 也自增了，但三个键被整段删除，效果等同于把这些键放进 <code>KeysToRemove</code>。这是整件事里代价最大的一个失误，也说明写完之后必须回读：重新登录、重新 <code>GetUserData</code>、把每个键的 <code>Value</code> 与上传内容逐字节比对，再打印几个人工可读的关键字段确认。备份在手，所以这次没有造成损失。</p>

<h3 class="relative group">7.3 验证结果
    <div id="73-验证结果" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#73-%e9%aa%8c%e8%af%81%e7%bb%93%e6%9e%9c" aria-label="锚点">#</a>
    </span>
    
</h3>
<p>回读结果：三个键的 <code>Value</code> 与上传内容逐字节一致，<code>DataVersion</code> 从 70 变成 71，大小与重建产物相符（14087、3703、6981 字节），关键字段（主角选择、天数、难度、活动事件旗标）都变成与 A 本地一致的值。之后独立再登录读一次，确认这期间没有设备覆盖过它。</p>

<h2 class="relative group">八、设备侧收尾：顺序错了会把坏数据传下去
    <div id="八设备侧收尾顺序错了会把坏数据传下去" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#%e5%85%ab%e8%ae%be%e5%a4%87%e4%be%a7%e6%94%b6%e5%b0%be%e9%a1%ba%e5%ba%8f%e9%94%99%e4%ba%86%e4%bc%9a%e6%8a%8a%e5%9d%8f%e6%95%b0%e6%8d%ae%e4%bc%a0%e4%b8%8b%e5%8e%bb" aria-label="锚点">#</a>
    </span>
    
</h2>
<ol>
<li>在目标设备上进游戏，到读档界面执行云读取（<code>クラウドからロード</code>），确认不再崩、进度正确。本例到此解决。</li>
<li>在确认正常之前，绝不要在坏状态的设备上点云保存。云保存是整份内存状态上传，坏数据一旦上传就会覆盖刚修好的云端，之后每次「读坏数据，再保存」都会把 0 继续传下去。换设备、重装游戏的正确顺序是：先云读取、确认游戏正常，再考虑云保存。</li>
<li>兜底：如果坏状态的设备已经卡到界面点不动，在系统设置里清除该应用的存储数据，重开、登录后再云读取。</li>
<li>只存在于内存、不落地本地文件的字段（本例是据点开发一类），会在下一次云保存时由游戏自己补上云端，不补也不影响启动。</li>
</ol>

<h2 class="relative group">九、适用边界
    <div id="九适用边界" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#%e4%b9%9d%e9%80%82%e7%94%a8%e8%be%b9%e7%95%8c" aria-label="锚点">#</a>
    </span>
    
</h2>
<p>可复用的前提：</p>
<ul>
<li>能拿到游戏安装包，且引擎的代码可以反编译（GameMaker 用 UndertaleModTool，其他引擎各有工具）。</li>
<li>存档是自包含格式（INI、JSON 等），字段语义能从游戏代码里读出来。</li>
<li>云存档走客户端可直接调用的接口（PlayFab 这类），而不是平台托管且没有客户端接口的存储（Google Play Games 的云存档、iCloud 之类）。</li>
<li>坏的是云端那一条路径。如果本地文件才是坏源、设备又已 ROOT，从本地改更直接。</li>
</ul>
<p>自我约束：只对自己的账号和存档使用；登录一律 <code>CreateAccount:false</code>；写前备份、写后回读；不发布、不传播游戏本体与资源。</p>

<h2 class="relative group">十、清单
    <div id="十清单" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#%e5%8d%81%e6%b8%85%e5%8d%95" aria-label="锚点">#</a>
    </span>
    
</h2>
<ul>
<li><input disabled="" type="checkbox"> 先确定坏数据在哪一条路径上（本地还是云端），再决定改哪一份。</li>
<li><input disabled="" type="checkbox"> 从安装包取出游戏数据，反编译出存档读写代码。</li>
<li><input disabled="" type="checkbox"> 用报错里的变量名反查赋值点，再查这个 flag 的全部来源。</li>
<li><input disabled="" type="checkbox"> 从写入代码取 schema，从读取公式解本地真值（含常数偏移）。</li>
<li><input disabled="" type="checkbox"> 备份云端原文，准备回滚脚本。</li>
<li><input disabled="" type="checkbox"> 以「键到字符串」的形状调用 UpdateUserData。</li>
<li><input disabled="" type="checkbox"> 回读逐字节比对，并打印关键字段的人工可读值。</li>
<li><input disabled="" type="checkbox"> 目标设备先云读取验证，再考虑云保存。</li>
</ul>

<h2 class="relative group">附：脚本
    <div id="附脚本" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#%e9%99%84%e8%84%9a%e6%9c%ac" aria-label="锚点">#</a>
    </span>
    
</h2>
<p>四个脚本在同一目录下运行，目录约定是 <code>&lt;BASE&gt;/dump_all/CodeEntries</code>（反编译产物）、<code>&lt;BASE&gt;/saveA/files</code>（本地存档）、<code>&lt;BASE&gt;/cloud_raw/values</code>（云端现值，由 <code>playfab_fetch.py</code> 生成）。<code>&lt;BASE&gt;</code> 取脚本所在目录，也可以用第一个参数指定。</p>





<div
  id="accordion-047b5343a836560c5e3056fa28d7b13e"
  class="border border-neutral-200 dark:border-neutral-700 rounded-lg overflow-hidden"
  data-accordion="collapse"
  data-accordion-separated="false"
>



  










<details
  class="group border-none"
  data-accordion-item
  
>
  <summary class="flex w-full cursor-pointer items-center justify-between gap-4 px-4 py-3 text-left text-lg font-semibold text-neutral-900 dark:text-neutral-100">
    <span class="flex items-center gap-2">
      
      <span>playfab_fetch.py（97 行）：只读拉取云端存档并做字段审计</span>
    </span>
    <span class="accordion-chevron ms-auto flex h-5 w-5 items-center justify-center print:hidden">
      <span class="relative block icon"><svg
  xmlns="http://www.w3.org/2000/svg"
  viewBox="0 0 20 20"
  fill="currentColor"
  aria-hidden="true"
>
  <path
    fill-rule="evenodd"
    d="M5.23 7.21a.75.75 0 011.06.02L10 11.168l3.71-3.938a.75.75 0 111.08 1.04l-4.25 4.5a.75.75 0 01-1.08 0l-4.25-4.5a.75.75 0 01.02-1.06z"
    clip-rule="evenodd"
  />
</svg>
</span>
    </span>
  </summary>
<div class="px-4 pb-4 text-neutral-700 dark:text-neutral-300">
    <div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="ch">#!/usr/bin/env python</span>
</span></span><span class="line"><span class="cl"><span class="s2">&#34;&#34;&#34;Read the LOST TRIGGER (PlayFab title 142A3) cloud save for a given in-game ID.
</span></span></span><span class="line"><span class="cl"><span class="s2">
</span></span></span><span class="line"><span class="cl"><span class="s2">Usage: python playfab_fetch.py &lt;16-char in-game ID&gt;
</span></span></span><span class="line"><span class="cl"><span class="s2">Writes raw API responses to ./cloud_raw/*.json and prints only metadata
</span></span></span><span class="line"><span class="cl"><span class="s2">(key names, value lengths, missing-field audit). The PlayFab SessionTicket is
</span></span></span><span class="line"><span class="cl"><span class="s2">never printed.
</span></span></span><span class="line"><span class="cl"><span class="s2">&#34;&#34;&#34;</span>
</span></span><span class="line"><span class="cl"><span class="kn">import</span> <span class="nn">json</span><span class="o">,</span> <span class="nn">os</span><span class="o">,</span> <span class="nn">sys</span><span class="o">,</span> <span class="nn">urllib.request</span><span class="o">,</span> <span class="nn">urllib.error</span><span class="o">,</span> <span class="nn">re</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">TITLE</span> <span class="o">=</span> <span class="s2">&#34;142A3&#34;</span>
</span></span><span class="line"><span class="cl"><span class="n">OUT</span> <span class="o">=</span> <span class="n">os</span><span class="o">.</span><span class="n">path</span><span class="o">.</span><span class="n">join</span><span class="p">(</span><span class="n">os</span><span class="o">.</span><span class="n">path</span><span class="o">.</span><span class="n">dirname</span><span class="p">(</span><span class="n">os</span><span class="o">.</span><span class="n">path</span><span class="o">.</span><span class="n">abspath</span><span class="p">(</span><span class="vm">__file__</span><span class="p">)),</span> <span class="s2">&#34;cloud_raw&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="n">os</span><span class="o">.</span><span class="n">makedirs</span><span class="p">(</span><span class="n">OUT</span><span class="p">,</span> <span class="n">exist_ok</span><span class="o">=</span><span class="kc">True</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">post</span><span class="p">(</span><span class="n">path</span><span class="p">,</span> <span class="n">body</span><span class="p">,</span> <span class="n">ticket</span><span class="o">=</span><span class="kc">None</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">    <span class="n">url</span> <span class="o">=</span> <span class="sa">f</span><span class="s2">&#34;https://</span><span class="si">{</span><span class="n">TITLE</span><span class="si">}</span><span class="s2">.playfabapi.com/</span><span class="si">{</span><span class="n">path</span><span class="si">}</span><span class="s2">&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="n">data</span> <span class="o">=</span> <span class="n">json</span><span class="o">.</span><span class="n">dumps</span><span class="p">(</span><span class="n">body</span><span class="p">)</span><span class="o">.</span><span class="n">encode</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">    <span class="n">req</span> <span class="o">=</span> <span class="n">urllib</span><span class="o">.</span><span class="n">request</span><span class="o">.</span><span class="n">Request</span><span class="p">(</span><span class="n">url</span><span class="p">,</span> <span class="n">data</span><span class="o">=</span><span class="n">data</span><span class="p">,</span> <span class="n">method</span><span class="o">=</span><span class="s2">&#34;POST&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">req</span><span class="o">.</span><span class="n">add_header</span><span class="p">(</span><span class="s2">&#34;Content-Type&#34;</span><span class="p">,</span> <span class="s2">&#34;application/json&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="n">ticket</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">        <span class="n">req</span><span class="o">.</span><span class="n">add_header</span><span class="p">(</span><span class="s2">&#34;X-Authorization&#34;</span><span class="p">,</span> <span class="n">ticket</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">try</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">        <span class="k">with</span> <span class="n">urllib</span><span class="o">.</span><span class="n">request</span><span class="o">.</span><span class="n">urlopen</span><span class="p">(</span><span class="n">req</span><span class="p">,</span> <span class="n">timeout</span><span class="o">=</span><span class="mi">60</span><span class="p">)</span> <span class="k">as</span> <span class="n">r</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">            <span class="k">return</span> <span class="n">r</span><span class="o">.</span><span class="n">status</span><span class="p">,</span> <span class="n">json</span><span class="o">.</span><span class="n">loads</span><span class="p">(</span><span class="n">r</span><span class="o">.</span><span class="n">read</span><span class="p">()</span><span class="o">.</span><span class="n">decode</span><span class="p">())</span>
</span></span><span class="line"><span class="cl">    <span class="k">except</span> <span class="n">urllib</span><span class="o">.</span><span class="n">error</span><span class="o">.</span><span class="n">HTTPError</span> <span class="k">as</span> <span class="n">e</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">        <span class="n">raw</span> <span class="o">=</span> <span class="n">e</span><span class="o">.</span><span class="n">read</span><span class="p">()</span><span class="o">.</span><span class="n">decode</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">        <span class="k">try</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">            <span class="k">return</span> <span class="n">e</span><span class="o">.</span><span class="n">code</span><span class="p">,</span> <span class="n">json</span><span class="o">.</span><span class="n">loads</span><span class="p">(</span><span class="n">raw</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="k">except</span> <span class="ne">Exception</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">            <span class="k">return</span> <span class="n">e</span><span class="o">.</span><span class="n">code</span><span class="p">,</span> <span class="p">{</span><span class="s2">&#34;raw&#34;</span><span class="p">:</span> <span class="n">raw</span><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">main</span><span class="p">():</span>
</span></span><span class="line"><span class="cl">    <span class="n">pid</span> <span class="o">=</span> <span class="n">sys</span><span class="o">.</span><span class="n">argv</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span><span class="o">.</span><span class="n">strip</span><span class="p">()</span> <span class="k">if</span> <span class="nb">len</span><span class="p">(</span><span class="n">sys</span><span class="o">.</span><span class="n">argv</span><span class="p">)</span> <span class="o">&gt;</span> <span class="mi">1</span> <span class="k">else</span> <span class="nb">open</span><span class="p">(</span><span class="s2">&#34;player_id.txt&#34;</span><span class="p">)</span><span class="o">.</span><span class="n">read</span><span class="p">()</span><span class="o">.</span><span class="n">strip</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">    <span class="nb">print</span><span class="p">(</span><span class="sa">f</span><span class="s2">&#34;[i] CustomId length = </span><span class="si">{</span><span class="nb">len</span><span class="p">(</span><span class="n">pid</span><span class="p">)</span><span class="si">}</span><span class="s2">&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="n">st</span><span class="p">,</span> <span class="n">resp</span> <span class="o">=</span> <span class="n">post</span><span class="p">(</span><span class="s2">&#34;Client/LoginWithCustomID&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                    <span class="p">{</span><span class="s2">&#34;TitleId&#34;</span><span class="p">:</span> <span class="n">TITLE</span><span class="p">,</span> <span class="s2">&#34;CustomId&#34;</span><span class="p">:</span> <span class="n">pid</span><span class="p">,</span> <span class="s2">&#34;CreateAccount&#34;</span><span class="p">:</span> <span class="kc">False</span><span class="p">,</span> <span class="s2">&#34;InfoRequestParameters&#34;</span><span class="p">:</span> <span class="p">{</span><span class="s2">&#34;GetUserData&#34;</span><span class="p">:</span> <span class="kc">False</span><span class="p">}})</span>
</span></span><span class="line"><span class="cl">    <span class="n">json</span><span class="o">.</span><span class="n">dump</span><span class="p">(</span><span class="n">resp</span><span class="p">,</span> <span class="nb">open</span><span class="p">(</span><span class="n">os</span><span class="o">.</span><span class="n">path</span><span class="o">.</span><span class="n">join</span><span class="p">(</span><span class="n">OUT</span><span class="p">,</span> <span class="s2">&#34;login.json&#34;</span><span class="p">),</span> <span class="s2">&#34;w&#34;</span><span class="p">),</span> <span class="n">indent</span><span class="o">=</span><span class="mi">1</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="n">resp</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s2">&#34;code&#34;</span><span class="p">)</span> <span class="o">!=</span> <span class="mi">200</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">        <span class="nb">print</span><span class="p">(</span><span class="s2">&#34;[x] login failed:&#34;</span><span class="p">,</span> <span class="n">resp</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s2">&#34;errorCode&#34;</span><span class="p">),</span> <span class="s2">&#34;-&#34;</span><span class="p">,</span> <span class="n">resp</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s2">&#34;errorMessage&#34;</span><span class="p">))</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span>
</span></span><span class="line"><span class="cl">    <span class="n">d</span> <span class="o">=</span> <span class="n">resp</span><span class="p">[</span><span class="s2">&#34;data&#34;</span><span class="p">]</span>
</span></span><span class="line"><span class="cl">    <span class="n">ticket</span> <span class="o">=</span> <span class="n">d</span><span class="p">[</span><span class="s2">&#34;SessionTicket&#34;</span><span class="p">]</span>
</span></span><span class="line"><span class="cl">    <span class="nb">print</span><span class="p">(</span><span class="s2">&#34;[+] login ok; PlayFabId:&#34;</span><span class="p">,</span> <span class="n">d</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s2">&#34;PlayFabId&#34;</span><span class="p">),</span> <span class="s2">&#34;| NewlyCreated:&#34;</span><span class="p">,</span> <span class="n">d</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s2">&#34;NewlyCreated&#34;</span><span class="p">))</span>
</span></span><span class="line"><span class="cl">    <span class="nb">print</span><span class="p">(</span><span class="s2">&#34;[i] SessionTicket length:&#34;</span><span class="p">,</span> <span class="nb">len</span><span class="p">(</span><span class="n">ticket</span><span class="p">),</span> <span class="s2">&#34;prefix:&#34;</span><span class="p">,</span> <span class="n">ticket</span><span class="p">[:</span><span class="mi">6</span><span class="p">]</span> <span class="o">+</span> <span class="s2">&#34;…&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="n">st</span><span class="p">,</span> <span class="n">resp</span> <span class="o">=</span> <span class="n">post</span><span class="p">(</span><span class="s2">&#34;Client/GetUserData&#34;</span><span class="p">,</span> <span class="p">{},</span> <span class="n">ticket</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">json</span><span class="o">.</span><span class="n">dump</span><span class="p">(</span><span class="n">resp</span><span class="p">,</span> <span class="nb">open</span><span class="p">(</span><span class="n">os</span><span class="o">.</span><span class="n">path</span><span class="o">.</span><span class="n">join</span><span class="p">(</span><span class="n">OUT</span><span class="p">,</span> <span class="s2">&#34;userdata.json&#34;</span><span class="p">),</span> <span class="s2">&#34;w&#34;</span><span class="p">),</span> <span class="n">indent</span><span class="o">=</span><span class="mi">1</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="n">resp</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s2">&#34;code&#34;</span><span class="p">)</span> <span class="o">!=</span> <span class="mi">200</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">        <span class="nb">print</span><span class="p">(</span><span class="s2">&#34;[x] GetUserData failed:&#34;</span><span class="p">,</span> <span class="n">resp</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s2">&#34;errorCode&#34;</span><span class="p">),</span> <span class="s2">&#34;-&#34;</span><span class="p">,</span> <span class="n">resp</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s2">&#34;errorMessage&#34;</span><span class="p">))</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span>
</span></span><span class="line"><span class="cl">    <span class="n">data</span> <span class="o">=</span> <span class="n">resp</span><span class="p">[</span><span class="s2">&#34;data&#34;</span><span class="p">]</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s2">&#34;Data&#34;</span><span class="p">,</span> <span class="p">{})</span>
</span></span><span class="line"><span class="cl">    <span class="nb">print</span><span class="p">(</span><span class="sa">f</span><span class="s2">&#34;[+] cloud keys: </span><span class="si">{</span><span class="nb">sorted</span><span class="p">(</span><span class="n">data</span><span class="o">.</span><span class="n">keys</span><span class="p">())</span><span class="si">}</span><span class="s2">&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">outdir</span> <span class="o">=</span> <span class="n">os</span><span class="o">.</span><span class="n">path</span><span class="o">.</span><span class="n">join</span><span class="p">(</span><span class="n">OUT</span><span class="p">,</span> <span class="s2">&#34;values&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">os</span><span class="o">.</span><span class="n">makedirs</span><span class="p">(</span><span class="n">outdir</span><span class="p">,</span> <span class="n">exist_ok</span><span class="o">=</span><span class="kc">True</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">for</span> <span class="n">k</span><span class="p">,</span> <span class="n">v</span> <span class="ow">in</span> <span class="nb">sorted</span><span class="p">(</span><span class="n">data</span><span class="o">.</span><span class="n">items</span><span class="p">()):</span>
</span></span><span class="line"><span class="cl">        <span class="n">val</span> <span class="o">=</span> <span class="n">v</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s2">&#34;Value&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="nb">print</span><span class="p">(</span><span class="sa">f</span><span class="s2">&#34;    </span><span class="si">{</span><span class="n">k</span><span class="si">}</span><span class="s2">: len=</span><span class="si">{</span><span class="nb">len</span><span class="p">(</span><span class="n">val</span><span class="p">)</span> <span class="k">if</span> <span class="n">val</span> <span class="ow">is</span> <span class="ow">not</span> <span class="kc">None</span> <span class="k">else</span> <span class="mi">0</span><span class="si">}</span><span class="s2"> LastUpdated=</span><span class="si">{</span><span class="n">v</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s1">&#39;LastUpdated&#39;</span><span class="p">)</span><span class="si">}</span><span class="s2">&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="n">val</span> <span class="ow">is</span> <span class="ow">not</span> <span class="kc">None</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">            <span class="nb">open</span><span class="p">(</span><span class="n">os</span><span class="o">.</span><span class="n">path</span><span class="o">.</span><span class="n">join</span><span class="p">(</span><span class="n">outdir</span><span class="p">,</span> <span class="n">k</span> <span class="o">+</span> <span class="s2">&#34;.json&#34;</span><span class="p">),</span> <span class="s2">&#34;w&#34;</span><span class="p">,</span> <span class="n">encoding</span><span class="o">=</span><span class="s2">&#34;utf-8&#34;</span><span class="p">)</span><span class="o">.</span><span class="n">write</span><span class="p">(</span><span class="n">val</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="c1"># ---- audit against the fields the installed version expects ----</span>
</span></span><span class="line"><span class="cl">    <span class="n">expected</span> <span class="o">=</span> <span class="p">{}</span>
</span></span><span class="line"><span class="cl">    <span class="n">src</span> <span class="o">=</span> <span class="n">os</span><span class="o">.</span><span class="n">path</span><span class="o">.</span><span class="n">join</span><span class="p">(</span><span class="n">os</span><span class="o">.</span><span class="n">path</span><span class="o">.</span><span class="n">dirname</span><span class="p">(</span><span class="n">os</span><span class="o">.</span><span class="n">path</span><span class="o">.</span><span class="n">abspath</span><span class="p">(</span><span class="vm">__file__</span><span class="p">)),</span>
</span></span><span class="line"><span class="cl">                       <span class="s2">&#34;dump_all/CodeEntries/gml_Object_o_popup_option_Step_0.gml&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="n">os</span><span class="o">.</span><span class="n">path</span><span class="o">.</span><span class="n">exists</span><span class="p">(</span><span class="n">src</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">        <span class="n">lines</span> <span class="o">=</span> <span class="nb">open</span><span class="p">(</span><span class="n">src</span><span class="p">,</span> <span class="n">encoding</span><span class="o">=</span><span class="s2">&#34;utf-8&#34;</span><span class="p">,</span> <span class="n">errors</span><span class="o">=</span><span class="s2">&#34;replace&#34;</span><span class="p">)</span><span class="o">.</span><span class="n">read</span><span class="p">()</span><span class="o">.</span><span class="n">split</span><span class="p">(</span><span class="s2">&#34;</span><span class="se">\n</span><span class="s2">&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="n">bounds</span> <span class="o">=</span> <span class="p">{</span><span class="s2">&#34;userdata&#34;</span><span class="p">:</span> <span class="p">(</span><span class="mi">23</span><span class="p">,</span> <span class="mi">1736</span><span class="p">),</span> <span class="s2">&#34;unitdata&#34;</span><span class="p">:</span> <span class="p">(</span><span class="mi">1749</span><span class="p">,</span> <span class="mi">2105</span><span class="p">),</span> <span class="s2">&#34;etcdata&#34;</span><span class="p">:</span> <span class="p">(</span><span class="mi">2118</span><span class="p">,</span> <span class="mi">2895</span><span class="p">)}</span>
</span></span><span class="line"><span class="cl">        <span class="k">for</span> <span class="n">name</span><span class="p">,</span> <span class="p">(</span><span class="n">a</span><span class="p">,</span> <span class="n">b</span><span class="p">)</span> <span class="ow">in</span> <span class="n">bounds</span><span class="o">.</span><span class="n">items</span><span class="p">():</span>
</span></span><span class="line"><span class="cl">            <span class="n">keys</span> <span class="o">=</span> <span class="p">[]</span>
</span></span><span class="line"><span class="cl">            <span class="k">for</span> <span class="n">ln</span> <span class="ow">in</span> <span class="n">lines</span><span class="p">[</span><span class="n">a</span> <span class="o">-</span> <span class="mi">1</span><span class="p">:</span><span class="n">b</span><span class="p">]:</span>
</span></span><span class="line"><span class="cl">                <span class="n">m</span> <span class="o">=</span> <span class="n">re</span><span class="o">.</span><span class="k">match</span><span class="p">(</span><span class="sa">r</span><span class="s2">&#34;\s*([A-Za-z_][A-Za-z0-9_]*):\s&#34;</span><span class="p">,</span> <span class="n">ln</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">                <span class="k">if</span> <span class="n">m</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">                    <span class="n">keys</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">m</span><span class="o">.</span><span class="n">group</span><span class="p">(</span><span class="mi">1</span><span class="p">))</span>
</span></span><span class="line"><span class="cl">            <span class="n">expected</span><span class="p">[</span><span class="n">name</span><span class="p">]</span> <span class="o">=</span> <span class="n">keys</span>
</span></span><span class="line"><span class="cl">    <span class="k">for</span> <span class="n">name</span><span class="p">,</span> <span class="n">keys</span> <span class="ow">in</span> <span class="n">expected</span><span class="o">.</span><span class="n">items</span><span class="p">():</span>
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="n">name</span> <span class="ow">not</span> <span class="ow">in</span> <span class="n">data</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">            <span class="nb">print</span><span class="p">(</span><span class="sa">f</span><span class="s2">&#34;[!] cloud has NO &#39;</span><span class="si">{</span><span class="n">name</span><span class="si">}</span><span class="s2">&#39; key (expected </span><span class="si">{</span><span class="nb">len</span><span class="p">(</span><span class="n">keys</span><span class="p">)</span><span class="si">}</span><span class="s2"> fields)&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">            <span class="k">continue</span>
</span></span><span class="line"><span class="cl">        <span class="k">try</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">            <span class="n">got</span> <span class="o">=</span> <span class="n">json</span><span class="o">.</span><span class="n">loads</span><span class="p">(</span><span class="n">data</span><span class="p">[</span><span class="n">name</span><span class="p">][</span><span class="s2">&#34;Value&#34;</span><span class="p">])</span>
</span></span><span class="line"><span class="cl">        <span class="k">except</span> <span class="ne">Exception</span> <span class="k">as</span> <span class="n">e</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">            <span class="nb">print</span><span class="p">(</span><span class="sa">f</span><span class="s2">&#34;[!] &#39;</span><span class="si">{</span><span class="n">name</span><span class="si">}</span><span class="s2">&#39; is not valid JSON: </span><span class="si">{</span><span class="n">e</span><span class="si">}</span><span class="s2">&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">            <span class="k">continue</span>
</span></span><span class="line"><span class="cl">        <span class="n">miss</span> <span class="o">=</span> <span class="p">[</span><span class="n">k</span> <span class="k">for</span> <span class="n">k</span> <span class="ow">in</span> <span class="n">keys</span> <span class="k">if</span> <span class="n">k</span> <span class="ow">not</span> <span class="ow">in</span> <span class="n">got</span><span class="p">]</span>
</span></span><span class="line"><span class="cl">        <span class="n">extra</span> <span class="o">=</span> <span class="p">[</span><span class="n">k</span> <span class="k">for</span> <span class="n">k</span> <span class="ow">in</span> <span class="n">got</span> <span class="k">if</span> <span class="n">k</span> <span class="ow">not</span> <span class="ow">in</span> <span class="n">keys</span><span class="p">]</span>
</span></span><span class="line"><span class="cl">        <span class="nb">print</span><span class="p">(</span><span class="sa">f</span><span class="s2">&#34;[audit] </span><span class="si">{</span><span class="n">name</span><span class="si">}</span><span class="s2">: cloud fields=</span><span class="si">{</span><span class="nb">len</span><span class="p">(</span><span class="n">got</span><span class="p">)</span><span class="si">}</span><span class="s2"> expected=</span><span class="si">{</span><span class="nb">len</span><span class="p">(</span><span class="n">keys</span><span class="p">)</span><span class="si">}</span><span class="s2"> missing=</span><span class="si">{</span><span class="nb">len</span><span class="p">(</span><span class="n">miss</span><span class="p">)</span><span class="si">}</span><span class="s2"> extra=</span><span class="si">{</span><span class="nb">len</span><span class="p">(</span><span class="n">extra</span><span class="p">)</span><span class="si">}</span><span class="s2">&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="n">miss</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">            <span class="nb">print</span><span class="p">(</span><span class="s2">&#34;        MISSING:&#34;</span><span class="p">,</span> <span class="n">miss</span><span class="p">[:</span><span class="mi">60</span><span class="p">])</span>
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="n">extra</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">            <span class="nb">print</span><span class="p">(</span><span class="s2">&#34;        EXTRA  :&#34;</span><span class="p">,</span> <span class="n">extra</span><span class="p">[:</span><span class="mi">60</span><span class="p">])</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">if</span> <span class="vm">__name__</span> <span class="o">==</span> <span class="s2">&#34;__main__&#34;</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">    <span class="n">main</span><span class="p">()</span></span></span></code></pre></div></div>

  </div>
</details>




  










<details
  class="group border-none"
  data-accordion-item
  
>
  <summary class="flex w-full cursor-pointer items-center justify-between gap-4 px-4 py-3 text-left text-lg font-semibold text-neutral-900 dark:text-neutral-100">
    <span class="flex items-center gap-2">
      
      <span>build_fixed_cloud.py（168 行）：用游戏自己的公式从本地存档重建三个 blob</span>
    </span>
    <span class="accordion-chevron ms-auto flex h-5 w-5 items-center justify-center print:hidden">
      <span class="relative block icon"><svg
  xmlns="http://www.w3.org/2000/svg"
  viewBox="0 0 20 20"
  fill="currentColor"
  aria-hidden="true"
>
  <path
    fill-rule="evenodd"
    d="M5.23 7.21a.75.75 0 011.06.02L10 11.168l3.71-3.938a.75.75 0 111.08 1.04l-4.25 4.5a.75.75 0 01-1.08 0l-4.25-4.5a.75.75 0 01.02-1.06z"
    clip-rule="evenodd"
  />
</svg>
</span>
    </span>
  </summary>
<div class="px-4 pb-4 text-neutral-700 dark:text-neutral-300">
    <div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="s2">&#34;&#34;&#34;Build corrected cloud blobs from device A&#39;s local save files.
</span></span></span><span class="line"><span class="cl"><span class="s2">
</span></span></span><span class="line"><span class="cl"><span class="s2">- schema (which key holds which global): from the game&#39;s own save code
</span></span></span><span class="line"><span class="cl"><span class="s2">  (gml_Object_o_popup_option_Step_0.gml)
</span></span></span><span class="line"><span class="cl"><span class="s2">- values: from device A&#39;s INI files, decoded with the game&#39;s own load formulas
</span></span></span><span class="line"><span class="cl"><span class="s2">  (gameload_*.gml, including the +K obfuscation offsets)
</span></span></span><span class="line"><span class="cl"><span class="s2">- fields with no local source (e.g. save_land.ini absent, or cloud-only fields)
</span></span></span><span class="line"><span class="cl"><span class="s2">  keep the value currently stored in the cloud.
</span></span></span><span class="line"><span class="cl"><span class="s2">&#34;&#34;&#34;</span>
</span></span><span class="line"><span class="cl"><span class="kn">import</span> <span class="nn">json</span><span class="o">,</span> <span class="nn">os</span><span class="o">,</span> <span class="nn">re</span><span class="o">,</span> <span class="nn">sys</span><span class="o">,</span> <span class="nn">shutil</span><span class="o">,</span> <span class="nn">time</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># 目录约定（都在 BASE 下）：dump_all/CodeEntries（反编译产物）、saveA/files（本地存档）、</span>
</span></span><span class="line"><span class="cl"><span class="c1"># cloud_raw/values（云端现值，playfab_fetch.py 的输出）。BASE 取脚本所在目录，或用 argv[1] 指定。</span>
</span></span><span class="line"><span class="cl"><span class="n">BASE</span> <span class="o">=</span> <span class="n">os</span><span class="o">.</span><span class="n">path</span><span class="o">.</span><span class="n">abspath</span><span class="p">(</span><span class="n">sys</span><span class="o">.</span><span class="n">argv</span><span class="p">[</span><span class="mi">1</span><span class="p">])</span> <span class="k">if</span> <span class="nb">len</span><span class="p">(</span><span class="n">sys</span><span class="o">.</span><span class="n">argv</span><span class="p">)</span> <span class="o">&gt;</span> <span class="mi">1</span> <span class="k">else</span> <span class="n">os</span><span class="o">.</span><span class="n">path</span><span class="o">.</span><span class="n">dirname</span><span class="p">(</span><span class="n">os</span><span class="o">.</span><span class="n">path</span><span class="o">.</span><span class="n">abspath</span><span class="p">(</span><span class="vm">__file__</span><span class="p">))</span>
</span></span><span class="line"><span class="cl"><span class="n">CE</span> <span class="o">=</span> <span class="n">os</span><span class="o">.</span><span class="n">path</span><span class="o">.</span><span class="n">join</span><span class="p">(</span><span class="n">BASE</span><span class="p">,</span> <span class="s2">&#34;dump_all/CodeEntries&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="n">SAVE</span> <span class="o">=</span> <span class="n">os</span><span class="o">.</span><span class="n">path</span><span class="o">.</span><span class="n">join</span><span class="p">(</span><span class="n">BASE</span><span class="p">,</span> <span class="s2">&#34;saveA/files&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="n">CLOUD</span> <span class="o">=</span> <span class="n">os</span><span class="o">.</span><span class="n">path</span><span class="o">.</span><span class="n">join</span><span class="p">(</span><span class="n">BASE</span><span class="p">,</span> <span class="s2">&#34;cloud_raw/values&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># ---------- 1. global -&gt; (ini, section, key, op, offset) ----------</span>
</span></span><span class="line"><span class="cl"><span class="n">LOADERS</span> <span class="o">=</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="s2">&#34;gml_GlobalScript_gameload_info.gml&#34;</span><span class="p">:</span> <span class="s2">&#34;save_info.ini&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="s2">&#34;gml_GlobalScript_gameload_data.gml&#34;</span><span class="p">:</span> <span class="s2">&#34;save_data.ini&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="s2">&#34;gml_GlobalScript_gameload_stat.gml&#34;</span><span class="p">:</span> <span class="s2">&#34;save_stat.ini&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="s2">&#34;gml_GlobalScript_gameload_unit.gml&#34;</span><span class="p">:</span> <span class="s2">&#34;save_unit.ini&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="s2">&#34;gml_GlobalScript_gameload_item.gml&#34;</span><span class="p">:</span> <span class="s2">&#34;save_item.ini&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="s2">&#34;gml_GlobalScript_gameload_menu.gml&#34;</span><span class="p">:</span> <span class="s2">&#34;save_menu.ini&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="s2">&#34;gml_GlobalScript_gameload_achievement.gml&#34;</span><span class="p">:</span> <span class="s2">&#34;save_achievement.ini&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="s2">&#34;gml_GlobalScript_gameload_land.gml&#34;</span><span class="p">:</span> <span class="s2">&#34;save_land.ini&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="s2">&#34;gml_GlobalScript_gameload_deck.gml&#34;</span><span class="p">:</span> <span class="s2">&#34;save_deck.ini&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="c1"># global = [floor(] ini_read_real(&#34;sec&#34;,&#34;key&#34;,def) [)] [ -/+ K] ;</span>
</span></span><span class="line"><span class="cl"><span class="n">READ</span> <span class="o">=</span> <span class="n">re</span><span class="o">.</span><span class="n">compile</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">    <span class="sa">r</span><span class="s1">&#39;global\.([A-Za-z_][A-Za-z0-9_]*)\s*=\s*(?:(floor|round|int64)\()?\s*&#39;</span>
</span></span><span class="line"><span class="cl">    <span class="sa">r</span><span class="s1">&#39;ini_read_(real|string)\(&#34;([^&#34;]+)&#34;,\s*&#34;([^&#34;]+)&#34;,\s*([^)]*)\)\s*\)?\s*([-+]\s*[0-9.]+)?\s*;&#39;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="c1"># variant where the offset sits inside the floor(): floor(ini_read_real(s,k,d) - 84)</span>
</span></span><span class="line"><span class="cl"><span class="n">READ_INNER</span> <span class="o">=</span> <span class="n">re</span><span class="o">.</span><span class="n">compile</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">    <span class="sa">r</span><span class="s1">&#39;global\.([A-Za-z_][A-Za-z0-9_]*)\s*=\s*(?:(floor|round|int64)\()?\s*&#39;</span>
</span></span><span class="line"><span class="cl">    <span class="sa">r</span><span class="s1">&#39;ini_read_(real|string)\(&#34;([^&#34;]+)&#34;,\s*&#34;([^&#34;]+)&#34;,\s*([^(]*?)\)\s*([-+]\s*[0-9.]+)\)\s*;&#39;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="n">gl_map</span><span class="p">,</span> <span class="n">unsupported</span> <span class="o">=</span> <span class="p">{},</span> <span class="p">[]</span>
</span></span><span class="line"><span class="cl"><span class="k">for</span> <span class="n">fn</span><span class="p">,</span> <span class="n">inif</span> <span class="ow">in</span> <span class="n">LOADERS</span><span class="o">.</span><span class="n">items</span><span class="p">():</span>
</span></span><span class="line"><span class="cl">    <span class="n">p</span> <span class="o">=</span> <span class="n">os</span><span class="o">.</span><span class="n">path</span><span class="o">.</span><span class="n">join</span><span class="p">(</span><span class="n">CE</span><span class="p">,</span> <span class="n">fn</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="ow">not</span> <span class="n">os</span><span class="o">.</span><span class="n">path</span><span class="o">.</span><span class="n">exists</span><span class="p">(</span><span class="n">p</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">        <span class="n">unsupported</span><span class="o">.</span><span class="n">append</span><span class="p">((</span><span class="n">fn</span><span class="p">,</span> <span class="s2">&#34;missing code entry&#34;</span><span class="p">))</span>
</span></span><span class="line"><span class="cl">        <span class="k">continue</span>
</span></span><span class="line"><span class="cl">    <span class="k">for</span> <span class="n">line</span> <span class="ow">in</span> <span class="nb">open</span><span class="p">(</span><span class="n">p</span><span class="p">,</span> <span class="n">encoding</span><span class="o">=</span><span class="s1">&#39;utf-8&#39;</span><span class="p">,</span> <span class="n">errors</span><span class="o">=</span><span class="s1">&#39;replace&#39;</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">        <span class="n">line</span> <span class="o">=</span> <span class="n">line</span><span class="o">.</span><span class="n">strip</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="s1">&#39;ini_read_&#39;</span> <span class="ow">not</span> <span class="ow">in</span> <span class="n">line</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">            <span class="k">continue</span>
</span></span><span class="line"><span class="cl">        <span class="n">m</span> <span class="o">=</span> <span class="n">READ_INNER</span><span class="o">.</span><span class="k">match</span><span class="p">(</span><span class="n">line</span><span class="p">)</span> <span class="ow">or</span> <span class="n">READ</span><span class="o">.</span><span class="k">match</span><span class="p">(</span><span class="n">line</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="ow">not</span> <span class="n">m</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">            <span class="n">unsupported</span><span class="o">.</span><span class="n">append</span><span class="p">((</span><span class="n">fn</span><span class="p">,</span> <span class="n">line</span><span class="p">[:</span><span class="mi">110</span><span class="p">]));</span> <span class="k">continue</span>
</span></span><span class="line"><span class="cl">        <span class="n">g</span><span class="p">,</span> <span class="n">fnc</span><span class="p">,</span> <span class="n">kind</span><span class="p">,</span> <span class="n">sec</span><span class="p">,</span> <span class="n">key</span><span class="p">,</span> <span class="n">dflt</span><span class="p">,</span> <span class="n">off</span> <span class="o">=</span> <span class="n">m</span><span class="o">.</span><span class="n">groups</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">        <span class="n">off</span> <span class="o">=</span> <span class="nb">float</span><span class="p">(</span><span class="n">off</span><span class="o">.</span><span class="n">replace</span><span class="p">(</span><span class="s1">&#39; &#39;</span><span class="p">,</span> <span class="s1">&#39;&#39;</span><span class="p">))</span> <span class="k">if</span> <span class="n">off</span> <span class="k">else</span> <span class="mf">0.0</span>
</span></span><span class="line"><span class="cl">        <span class="n">gl_map</span><span class="p">[</span><span class="n">g</span><span class="p">]</span> <span class="o">=</span> <span class="p">(</span><span class="n">inif</span><span class="p">,</span> <span class="n">sec</span><span class="p">,</span> <span class="n">key</span><span class="p">,</span> <span class="n">kind</span><span class="p">,</span> <span class="n">fnc</span><span class="p">,</span> <span class="n">off</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nb">print</span><span class="p">(</span><span class="sa">f</span><span class="s2">&#34;[i] globals mapped from local save: </span><span class="si">{</span><span class="nb">len</span><span class="p">(</span><span class="n">gl_map</span><span class="p">)</span><span class="si">}</span><span class="s2">; unparsed loader lines: </span><span class="si">{</span><span class="nb">len</span><span class="p">(</span><span class="n">unsupported</span><span class="p">)</span><span class="si">}</span><span class="s2">&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="k">for</span> <span class="n">u</span> <span class="ow">in</span> <span class="n">unsupported</span><span class="p">[:</span><span class="mi">8</span><span class="p">]:</span>
</span></span><span class="line"><span class="cl">    <span class="nb">print</span><span class="p">(</span><span class="s2">&#34;    unparsed:&#34;</span><span class="p">,</span> <span class="n">u</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">read_ini</span><span class="p">(</span><span class="n">path</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">    <span class="n">d</span><span class="p">,</span> <span class="n">sec</span> <span class="o">=</span> <span class="p">{},</span> <span class="kc">None</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="ow">not</span> <span class="n">os</span><span class="o">.</span><span class="n">path</span><span class="o">.</span><span class="n">exists</span><span class="p">(</span><span class="n">path</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="n">d</span>
</span></span><span class="line"><span class="cl">    <span class="k">for</span> <span class="n">line</span> <span class="ow">in</span> <span class="nb">open</span><span class="p">(</span><span class="n">path</span><span class="p">,</span> <span class="n">encoding</span><span class="o">=</span><span class="s1">&#39;utf-8&#39;</span><span class="p">,</span> <span class="n">errors</span><span class="o">=</span><span class="s1">&#39;replace&#39;</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">        <span class="n">line</span> <span class="o">=</span> <span class="n">line</span><span class="o">.</span><span class="n">strip</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="ow">not</span> <span class="n">line</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">            <span class="k">continue</span>
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="n">line</span><span class="o">.</span><span class="n">startswith</span><span class="p">(</span><span class="s1">&#39;[&#39;</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">            <span class="n">sec</span> <span class="o">=</span> <span class="n">line</span><span class="o">.</span><span class="n">strip</span><span class="p">(</span><span class="s1">&#39;[]&#39;</span><span class="p">);</span> <span class="k">continue</span>
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="s1">&#39;=&#39;</span> <span class="ow">in</span> <span class="n">line</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">            <span class="n">k</span><span class="p">,</span> <span class="n">v</span> <span class="o">=</span> <span class="n">line</span><span class="o">.</span><span class="n">split</span><span class="p">(</span><span class="s1">&#39;=&#39;</span><span class="p">,</span> <span class="mi">1</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">            <span class="n">v</span> <span class="o">=</span> <span class="n">v</span><span class="o">.</span><span class="n">strip</span><span class="p">()</span><span class="o">.</span><span class="n">strip</span><span class="p">(</span><span class="s1">&#39;&#34;&#39;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">            <span class="k">try</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">                <span class="n">d</span><span class="p">[(</span><span class="n">sec</span><span class="p">,</span> <span class="n">k</span><span class="o">.</span><span class="n">strip</span><span class="p">())]</span> <span class="o">=</span> <span class="nb">float</span><span class="p">(</span><span class="n">v</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">            <span class="k">except</span> <span class="ne">ValueError</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">                <span class="n">d</span><span class="p">[(</span><span class="n">sec</span><span class="p">,</span> <span class="n">k</span><span class="o">.</span><span class="n">strip</span><span class="p">())]</span> <span class="o">=</span> <span class="n">v</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="n">d</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">INIS</span> <span class="o">=</span> <span class="p">{</span><span class="n">f</span><span class="p">:</span> <span class="n">read_ini</span><span class="p">(</span><span class="n">os</span><span class="o">.</span><span class="n">path</span><span class="o">.</span><span class="n">join</span><span class="p">(</span><span class="n">SAVE</span><span class="p">,</span> <span class="n">f</span><span class="p">))</span> <span class="k">for</span> <span class="n">f</span> <span class="ow">in</span> <span class="nb">set</span><span class="p">(</span><span class="n">v</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="k">for</span> <span class="n">v</span> <span class="ow">in</span> <span class="n">gl_map</span><span class="o">.</span><span class="n">values</span><span class="p">())}</span>
</span></span><span class="line"><span class="cl"><span class="nb">print</span><span class="p">(</span><span class="s2">&#34;[i] local ini loaded:&#34;</span><span class="p">,</span> <span class="p">{</span><span class="n">k</span><span class="p">:</span> <span class="nb">len</span><span class="p">(</span><span class="n">v</span><span class="p">)</span> <span class="k">for</span> <span class="n">k</span><span class="p">,</span> <span class="n">v</span> <span class="ow">in</span> <span class="n">INIS</span><span class="o">.</span><span class="n">items</span><span class="p">()})</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">local_value</span><span class="p">(</span><span class="n">g</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">    <span class="s2">&#34;&#34;&#34;in-game value of a global, as the game would have it after boot.&#34;&#34;&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="n">m</span> <span class="o">=</span> <span class="n">gl_map</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="n">g</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="ow">not</span> <span class="n">m</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="kc">None</span>
</span></span><span class="line"><span class="cl">    <span class="n">inif</span><span class="p">,</span> <span class="n">sec</span><span class="p">,</span> <span class="n">key</span><span class="p">,</span> <span class="n">kind</span><span class="p">,</span> <span class="n">fnc</span><span class="p">,</span> <span class="n">off</span> <span class="o">=</span> <span class="n">m</span>
</span></span><span class="line"><span class="cl">    <span class="n">raw</span> <span class="o">=</span> <span class="n">INIS</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="n">inif</span><span class="p">,</span> <span class="p">{})</span><span class="o">.</span><span class="n">get</span><span class="p">((</span><span class="n">sec</span><span class="p">,</span> <span class="n">key</span><span class="p">))</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="n">raw</span> <span class="ow">is</span> <span class="kc">None</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="kc">None</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="n">kind</span> <span class="o">==</span> <span class="s1">&#39;string&#39;</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="n">raw</span>
</span></span><span class="line"><span class="cl">    <span class="k">try</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">        <span class="n">v</span> <span class="o">=</span> <span class="nb">float</span><span class="p">(</span><span class="n">raw</span><span class="p">)</span> <span class="o">+</span> <span class="n">off</span>
</span></span><span class="line"><span class="cl">    <span class="k">except</span> <span class="p">(</span><span class="ne">TypeError</span><span class="p">,</span> <span class="ne">ValueError</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="kc">None</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="n">fnc</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">        <span class="n">v</span> <span class="o">=</span> <span class="nb">int</span><span class="p">(</span><span class="n">v</span> <span class="o">//</span> <span class="mi">1</span><span class="p">)</span> <span class="k">if</span> <span class="n">v</span> <span class="o">&gt;=</span> <span class="mi">0</span> <span class="k">else</span> <span class="o">-</span><span class="nb">int</span><span class="p">((</span><span class="o">-</span><span class="n">v</span><span class="p">)</span> <span class="o">//</span> <span class="mi">1</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="n">v</span> <span class="o">==</span> <span class="nb">int</span><span class="p">(</span><span class="n">v</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="nb">int</span><span class="p">(</span><span class="n">v</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="nb">round</span><span class="p">(</span><span class="n">v</span><span class="p">,</span> <span class="mi">6</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># ---------- 2. schema from the game&#39;s save code ----------</span>
</span></span><span class="line"><span class="cl"><span class="n">lines</span> <span class="o">=</span> <span class="nb">open</span><span class="p">(</span><span class="n">os</span><span class="o">.</span><span class="n">path</span><span class="o">.</span><span class="n">join</span><span class="p">(</span><span class="n">CE</span><span class="p">,</span> <span class="s2">&#34;gml_Object_o_popup_option_Step_0.gml&#34;</span><span class="p">),</span> <span class="n">encoding</span><span class="o">=</span><span class="s1">&#39;utf-8&#39;</span><span class="p">,</span> <span class="n">errors</span><span class="o">=</span><span class="s1">&#39;replace&#39;</span><span class="p">)</span><span class="o">.</span><span class="n">read</span><span class="p">()</span><span class="o">.</span><span class="n">split</span><span class="p">(</span><span class="s1">&#39;</span><span class="se">\n</span><span class="s1">&#39;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="n">BOUNDS</span> <span class="o">=</span> <span class="p">{</span><span class="s2">&#34;userdata&#34;</span><span class="p">:</span> <span class="p">(</span><span class="mi">25</span><span class="p">,</span> <span class="mi">1735</span><span class="p">),</span> <span class="s2">&#34;unitdata&#34;</span><span class="p">:</span> <span class="p">(</span><span class="mi">1750</span><span class="p">,</span> <span class="mi">2104</span><span class="p">),</span> <span class="s2">&#34;etcdata&#34;</span><span class="p">:</span> <span class="p">(</span><span class="mi">2119</span><span class="p">,</span> <span class="mi">2894</span><span class="p">)}</span>
</span></span><span class="line"><span class="cl"><span class="n">FIELD</span> <span class="o">=</span> <span class="n">re</span><span class="o">.</span><span class="n">compile</span><span class="p">(</span><span class="sa">r</span><span class="s1">&#39;^\s*([A-Za-z_][A-Za-z0-9_]*):\s*(.+?),?\s*$&#39;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">schema</span><span class="p">(</span><span class="n">a</span><span class="p">,</span> <span class="n">b</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">    <span class="n">out</span> <span class="o">=</span> <span class="p">[]</span>
</span></span><span class="line"><span class="cl">    <span class="k">for</span> <span class="n">ln</span> <span class="ow">in</span> <span class="n">lines</span><span class="p">[</span><span class="n">a</span> <span class="o">-</span> <span class="mi">1</span><span class="p">:</span><span class="n">b</span><span class="p">]:</span>
</span></span><span class="line"><span class="cl">        <span class="n">m</span> <span class="o">=</span> <span class="n">FIELD</span><span class="o">.</span><span class="k">match</span><span class="p">(</span><span class="n">ln</span><span class="o">.</span><span class="n">rstrip</span><span class="p">())</span>
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="n">m</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">            <span class="n">out</span><span class="o">.</span><span class="n">append</span><span class="p">((</span><span class="n">m</span><span class="o">.</span><span class="n">group</span><span class="p">(</span><span class="mi">1</span><span class="p">),</span> <span class="n">m</span><span class="o">.</span><span class="n">group</span><span class="p">(</span><span class="mi">2</span><span class="p">)</span><span class="o">.</span><span class="n">strip</span><span class="p">()))</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="n">out</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">STATS</span> <span class="o">=</span> <span class="p">{}</span>
</span></span><span class="line"><span class="cl"><span class="n">built</span> <span class="o">=</span> <span class="p">{}</span>
</span></span><span class="line"><span class="cl"><span class="k">for</span> <span class="n">blob</span><span class="p">,</span> <span class="p">(</span><span class="n">a</span><span class="p">,</span> <span class="n">b</span><span class="p">)</span> <span class="ow">in</span> <span class="n">BOUNDS</span><span class="o">.</span><span class="n">items</span><span class="p">():</span>
</span></span><span class="line"><span class="cl">    <span class="n">cur</span> <span class="o">=</span> <span class="n">json</span><span class="o">.</span><span class="n">loads</span><span class="p">(</span><span class="nb">open</span><span class="p">(</span><span class="n">os</span><span class="o">.</span><span class="n">path</span><span class="o">.</span><span class="n">join</span><span class="p">(</span><span class="n">CLOUD</span><span class="p">,</span> <span class="n">blob</span> <span class="o">+</span> <span class="s2">&#34;.json&#34;</span><span class="p">),</span> <span class="n">encoding</span><span class="o">=</span><span class="s1">&#39;utf-8&#39;</span><span class="p">)</span><span class="o">.</span><span class="n">read</span><span class="p">())</span>
</span></span><span class="line"><span class="cl">    <span class="n">fields</span> <span class="o">=</span> <span class="n">schema</span><span class="p">(</span><span class="n">a</span><span class="p">,</span> <span class="n">b</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">out</span><span class="p">,</span> <span class="n">cnt</span> <span class="o">=</span> <span class="p">{},</span> <span class="p">{</span><span class="s2">&#34;from_local&#34;</span><span class="p">:</span> <span class="mi">0</span><span class="p">,</span> <span class="s2">&#34;kept_cloud&#34;</span><span class="p">:</span> <span class="mi">0</span><span class="p">,</span> <span class="s2">&#34;only_cloud&#34;</span><span class="p">:</span> <span class="mi">0</span><span class="p">,</span> <span class="s2">&#34;changed&#34;</span><span class="p">:</span> <span class="mi">0</span><span class="p">,</span> <span class="s2">&#34;same&#34;</span><span class="p">:</span> <span class="mi">0</span><span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="k">for</span> <span class="n">key</span><span class="p">,</span> <span class="n">expr</span> <span class="ow">in</span> <span class="n">fields</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="n">key</span> <span class="o">==</span> <span class="n">blob</span><span class="p">:</span>                      <span class="c1"># wrapper key of the outer object</span>
</span></span><span class="line"><span class="cl">            <span class="k">continue</span>
</span></span><span class="line"><span class="cl">        <span class="n">m</span> <span class="o">=</span> <span class="n">re</span><span class="o">.</span><span class="n">fullmatch</span><span class="p">(</span><span class="sa">r</span><span class="s1">&#39;global\.([A-Za-z_][A-Za-z0-9_]*)&#39;</span><span class="p">,</span> <span class="n">expr</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="n">val</span> <span class="o">=</span> <span class="n">local_value</span><span class="p">(</span><span class="n">m</span><span class="o">.</span><span class="n">group</span><span class="p">(</span><span class="mi">1</span><span class="p">))</span> <span class="k">if</span> <span class="n">m</span> <span class="k">else</span> <span class="kc">None</span>
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="n">val</span> <span class="ow">is</span> <span class="ow">not</span> <span class="kc">None</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">            <span class="n">out</span><span class="p">[</span><span class="n">key</span><span class="p">]</span> <span class="o">=</span> <span class="n">val</span><span class="p">;</span> <span class="n">cnt</span><span class="p">[</span><span class="s2">&#34;from_local&#34;</span><span class="p">]</span> <span class="o">+=</span> <span class="mi">1</span>
</span></span><span class="line"><span class="cl">        <span class="k">elif</span> <span class="n">key</span> <span class="ow">in</span> <span class="n">cur</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">            <span class="n">out</span><span class="p">[</span><span class="n">key</span><span class="p">]</span> <span class="o">=</span> <span class="n">cur</span><span class="p">[</span><span class="n">key</span><span class="p">];</span> <span class="n">cnt</span><span class="p">[</span><span class="s2">&#34;kept_cloud&#34;</span><span class="p">]</span> <span class="o">+=</span> <span class="mi">1</span>
</span></span><span class="line"><span class="cl">        <span class="k">else</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">            <span class="n">out</span><span class="p">[</span><span class="n">key</span><span class="p">]</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">cnt</span><span class="p">[</span><span class="s2">&#34;only_cloud&#34;</span><span class="p">]</span> <span class="o">+=</span> <span class="mi">1</span>
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="n">key</span> <span class="ow">in</span> <span class="n">cur</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">            <span class="k">if</span> <span class="n">out</span><span class="p">[</span><span class="n">key</span><span class="p">]</span> <span class="o">==</span> <span class="n">cur</span><span class="p">[</span><span class="n">key</span><span class="p">]:</span>
</span></span><span class="line"><span class="cl">                <span class="n">cnt</span><span class="p">[</span><span class="s2">&#34;same&#34;</span><span class="p">]</span> <span class="o">+=</span> <span class="mi">1</span>
</span></span><span class="line"><span class="cl">            <span class="k">else</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">                <span class="n">cnt</span><span class="p">[</span><span class="s2">&#34;changed&#34;</span><span class="p">]</span> <span class="o">+=</span> <span class="mi">1</span>
</span></span><span class="line"><span class="cl">    <span class="n">extra</span> <span class="o">=</span> <span class="p">[</span><span class="n">k</span> <span class="k">for</span> <span class="n">k</span> <span class="ow">in</span> <span class="n">cur</span> <span class="k">if</span> <span class="n">k</span> <span class="ow">not</span> <span class="ow">in</span> <span class="n">out</span><span class="p">]</span>
</span></span><span class="line"><span class="cl">    <span class="n">built</span><span class="p">[</span><span class="n">blob</span><span class="p">]</span> <span class="o">=</span> <span class="n">out</span>
</span></span><span class="line"><span class="cl">    <span class="n">STATS</span><span class="p">[</span><span class="n">blob</span><span class="p">]</span> <span class="o">=</span> <span class="p">(</span><span class="n">cnt</span><span class="p">,</span> <span class="nb">len</span><span class="p">(</span><span class="n">fields</span><span class="p">),</span> <span class="n">extra</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="nb">print</span><span class="p">(</span><span class="sa">f</span><span class="s2">&#34;</span><span class="se">\n</span><span class="s2">== </span><span class="si">{</span><span class="n">blob</span><span class="si">}</span><span class="s2">: fields=</span><span class="si">{</span><span class="nb">len</span><span class="p">(</span><span class="n">fields</span><span class="p">)</span><span class="si">}</span><span class="s2"> extra-in-cloud=</span><span class="si">{</span><span class="nb">len</span><span class="p">(</span><span class="n">extra</span><span class="p">)</span><span class="si">}</span><span class="s2"> </span><span class="si">{</span><span class="n">extra</span><span class="p">[:</span><span class="mi">5</span><span class="p">]</span><span class="si">}</span><span class="s2">&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="nb">print</span><span class="p">(</span><span class="s2">&#34;   &#34;</span><span class="p">,</span> <span class="n">cnt</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># ---------- 3. sanity: what the game will read back for the crash-critical fields ----------</span>
</span></span><span class="line"><span class="cl"><span class="n">u</span> <span class="o">=</span> <span class="n">built</span><span class="p">[</span><span class="s2">&#34;userdata&#34;</span><span class="p">]</span>
</span></span><span class="line"><span class="cl"><span class="nb">print</span><span class="p">(</span><span class="s2">&#34;</span><span class="se">\n</span><span class="s2">[crash-critical fields in the rebuilt userdata]&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="k">for</span> <span class="n">k</span><span class="p">,</span> <span class="n">what</span> <span class="ow">in</span> <span class="p">[(</span><span class="s2">&#34;a27&#34;</span><span class="p">,</span> <span class="s2">&#34;p1_select (1=CROW 2=RAVEN)&#34;</span><span class="p">),</span> <span class="p">(</span><span class="s2">&#34;a7&#34;</span><span class="p">,</span> <span class="s2">&#34;p1_gameturn (DAY)&#34;</span><span class="p">),</span> <span class="p">(</span><span class="s2">&#34;a28&#34;</span><span class="p">,</span> <span class="s2">&#34;difficulty&#34;</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">                <span class="p">(</span><span class="s2">&#34;a29&#34;</span><span class="p">,</span> <span class="s2">&#34;difficulty_style&#34;</span><span class="p">),</span> <span class="p">(</span><span class="s2">&#34;a3&#34;</span><span class="p">,</span> <span class="s2">&#34;p1_gold&#34;</span><span class="p">),</span> <span class="p">(</span><span class="s2">&#34;a4&#34;</span><span class="p">,</span> <span class="s2">&#34;p1_supplies&#34;</span><span class="p">),</span> <span class="p">(</span><span class="s2">&#34;a5&#34;</span><span class="p">,</span> <span class="s2">&#34;p1_medicine&#34;</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">                <span class="p">(</span><span class="s2">&#34;a6&#34;</span><span class="p">,</span> <span class="s2">&#34;p1_cash&#34;</span><span class="p">),</span> <span class="p">(</span><span class="s2">&#34;a1&#34;</span><span class="p">,</span> <span class="s2">&#34;check_tutorial&#34;</span><span class="p">),</span> <span class="p">(</span><span class="s2">&#34;a2&#34;</span><span class="p">,</span> <span class="s2">&#34;p1_gameclear&#34;</span><span class="p">),</span> <span class="p">(</span><span class="s2">&#34;a9&#34;</span><span class="p">,</span> <span class="s2">&#34;newgame&#34;</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">                <span class="p">(</span><span class="s2">&#34;a24&#34;</span><span class="p">,</span> <span class="s2">&#34;bgm_ok&#34;</span><span class="p">),</span> <span class="p">(</span><span class="s2">&#34;a25&#34;</span><span class="p">,</span> <span class="s2">&#34;se_ok&#34;</span><span class="p">),</span> <span class="p">(</span><span class="s2">&#34;a26&#34;</span><span class="p">,</span> <span class="s2">&#34;set_language&#34;</span><span class="p">),</span> <span class="p">(</span><span class="s2">&#34;a11&#34;</span><span class="p">,</span> <span class="s2">&#34;p1_pioneer&#34;</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">                <span class="p">(</span><span class="s2">&#34;a13&#34;</span><span class="p">,</span> <span class="s2">&#34;p1_gameturn2&#34;</span><span class="p">),</span> <span class="p">(</span><span class="s2">&#34;a15&#34;</span><span class="p">,</span> <span class="s2">&#34;proposal_count&#34;</span><span class="p">),</span> <span class="p">(</span><span class="s2">&#34;a12&#34;</span><span class="p">,</span> <span class="s2">&#34;event_boss_num&#34;</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">                <span class="p">(</span><span class="s2">&#34;a30&#34;</span><span class="p">,</span> <span class="s2">&#34;ope_num&#34;</span><span class="p">),</span> <span class="p">(</span><span class="s2">&#34;a31&#34;</span><span class="p">,</span> <span class="s2">&#34;option_battle_speed&#34;</span><span class="p">),</span> <span class="p">(</span><span class="s2">&#34;a32&#34;</span><span class="p">,</span> <span class="s2">&#34;map_tap_now&#34;</span><span class="p">)]:</span>
</span></span><span class="line"><span class="cl">    <span class="nb">print</span><span class="p">(</span><span class="sa">f</span><span class="s2">&#34;   </span><span class="si">{</span><span class="n">k</span><span class="si">:</span><span class="s2">&gt;5</span><span class="si">}</span><span class="s2"> = </span><span class="si">{</span><span class="n">u</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="n">k</span><span class="p">)</span><span class="si">!r:</span><span class="s2">&gt;12</span><span class="si">}</span><span class="s2">   </span><span class="si">{</span><span class="n">what</span><span class="si">}</span><span class="s2">&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="nb">print</span><span class="p">(</span><span class="s2">&#34;   h1..h10 =&#34;</span><span class="p">,</span> <span class="p">[</span><span class="n">u</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s1">&#39;h</span><span class="si">%d</span><span class="s1">&#39;</span> <span class="o">%</span> <span class="n">i</span><span class="p">)</span> <span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="mi">11</span><span class="p">)])</span>
</span></span><span class="line"><span class="cl"><span class="nb">print</span><span class="p">(</span><span class="s2">&#34;   h41..h48 =&#34;</span><span class="p">,</span> <span class="p">[</span><span class="n">u</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s1">&#39;h</span><span class="si">%d</span><span class="s1">&#39;</span> <span class="o">%</span> <span class="n">i</span><span class="p">)</span> <span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="mi">41</span><span class="p">,</span> <span class="mi">49</span><span class="p">)])</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># crash simulation: every key the load code reads for check_event_sub2_* / mission_clear</span>
</span></span><span class="line"><span class="cl"><span class="n">ev</span> <span class="o">=</span> <span class="p">{</span><span class="n">k</span><span class="p">:</span> <span class="n">u</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="n">k</span><span class="p">)</span> <span class="k">for</span> <span class="n">k</span> <span class="ow">in</span> <span class="p">(</span><span class="s1">&#39;h1&#39;</span><span class="p">,</span> <span class="s1">&#39;h2&#39;</span><span class="p">,</span> <span class="s1">&#39;h3&#39;</span><span class="p">,</span> <span class="s1">&#39;h4&#39;</span><span class="p">,</span> <span class="s1">&#39;h5&#39;</span><span class="p">)}</span>
</span></span><span class="line"><span class="cl"><span class="nb">print</span><span class="p">(</span><span class="s2">&#34;   event flags -&gt;&#34;</span><span class="p">,</span> <span class="n">ev</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="nb">print</span><span class="p">(</span><span class="s2">&#34;   mission_clear-ish fields present:&#34;</span><span class="p">,</span> <span class="nb">sum</span><span class="p">(</span><span class="mi">1</span> <span class="k">for</span> <span class="n">k</span> <span class="ow">in</span> <span class="n">u</span> <span class="k">if</span> <span class="n">k</span><span class="o">.</span><span class="n">startswith</span><span class="p">(</span><span class="s1">&#39;g&#39;</span><span class="p">)))</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">out_dir</span> <span class="o">=</span> <span class="n">os</span><span class="o">.</span><span class="n">path</span><span class="o">.</span><span class="n">join</span><span class="p">(</span><span class="n">BASE</span><span class="p">,</span> <span class="s2">&#34;cloud_build&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="n">os</span><span class="o">.</span><span class="n">makedirs</span><span class="p">(</span><span class="n">out_dir</span><span class="p">,</span> <span class="n">exist_ok</span><span class="o">=</span><span class="kc">True</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="k">for</span> <span class="n">blob</span><span class="p">,</span> <span class="n">obj</span> <span class="ow">in</span> <span class="n">built</span><span class="o">.</span><span class="n">items</span><span class="p">():</span>
</span></span><span class="line"><span class="cl">    <span class="n">raw</span> <span class="o">=</span> <span class="n">json</span><span class="o">.</span><span class="n">dumps</span><span class="p">(</span><span class="n">obj</span><span class="p">,</span> <span class="n">separators</span><span class="o">=</span><span class="p">(</span><span class="s1">&#39;,&#39;</span><span class="p">,</span> <span class="s1">&#39;:&#39;</span><span class="p">))</span>
</span></span><span class="line"><span class="cl">    <span class="nb">open</span><span class="p">(</span><span class="n">os</span><span class="o">.</span><span class="n">path</span><span class="o">.</span><span class="n">join</span><span class="p">(</span><span class="n">out_dir</span><span class="p">,</span> <span class="n">blob</span> <span class="o">+</span> <span class="s2">&#34;.json&#34;</span><span class="p">),</span> <span class="s2">&#34;w&#34;</span><span class="p">,</span> <span class="n">encoding</span><span class="o">=</span><span class="s2">&#34;utf-8&#34;</span><span class="p">)</span><span class="o">.</span><span class="n">write</span><span class="p">(</span><span class="n">raw</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="nb">print</span><span class="p">(</span><span class="sa">f</span><span class="s2">&#34;</span><span class="se">\n</span><span class="s2">[i] wrote cloud_build/</span><span class="si">{</span><span class="n">blob</span><span class="si">}</span><span class="s2">.json  (</span><span class="si">{</span><span class="nb">len</span><span class="p">(</span><span class="n">raw</span><span class="p">)</span><span class="si">}</span><span class="s2"> bytes, </span><span class="si">{</span><span class="nb">len</span><span class="p">(</span><span class="n">obj</span><span class="p">)</span><span class="si">}</span><span class="s2"> fields)&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="nb">print</span><span class="p">(</span><span class="s2">&#34;</span><span class="se">\n</span><span class="s2">[i] current cloud sizes:&#34;</span><span class="p">,</span> <span class="p">{</span><span class="n">b</span><span class="p">:</span> <span class="n">os</span><span class="o">.</span><span class="n">path</span><span class="o">.</span><span class="n">getsize</span><span class="p">(</span><span class="n">os</span><span class="o">.</span><span class="n">path</span><span class="o">.</span><span class="n">join</span><span class="p">(</span><span class="n">CLOUD</span><span class="p">,</span> <span class="n">b</span> <span class="o">+</span> <span class="s2">&#34;.json&#34;</span><span class="p">))</span> <span class="k">for</span> <span class="n">b</span> <span class="ow">in</span> <span class="n">built</span><span class="p">})</span></span></span></code></pre></div></div>

  </div>
</details>




  










<details
  class="group border-none"
  data-accordion-item
  
>
  <summary class="flex w-full cursor-pointer items-center justify-between gap-4 px-4 py-3 text-left text-lg font-semibold text-neutral-900 dark:text-neutral-100">
    <span class="flex items-center gap-2">
      
      <span>playfab_push.py（99 行）：备份、写回、回读校验</span>
    </span>
    <span class="accordion-chevron ms-auto flex h-5 w-5 items-center justify-center print:hidden">
      <span class="relative block icon"><svg
  xmlns="http://www.w3.org/2000/svg"
  viewBox="0 0 20 20"
  fill="currentColor"
  aria-hidden="true"
>
  <path
    fill-rule="evenodd"
    d="M5.23 7.21a.75.75 0 011.06.02L10 11.168l3.71-3.938a.75.75 0 111.08 1.04l-4.25 4.5a.75.75 0 01-1.08 0l-4.25-4.5a.75.75 0 01.02-1.06z"
    clip-rule="evenodd"
  />
</svg>
</span>
    </span>
  </summary>
<div class="px-4 pb-4 text-neutral-700 dark:text-neutral-300">
    <div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="ch">#!/usr/bin/env python</span>
</span></span><span class="line"><span class="cl"><span class="s2">&#34;&#34;&#34;Push the rebuilt cloud blobs (cloud_build/*.json) to the user&#39;s PlayFab save.
</span></span></span><span class="line"><span class="cl"><span class="s2">
</span></span></span><span class="line"><span class="cl"><span class="s2">Backs up the current values first, then Client/UpdateUserData, then re-reads the
</span></span></span><span class="line"><span class="cl"><span class="s2">cloud and verifies. Never prints the session ticket.
</span></span></span><span class="line"><span class="cl"><span class="s2">&#34;&#34;&#34;</span>
</span></span><span class="line"><span class="cl"><span class="kn">import</span> <span class="nn">json</span><span class="o">,</span> <span class="nn">os</span><span class="o">,</span> <span class="nn">shutil</span><span class="o">,</span> <span class="nn">sys</span><span class="o">,</span> <span class="nn">time</span><span class="o">,</span> <span class="nn">urllib.request</span><span class="o">,</span> <span class="nn">urllib.error</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">TITLE</span> <span class="o">=</span> <span class="s2">&#34;142A3&#34;</span>
</span></span><span class="line"><span class="cl"><span class="n">BASE</span> <span class="o">=</span> <span class="n">os</span><span class="o">.</span><span class="n">path</span><span class="o">.</span><span class="n">dirname</span><span class="p">(</span><span class="n">os</span><span class="o">.</span><span class="n">path</span><span class="o">.</span><span class="n">abspath</span><span class="p">(</span><span class="vm">__file__</span><span class="p">))</span>
</span></span><span class="line"><span class="cl"><span class="n">CLOUD</span> <span class="o">=</span> <span class="n">os</span><span class="o">.</span><span class="n">path</span><span class="o">.</span><span class="n">join</span><span class="p">(</span><span class="n">BASE</span><span class="p">,</span> <span class="s2">&#34;cloud_raw&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="n">BUILD</span> <span class="o">=</span> <span class="n">os</span><span class="o">.</span><span class="n">path</span><span class="o">.</span><span class="n">join</span><span class="p">(</span><span class="n">BASE</span><span class="p">,</span> <span class="s2">&#34;cloud_build&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="n">BACKUP</span> <span class="o">=</span> <span class="n">os</span><span class="o">.</span><span class="n">path</span><span class="o">.</span><span class="n">join</span><span class="p">(</span><span class="n">BASE</span><span class="p">,</span> <span class="s2">&#34;cloud_backup&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">post</span><span class="p">(</span><span class="n">path</span><span class="p">,</span> <span class="n">body</span><span class="p">,</span> <span class="n">ticket</span><span class="o">=</span><span class="kc">None</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">    <span class="n">req</span> <span class="o">=</span> <span class="n">urllib</span><span class="o">.</span><span class="n">request</span><span class="o">.</span><span class="n">Request</span><span class="p">(</span><span class="sa">f</span><span class="s2">&#34;https://</span><span class="si">{</span><span class="n">TITLE</span><span class="si">}</span><span class="s2">.playfabapi.com/</span><span class="si">{</span><span class="n">path</span><span class="si">}</span><span class="s2">&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                                 <span class="n">data</span><span class="o">=</span><span class="n">json</span><span class="o">.</span><span class="n">dumps</span><span class="p">(</span><span class="n">body</span><span class="p">)</span><span class="o">.</span><span class="n">encode</span><span class="p">(),</span> <span class="n">method</span><span class="o">=</span><span class="s2">&#34;POST&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">req</span><span class="o">.</span><span class="n">add_header</span><span class="p">(</span><span class="s2">&#34;Content-Type&#34;</span><span class="p">,</span> <span class="s2">&#34;application/json&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="n">ticket</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">        <span class="n">req</span><span class="o">.</span><span class="n">add_header</span><span class="p">(</span><span class="s2">&#34;X-Authorization&#34;</span><span class="p">,</span> <span class="n">ticket</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">try</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">        <span class="k">with</span> <span class="n">urllib</span><span class="o">.</span><span class="n">request</span><span class="o">.</span><span class="n">urlopen</span><span class="p">(</span><span class="n">req</span><span class="p">,</span> <span class="n">timeout</span><span class="o">=</span><span class="mi">90</span><span class="p">)</span> <span class="k">as</span> <span class="n">r</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">            <span class="k">return</span> <span class="n">r</span><span class="o">.</span><span class="n">status</span><span class="p">,</span> <span class="n">json</span><span class="o">.</span><span class="n">loads</span><span class="p">(</span><span class="n">r</span><span class="o">.</span><span class="n">read</span><span class="p">()</span><span class="o">.</span><span class="n">decode</span><span class="p">())</span>
</span></span><span class="line"><span class="cl">    <span class="k">except</span> <span class="n">urllib</span><span class="o">.</span><span class="n">error</span><span class="o">.</span><span class="n">HTTPError</span> <span class="k">as</span> <span class="n">e</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">        <span class="n">raw</span> <span class="o">=</span> <span class="n">e</span><span class="o">.</span><span class="n">read</span><span class="p">()</span><span class="o">.</span><span class="n">decode</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">        <span class="k">try</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">            <span class="k">return</span> <span class="n">e</span><span class="o">.</span><span class="n">code</span><span class="p">,</span> <span class="n">json</span><span class="o">.</span><span class="n">loads</span><span class="p">(</span><span class="n">raw</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="k">except</span> <span class="ne">Exception</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">            <span class="k">return</span> <span class="n">e</span><span class="o">.</span><span class="n">code</span><span class="p">,</span> <span class="p">{</span><span class="s2">&#34;raw&#34;</span><span class="p">:</span> <span class="n">raw</span><span class="p">[:</span><span class="mi">2000</span><span class="p">]}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">main</span><span class="p">():</span>
</span></span><span class="line"><span class="cl">    <span class="n">args</span> <span class="o">=</span> <span class="p">[</span><span class="n">a</span> <span class="k">for</span> <span class="n">a</span> <span class="ow">in</span> <span class="n">sys</span><span class="o">.</span><span class="n">argv</span><span class="p">[</span><span class="mi">1</span><span class="p">:]</span> <span class="k">if</span> <span class="ow">not</span> <span class="n">a</span><span class="o">.</span><span class="n">startswith</span><span class="p">(</span><span class="s2">&#34;-&#34;</span><span class="p">)]</span>
</span></span><span class="line"><span class="cl">    <span class="n">dry</span> <span class="o">=</span> <span class="s2">&#34;--dry&#34;</span> <span class="ow">in</span> <span class="n">sys</span><span class="o">.</span><span class="n">argv</span>
</span></span><span class="line"><span class="cl">    <span class="n">pid</span> <span class="o">=</span> <span class="n">args</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span><span class="o">.</span><span class="n">strip</span><span class="p">()</span> <span class="k">if</span> <span class="n">args</span> <span class="k">else</span> <span class="nb">open</span><span class="p">(</span><span class="n">os</span><span class="o">.</span><span class="n">path</span><span class="o">.</span><span class="n">join</span><span class="p">(</span><span class="n">BASE</span><span class="p">,</span> <span class="s2">&#34;player_id.txt&#34;</span><span class="p">))</span><span class="o">.</span><span class="n">read</span><span class="p">()</span><span class="o">.</span><span class="n">strip</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="n">st</span><span class="p">,</span> <span class="n">resp</span> <span class="o">=</span> <span class="n">post</span><span class="p">(</span><span class="s2">&#34;Client/LoginWithCustomID&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                    <span class="p">{</span><span class="s2">&#34;TitleId&#34;</span><span class="p">:</span> <span class="n">TITLE</span><span class="p">,</span> <span class="s2">&#34;CustomId&#34;</span><span class="p">:</span> <span class="n">pid</span><span class="p">,</span> <span class="s2">&#34;CreateAccount&#34;</span><span class="p">:</span> <span class="kc">False</span><span class="p">})</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="n">resp</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s2">&#34;code&#34;</span><span class="p">)</span> <span class="o">!=</span> <span class="mi">200</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">        <span class="nb">print</span><span class="p">(</span><span class="s2">&#34;[x] login failed:&#34;</span><span class="p">,</span> <span class="n">resp</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s2">&#34;errorCode&#34;</span><span class="p">),</span> <span class="n">resp</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s2">&#34;errorMessage&#34;</span><span class="p">));</span> <span class="k">return</span> <span class="mi">1</span>
</span></span><span class="line"><span class="cl">    <span class="n">ticket</span> <span class="o">=</span> <span class="n">resp</span><span class="p">[</span><span class="s2">&#34;data&#34;</span><span class="p">][</span><span class="s2">&#34;SessionTicket&#34;</span><span class="p">]</span>
</span></span><span class="line"><span class="cl">    <span class="nb">print</span><span class="p">(</span><span class="s2">&#34;[+] logged in, PlayFabId:&#34;</span><span class="p">,</span> <span class="n">resp</span><span class="p">[</span><span class="s2">&#34;data&#34;</span><span class="p">]</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s2">&#34;PlayFabId&#34;</span><span class="p">))</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="c1"># ---- backup current cloud values ----</span>
</span></span><span class="line"><span class="cl">    <span class="n">st</span><span class="p">,</span> <span class="n">resp</span> <span class="o">=</span> <span class="n">post</span><span class="p">(</span><span class="s2">&#34;Client/GetUserData&#34;</span><span class="p">,</span> <span class="p">{},</span> <span class="n">ticket</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="n">resp</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s2">&#34;code&#34;</span><span class="p">)</span> <span class="o">!=</span> <span class="mi">200</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">        <span class="nb">print</span><span class="p">(</span><span class="s2">&#34;[x] GetUserData failed:&#34;</span><span class="p">,</span> <span class="n">resp</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s2">&#34;errorCode&#34;</span><span class="p">));</span> <span class="k">return</span> <span class="mi">1</span>
</span></span><span class="line"><span class="cl">    <span class="n">cur</span> <span class="o">=</span> <span class="n">resp</span><span class="p">[</span><span class="s2">&#34;data&#34;</span><span class="p">][</span><span class="s2">&#34;Data&#34;</span><span class="p">]</span>
</span></span><span class="line"><span class="cl">    <span class="n">ts</span> <span class="o">=</span> <span class="n">time</span><span class="o">.</span><span class="n">strftime</span><span class="p">(</span><span class="s2">&#34;%Y%m</span><span class="si">%d</span><span class="s2">-%H%M%S&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">os</span><span class="o">.</span><span class="n">makedirs</span><span class="p">(</span><span class="n">BACKUP</span><span class="p">,</span> <span class="n">exist_ok</span><span class="o">=</span><span class="kc">True</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">bpath</span> <span class="o">=</span> <span class="n">os</span><span class="o">.</span><span class="n">path</span><span class="o">.</span><span class="n">join</span><span class="p">(</span><span class="n">BACKUP</span><span class="p">,</span> <span class="sa">f</span><span class="s2">&#34;cloud_backup_</span><span class="si">{</span><span class="n">ts</span><span class="si">}</span><span class="s2">.json&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">json</span><span class="o">.</span><span class="n">dump</span><span class="p">({</span><span class="n">k</span><span class="p">:</span> <span class="p">{</span><span class="s2">&#34;Value&#34;</span><span class="p">:</span> <span class="n">v</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s2">&#34;Value&#34;</span><span class="p">),</span> <span class="s2">&#34;LastUpdated&#34;</span><span class="p">:</span> <span class="n">v</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s2">&#34;LastUpdated&#34;</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">                   <span class="s2">&#34;Permission&#34;</span><span class="p">:</span> <span class="n">v</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s2">&#34;Permission&#34;</span><span class="p">)}</span> <span class="k">for</span> <span class="n">k</span><span class="p">,</span> <span class="n">v</span> <span class="ow">in</span> <span class="n">cur</span><span class="o">.</span><span class="n">items</span><span class="p">()},</span>
</span></span><span class="line"><span class="cl">              <span class="nb">open</span><span class="p">(</span><span class="n">bpath</span><span class="p">,</span> <span class="s2">&#34;w&#34;</span><span class="p">,</span> <span class="n">encoding</span><span class="o">=</span><span class="s2">&#34;utf-8&#34;</span><span class="p">),</span> <span class="n">indent</span><span class="o">=</span><span class="mi">1</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="nb">print</span><span class="p">(</span><span class="sa">f</span><span class="s2">&#34;[+] backed up current cloud data -&gt; </span><span class="si">{</span><span class="n">bpath</span><span class="si">}</span><span class="s2">&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">for</span> <span class="n">k</span><span class="p">,</span> <span class="n">v</span> <span class="ow">in</span> <span class="nb">sorted</span><span class="p">(</span><span class="n">cur</span><span class="o">.</span><span class="n">items</span><span class="p">()):</span>
</span></span><span class="line"><span class="cl">        <span class="nb">print</span><span class="p">(</span><span class="sa">f</span><span class="s2">&#34;    </span><span class="si">{</span><span class="n">k</span><span class="si">}</span><span class="s2">: </span><span class="si">{</span><span class="nb">len</span><span class="p">(</span><span class="n">v</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s1">&#39;Value&#39;</span><span class="p">)</span> <span class="ow">or</span> <span class="s1">&#39;&#39;</span><span class="p">)</span><span class="si">}</span><span class="s2"> bytes, LastUpdated </span><span class="si">{</span><span class="n">v</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s1">&#39;LastUpdated&#39;</span><span class="p">)</span><span class="si">}</span><span class="s2">, </span><span class="si">{</span><span class="n">v</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s1">&#39;Permission&#39;</span><span class="p">)</span><span class="si">}</span><span class="s2">&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="n">payload</span> <span class="o">=</span> <span class="p">{}</span>
</span></span><span class="line"><span class="cl">    <span class="k">for</span> <span class="n">blob</span> <span class="ow">in</span> <span class="p">(</span><span class="s2">&#34;userdata&#34;</span><span class="p">,</span> <span class="s2">&#34;unitdata&#34;</span><span class="p">,</span> <span class="s2">&#34;etcdata&#34;</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">        <span class="n">payload</span><span class="p">[</span><span class="n">blob</span><span class="p">]</span> <span class="o">=</span> <span class="nb">open</span><span class="p">(</span><span class="n">os</span><span class="o">.</span><span class="n">path</span><span class="o">.</span><span class="n">join</span><span class="p">(</span><span class="n">BUILD</span><span class="p">,</span> <span class="n">blob</span> <span class="o">+</span> <span class="s2">&#34;.json&#34;</span><span class="p">),</span> <span class="n">encoding</span><span class="o">=</span><span class="s2">&#34;utf-8&#34;</span><span class="p">)</span><span class="o">.</span><span class="n">read</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">    <span class="nb">print</span><span class="p">(</span><span class="s2">&#34;[i] upload sizes:&#34;</span><span class="p">,</span> <span class="p">{</span><span class="n">k</span><span class="p">:</span> <span class="nb">len</span><span class="p">(</span><span class="n">v</span><span class="p">)</span> <span class="k">for</span> <span class="n">k</span><span class="p">,</span> <span class="n">v</span> <span class="ow">in</span> <span class="n">payload</span><span class="o">.</span><span class="n">items</span><span class="p">()})</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="n">dry</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">        <span class="nb">print</span><span class="p">(</span><span class="s2">&#34;[dry-run] not uploading&#34;</span><span class="p">);</span> <span class="k">return</span> <span class="mi">0</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="n">st</span><span class="p">,</span> <span class="n">resp</span> <span class="o">=</span> <span class="n">post</span><span class="p">(</span><span class="s2">&#34;Client/UpdateUserData&#34;</span><span class="p">,</span> <span class="p">{</span><span class="s2">&#34;Data&#34;</span><span class="p">:</span> <span class="n">payload</span><span class="p">},</span> <span class="n">ticket</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="nb">print</span><span class="p">(</span><span class="s2">&#34;[upload] http&#34;</span><span class="p">,</span> <span class="n">st</span><span class="p">,</span> <span class="s2">&#34;-&gt;&#34;</span><span class="p">,</span> <span class="n">resp</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s2">&#34;code&#34;</span><span class="p">),</span> <span class="n">resp</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s2">&#34;status&#34;</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">          <span class="n">resp</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s2">&#34;errorCode&#34;</span><span class="p">)</span> <span class="ow">or</span> <span class="s2">&#34;&#34;</span><span class="p">,</span> <span class="n">resp</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s2">&#34;errorMessage&#34;</span><span class="p">)</span> <span class="ow">or</span> <span class="s2">&#34;&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="n">resp</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s2">&#34;code&#34;</span><span class="p">)</span> <span class="o">!=</span> <span class="mi">200</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">        <span class="nb">print</span><span class="p">(</span><span class="s2">&#34;[x] upload FAILED; backup:&#34;</span><span class="p">,</span> <span class="n">bpath</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="mi">1</span>
</span></span><span class="line"><span class="cl">    <span class="nb">print</span><span class="p">(</span><span class="s2">&#34;[+] UpdateUserData accepted:&#34;</span><span class="p">,</span> <span class="n">resp</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s2">&#34;data&#34;</span><span class="p">))</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="c1"># ---- verify by reading back ----</span>
</span></span><span class="line"><span class="cl">    <span class="n">st</span><span class="p">,</span> <span class="n">resp</span> <span class="o">=</span> <span class="n">post</span><span class="p">(</span><span class="s2">&#34;Client/GetUserData&#34;</span><span class="p">,</span> <span class="p">{},</span> <span class="n">ticket</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">data</span> <span class="o">=</span> <span class="n">resp</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s2">&#34;data&#34;</span><span class="p">,</span> <span class="p">{})</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s2">&#34;Data&#34;</span><span class="p">,</span> <span class="p">{})</span> <span class="ow">or</span> <span class="p">{}</span>
</span></span><span class="line"><span class="cl">    <span class="nb">print</span><span class="p">(</span><span class="s2">&#34;[i] read-back raw:&#34;</span><span class="p">,</span> <span class="n">json</span><span class="o">.</span><span class="n">dumps</span><span class="p">(</span><span class="n">resp</span><span class="p">)[:</span><span class="mi">400</span><span class="p">])</span>
</span></span><span class="line"><span class="cl">    <span class="n">ok</span> <span class="o">=</span> <span class="kc">True</span>
</span></span><span class="line"><span class="cl">    <span class="k">for</span> <span class="n">k</span> <span class="ow">in</span> <span class="nb">sorted</span><span class="p">(</span><span class="n">payload</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="n">k</span> <span class="ow">not</span> <span class="ow">in</span> <span class="n">data</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">            <span class="nb">print</span><span class="p">(</span><span class="sa">f</span><span class="s2">&#34;    </span><span class="si">{</span><span class="n">k</span><span class="si">}</span><span class="s2">: MISSING after upload&#34;</span><span class="p">);</span> <span class="n">ok</span> <span class="o">=</span> <span class="kc">False</span><span class="p">;</span> <span class="k">continue</span>
</span></span><span class="line"><span class="cl">        <span class="n">val</span> <span class="o">=</span> <span class="n">data</span><span class="p">[</span><span class="n">k</span><span class="p">]</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s2">&#34;Value&#34;</span><span class="p">)</span> <span class="ow">or</span> <span class="s2">&#34;&#34;</span>
</span></span><span class="line"><span class="cl">        <span class="n">same</span> <span class="o">=</span> <span class="n">val</span> <span class="o">==</span> <span class="n">payload</span><span class="p">[</span><span class="n">k</span><span class="p">]</span>
</span></span><span class="line"><span class="cl">        <span class="n">ok</span> <span class="o">&amp;=</span> <span class="n">same</span>
</span></span><span class="line"><span class="cl">        <span class="nb">print</span><span class="p">(</span><span class="sa">f</span><span class="s2">&#34;    </span><span class="si">{</span><span class="n">k</span><span class="si">}</span><span class="s2">: </span><span class="si">{</span><span class="nb">len</span><span class="p">(</span><span class="n">val</span><span class="p">)</span><span class="si">}</span><span class="s2"> bytes, LastUpdated </span><span class="si">{</span><span class="n">data</span><span class="p">[</span><span class="n">k</span><span class="p">]</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s1">&#39;LastUpdated&#39;</span><span class="p">)</span><span class="si">}</span><span class="s2">, &#34;</span>
</span></span><span class="line"><span class="cl">              <span class="sa">f</span><span class="s2">&#34;</span><span class="si">{</span><span class="n">data</span><span class="p">[</span><span class="n">k</span><span class="p">]</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s1">&#39;Permission&#39;</span><span class="p">)</span><span class="si">}</span><span class="s2">, matches_upload=</span><span class="si">{</span><span class="n">same</span><span class="si">}</span><span class="s2">&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="n">ok</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">        <span class="n">u</span> <span class="o">=</span> <span class="n">json</span><span class="o">.</span><span class="n">loads</span><span class="p">(</span><span class="nb">open</span><span class="p">(</span><span class="n">os</span><span class="o">.</span><span class="n">path</span><span class="o">.</span><span class="n">join</span><span class="p">(</span><span class="n">BUILD</span><span class="p">,</span> <span class="s2">&#34;userdata.json&#34;</span><span class="p">),</span> <span class="n">encoding</span><span class="o">=</span><span class="s2">&#34;utf-8&#34;</span><span class="p">)</span><span class="o">.</span><span class="n">read</span><span class="p">())</span>
</span></span><span class="line"><span class="cl">        <span class="nb">print</span><span class="p">(</span><span class="s2">&#34;[verify] p1_select(a27) =&#34;</span><span class="p">,</span> <span class="n">u</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s2">&#34;a27&#34;</span><span class="p">),</span> <span class="s2">&#34;| DAY(a7) =&#34;</span><span class="p">,</span> <span class="n">u</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s2">&#34;a7&#34;</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">              <span class="s2">&#34;| difficulty(a28) =&#34;</span><span class="p">,</span> <span class="n">u</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s2">&#34;a28&#34;</span><span class="p">),</span> <span class="s2">&#34;| check_event_sub2_1(h1) =&#34;</span><span class="p">,</span> <span class="n">u</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s2">&#34;h1&#34;</span><span class="p">))</span>
</span></span><span class="line"><span class="cl">        <span class="nb">print</span><span class="p">(</span><span class="s2">&#34;[+] cloud save replaced and verified.&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">else</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">        <span class="nb">print</span><span class="p">(</span><span class="s2">&#34;[!] verify mismatch&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="mi">0</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">if</span> <span class="vm">__name__</span> <span class="o">==</span> <span class="s2">&#34;__main__&#34;</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">    <span class="n">sys</span><span class="o">.</span><span class="n">exit</span><span class="p">(</span><span class="n">main</span><span class="p">())</span></span></span></code></pre></div></div>

  </div>
</details>




  










<details
  class="group border-none"
  data-accordion-item
  
>
  <summary class="flex w-full cursor-pointer items-center justify-between gap-4 px-4 py-3 text-left text-lg font-semibold text-neutral-900 dark:text-neutral-100">
    <span class="flex items-center gap-2">
      
      <span>playfab_restore.py（51 行）：用备份回滚云端存档</span>
    </span>
    <span class="accordion-chevron ms-auto flex h-5 w-5 items-center justify-center print:hidden">
      <span class="relative block icon"><svg
  xmlns="http://www.w3.org/2000/svg"
  viewBox="0 0 20 20"
  fill="currentColor"
  aria-hidden="true"
>
  <path
    fill-rule="evenodd"
    d="M5.23 7.21a.75.75 0 011.06.02L10 11.168l3.71-3.938a.75.75 0 111.08 1.04l-4.25 4.5a.75.75 0 01-1.08 0l-4.25-4.5a.75.75 0 01.02-1.06z"
    clip-rule="evenodd"
  />
</svg>
</span>
    </span>
  </summary>
<div class="px-4 pb-4 text-neutral-700 dark:text-neutral-300">
    <div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="ch">#!/usr/bin/env python</span>
</span></span><span class="line"><span class="cl"><span class="s2">&#34;&#34;&#34;Restore the PlayFab cloud save from a backup file made by playfab_push.py.
</span></span></span><span class="line"><span class="cl"><span class="s2">
</span></span></span><span class="line"><span class="cl"><span class="s2">Usage: python playfab_restore.py cloud_backup/cloud_backup_YYYYmmdd-HHMMSS.json
</span></span></span><span class="line"><span class="cl"><span class="s2">&#34;&#34;&#34;</span>
</span></span><span class="line"><span class="cl"><span class="kn">import</span> <span class="nn">json</span><span class="o">,</span> <span class="nn">os</span><span class="o">,</span> <span class="nn">sys</span><span class="o">,</span> <span class="nn">urllib.request</span><span class="o">,</span> <span class="nn">urllib.error</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">TITLE</span> <span class="o">=</span> <span class="s2">&#34;142A3&#34;</span>
</span></span><span class="line"><span class="cl"><span class="n">BASE</span> <span class="o">=</span> <span class="n">os</span><span class="o">.</span><span class="n">path</span><span class="o">.</span><span class="n">dirname</span><span class="p">(</span><span class="n">os</span><span class="o">.</span><span class="n">path</span><span class="o">.</span><span class="n">abspath</span><span class="p">(</span><span class="vm">__file__</span><span class="p">))</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">post</span><span class="p">(</span><span class="n">path</span><span class="p">,</span> <span class="n">body</span><span class="p">,</span> <span class="n">ticket</span><span class="o">=</span><span class="kc">None</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">    <span class="n">req</span> <span class="o">=</span> <span class="n">urllib</span><span class="o">.</span><span class="n">request</span><span class="o">.</span><span class="n">Request</span><span class="p">(</span><span class="sa">f</span><span class="s2">&#34;https://</span><span class="si">{</span><span class="n">TITLE</span><span class="si">}</span><span class="s2">.playfabapi.com/</span><span class="si">{</span><span class="n">path</span><span class="si">}</span><span class="s2">&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                                 <span class="n">data</span><span class="o">=</span><span class="n">json</span><span class="o">.</span><span class="n">dumps</span><span class="p">(</span><span class="n">body</span><span class="p">)</span><span class="o">.</span><span class="n">encode</span><span class="p">(),</span> <span class="n">method</span><span class="o">=</span><span class="s2">&#34;POST&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">req</span><span class="o">.</span><span class="n">add_header</span><span class="p">(</span><span class="s2">&#34;Content-Type&#34;</span><span class="p">,</span> <span class="s2">&#34;application/json&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="n">ticket</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">        <span class="n">req</span><span class="o">.</span><span class="n">add_header</span><span class="p">(</span><span class="s2">&#34;X-Authorization&#34;</span><span class="p">,</span> <span class="n">ticket</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">try</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">        <span class="k">with</span> <span class="n">urllib</span><span class="o">.</span><span class="n">request</span><span class="o">.</span><span class="n">urlopen</span><span class="p">(</span><span class="n">req</span><span class="p">,</span> <span class="n">timeout</span><span class="o">=</span><span class="mi">90</span><span class="p">)</span> <span class="k">as</span> <span class="n">r</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">            <span class="k">return</span> <span class="n">r</span><span class="o">.</span><span class="n">status</span><span class="p">,</span> <span class="n">json</span><span class="o">.</span><span class="n">loads</span><span class="p">(</span><span class="n">r</span><span class="o">.</span><span class="n">read</span><span class="p">()</span><span class="o">.</span><span class="n">decode</span><span class="p">())</span>
</span></span><span class="line"><span class="cl">    <span class="k">except</span> <span class="n">urllib</span><span class="o">.</span><span class="n">error</span><span class="o">.</span><span class="n">HTTPError</span> <span class="k">as</span> <span class="n">e</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">        <span class="n">raw</span> <span class="o">=</span> <span class="n">e</span><span class="o">.</span><span class="n">read</span><span class="p">()</span><span class="o">.</span><span class="n">decode</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">        <span class="k">try</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">            <span class="k">return</span> <span class="n">e</span><span class="o">.</span><span class="n">code</span><span class="p">,</span> <span class="n">json</span><span class="o">.</span><span class="n">loads</span><span class="p">(</span><span class="n">raw</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="k">except</span> <span class="ne">Exception</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">            <span class="k">return</span> <span class="n">e</span><span class="o">.</span><span class="n">code</span><span class="p">,</span> <span class="p">{</span><span class="s2">&#34;raw&#34;</span><span class="p">:</span> <span class="n">raw</span><span class="p">[:</span><span class="mi">2000</span><span class="p">]}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">main</span><span class="p">():</span>
</span></span><span class="line"><span class="cl">    <span class="n">path</span> <span class="o">=</span> <span class="n">sys</span><span class="o">.</span><span class="n">argv</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span>
</span></span><span class="line"><span class="cl">    <span class="n">pid</span> <span class="o">=</span> <span class="nb">open</span><span class="p">(</span><span class="n">os</span><span class="o">.</span><span class="n">path</span><span class="o">.</span><span class="n">join</span><span class="p">(</span><span class="n">BASE</span><span class="p">,</span> <span class="s2">&#34;player_id.txt&#34;</span><span class="p">))</span><span class="o">.</span><span class="n">read</span><span class="p">()</span><span class="o">.</span><span class="n">strip</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">    <span class="n">st</span><span class="p">,</span> <span class="n">resp</span> <span class="o">=</span> <span class="n">post</span><span class="p">(</span><span class="s2">&#34;Client/LoginWithCustomID&#34;</span><span class="p">,</span> <span class="p">{</span><span class="s2">&#34;TitleId&#34;</span><span class="p">:</span> <span class="n">TITLE</span><span class="p">,</span> <span class="s2">&#34;CustomId&#34;</span><span class="p">:</span> <span class="n">pid</span><span class="p">,</span> <span class="s2">&#34;CreateAccount&#34;</span><span class="p">:</span> <span class="kc">False</span><span class="p">})</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="n">resp</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s2">&#34;code&#34;</span><span class="p">)</span> <span class="o">!=</span> <span class="mi">200</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">        <span class="nb">print</span><span class="p">(</span><span class="s2">&#34;[x] login failed:&#34;</span><span class="p">,</span> <span class="n">resp</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s2">&#34;errorCode&#34;</span><span class="p">),</span> <span class="n">resp</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s2">&#34;errorMessage&#34;</span><span class="p">));</span> <span class="k">return</span> <span class="mi">1</span>
</span></span><span class="line"><span class="cl">    <span class="n">ticket</span> <span class="o">=</span> <span class="n">resp</span><span class="p">[</span><span class="s2">&#34;data&#34;</span><span class="p">][</span><span class="s2">&#34;SessionTicket&#34;</span><span class="p">]</span>
</span></span><span class="line"><span class="cl">    <span class="n">backup</span> <span class="o">=</span> <span class="n">json</span><span class="o">.</span><span class="n">load</span><span class="p">(</span><span class="nb">open</span><span class="p">(</span><span class="n">path</span><span class="p">,</span> <span class="n">encoding</span><span class="o">=</span><span class="s2">&#34;utf-8&#34;</span><span class="p">))</span>
</span></span><span class="line"><span class="cl">    <span class="n">payload</span> <span class="o">=</span> <span class="p">{</span><span class="n">k</span><span class="p">:</span> <span class="p">(</span><span class="n">v</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s2">&#34;Value&#34;</span><span class="p">)</span> <span class="k">if</span> <span class="nb">isinstance</span><span class="p">(</span><span class="n">v</span><span class="p">,</span> <span class="nb">dict</span><span class="p">)</span> <span class="k">else</span> <span class="n">v</span><span class="p">)</span> <span class="k">for</span> <span class="n">k</span><span class="p">,</span> <span class="n">v</span> <span class="ow">in</span> <span class="n">backup</span><span class="o">.</span><span class="n">items</span><span class="p">()}</span>
</span></span><span class="line"><span class="cl">    <span class="nb">print</span><span class="p">(</span><span class="s2">&#34;[i] restoring&#34;</span><span class="p">,</span> <span class="p">{</span><span class="n">k</span><span class="p">:</span> <span class="nb">len</span><span class="p">(</span><span class="n">v</span> <span class="ow">or</span> <span class="s2">&#34;&#34;</span><span class="p">)</span> <span class="k">for</span> <span class="n">k</span><span class="p">,</span> <span class="n">v</span> <span class="ow">in</span> <span class="n">payload</span><span class="o">.</span><span class="n">items</span><span class="p">()})</span>
</span></span><span class="line"><span class="cl">    <span class="n">st</span><span class="p">,</span> <span class="n">resp</span> <span class="o">=</span> <span class="n">post</span><span class="p">(</span><span class="s2">&#34;Client/UpdateUserData&#34;</span><span class="p">,</span> <span class="p">{</span><span class="s2">&#34;Data&#34;</span><span class="p">:</span> <span class="n">payload</span><span class="p">},</span> <span class="n">ticket</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="nb">print</span><span class="p">(</span><span class="s2">&#34;[restore] -&gt;&#34;</span><span class="p">,</span> <span class="n">resp</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s2">&#34;code&#34;</span><span class="p">),</span> <span class="n">resp</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s2">&#34;status&#34;</span><span class="p">),</span> <span class="n">resp</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s2">&#34;errorCode&#34;</span><span class="p">)</span> <span class="ow">or</span> <span class="s2">&#34;&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">          <span class="n">resp</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s2">&#34;errorMessage&#34;</span><span class="p">)</span> <span class="ow">or</span> <span class="s2">&#34;&#34;</span><span class="p">,</span> <span class="n">resp</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s2">&#34;data&#34;</span><span class="p">))</span>
</span></span><span class="line"><span class="cl">    <span class="n">st</span><span class="p">,</span> <span class="n">resp</span> <span class="o">=</span> <span class="n">post</span><span class="p">(</span><span class="s2">&#34;Client/GetUserData&#34;</span><span class="p">,</span> <span class="p">{},</span> <span class="n">ticket</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">data</span> <span class="o">=</span> <span class="n">resp</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s2">&#34;data&#34;</span><span class="p">,</span> <span class="p">{})</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s2">&#34;Data&#34;</span><span class="p">,</span> <span class="p">{})</span> <span class="ow">or</span> <span class="p">{}</span>
</span></span><span class="line"><span class="cl">    <span class="k">for</span> <span class="n">k</span> <span class="ow">in</span> <span class="nb">sorted</span><span class="p">(</span><span class="n">payload</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">        <span class="n">got</span> <span class="o">=</span> <span class="p">(</span><span class="n">data</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="n">k</span><span class="p">)</span> <span class="ow">or</span> <span class="p">{})</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s2">&#34;Value&#34;</span><span class="p">)</span> <span class="ow">or</span> <span class="s2">&#34;&#34;</span>
</span></span><span class="line"><span class="cl">        <span class="nb">print</span><span class="p">(</span><span class="sa">f</span><span class="s2">&#34;    </span><span class="si">{</span><span class="n">k</span><span class="si">}</span><span class="s2">: </span><span class="si">{</span><span class="nb">len</span><span class="p">(</span><span class="n">got</span><span class="p">)</span><span class="si">}</span><span class="s2"> bytes, matches_backup=</span><span class="si">{</span><span class="n">got</span> <span class="o">==</span> <span class="n">payload</span><span class="p">[</span><span class="n">k</span><span class="p">]</span><span class="si">}</span><span class="s2">&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="mi">0</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">if</span> <span class="vm">__name__</span> <span class="o">==</span> <span class="s2">&#34;__main__&#34;</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">    <span class="n">sys</span><span class="o">.</span><span class="n">exit</span><span class="p">(</span><span class="n">main</span><span class="p">())</span></span></span></code></pre></div></div>

  </div>
</details>

</div>

<style>
  #accordion-047b5343a836560c5e3056fa28d7b13e > details + details {
    border-top: 1px solid rgb(var(--color-neutral-200));
  }
  .dark #accordion-047b5343a836560c5e3056fa28d7b13e > details + details {
    border-top-color: rgb(var(--color-neutral-700));
  }
</style>

<style>
  #accordion-047b5343a836560c5e3056fa28d7b13e details[data-accordion-item] > summary .accordion-chevron {
    transform: rotate(-90deg);
    transition: transform 200ms ease-in-out;
  }
  #accordion-047b5343a836560c5e3056fa28d7b13e details[data-accordion-item][open] > summary .accordion-chevron {
    transform: rotate(0deg);
  }
</style>

<script>
  (() => {
    const root = document.getElementById("accordion-047b5343a836560c5e3056fa28d7b13e");
    if (!root) return;
    const items = root.querySelectorAll("details[data-accordion-item]");
    items.forEach((item) => {
      item.addEventListener("toggle", () => {
        if (!item.open) return;
        items.forEach((other) => {
          if (other !== item) other.removeAttribute("open");
        });
      });
    });
  })();
</script>


]]></content:encoded>
    </item>
  </channel>
</rss>
