<!-- translated from docs/troubleshooting.md, sha256 dbe5760f150d -->
# 故障排除

重新进入被锁定的主机、常见问题、报告问题，以及开发者模式。

## 被锁在外面？

PlayGuard 只能看到它所运行的系统的家长控制：**emuMMC 和 sysMMC 各有各的设置**（在一个系统上删除的 PIN，在另一个系统上仍然存在）。需要修复哪个系统，就在哪个系统上运行它。

### 二手主机

你知道 PIN，但前任主人的手机应用仍然关联着（解除关联失败，或者恢复出厂设置时要求输入他们的账号）。

*安全 › 解除手机应用的关联*，然后，如果你完全不想要家长控制，再选 *删除所有家长控制*。两者都可以离线使用，在 emuMMC 和 sysMMC 上都一样。

### 忘记了 PIN

*显示PIN* 本身就要求输入 PIN，所以这里帮不上忙。如果安装了恢复 sysmodule（见下文），放一个 `RESCUE` 文件并重启：恢复画面会设置一个新的 PIN（它从不显示旧的）。如果没有安装，*删除所有家长控制* 可以从头开始（会先保存一份设置备份；备份从不包含 PIN）：如果开启了 *用PIN保护PlayGuard*（默认为 *更改前*），请先把 SD 卡插到电脑上，在 `sd:/config/playguard/config.json` 中把 `"pin_lock"` 设为 `"off"`。

### 游戏时间阻止了一切（限制为 0 分钟），而且忘记了 PIN

这时 PlayGuard 本身无法启动。请**事先**安装可选的恢复 sysmodule（*主机 › 可选模块*，或手动安装 `playguard-rescue.zip`）；被锁在外面时，在 SD 卡上放一个空的 `config/playguard/RESCUE` 文件并启动主机——它会解除主机的锁定，让 PlayGuard 能够打开。见 [`sysmodule/rescue/README.md`](https://github.com/JigSawFr/PlayGuard/blob/main/sysmodule/rescue/README.md)（英文）。

这个模块必须与 PlayGuard 的版本一致：每次更新 PlayGuard 后，重新解压该版本的 `playguard-rescue.zip`（或者在通知铃铛的 *恢复 sysmodule 不是最新版本* 上选择 *更新*），然后重启主机。较旧的模块留下的报告 PlayGuard 无法核验：这时恢复画面不提供任何操作，会说明模块较旧，再次使用 `RESCUE` 也是同样的结果，直到它被更新。

### Homebrew 菜单被锁定

*主机 › 转发器 › 锁定自制软件菜单* 让相册和按住 R 启动的游戏都无法打开 Homebrew 菜单。如果 PlayGuard 的转发器和受保护的菜单都不在了（在主机的“设置”中被删除，或者在另一个系统上：锁定位于 SD 卡上，sysMMC 和 emuMMC 都会读取它）：

- **从电脑上：** 删除 SD 卡上的 `atmosphere/config/override_config.ini`（或者放回 `config/playguard/override_config.ini.backup`，即原来的文件），然后重启主机。
- **安装了恢复 sysmodule 时**（可选）：在 SD 卡上放一个空的 `config/playguard/RESCUE` 文件并重启；该 sysmodule 会在启动时解除锁定，PlayGuard 下次启动时会说明这一点。锁定之后更新了 Atmosphère，也会以同样的方式解除锁定。

### 主机未破解

PlayGuard 帮不上忙：它需要 Atmosphère。任天堂客服的主密钥（master key）流程是官方的解决办法。

## 常见问题

### 剩余时间没有减少

剩余时间约 90 秒没有变化时，*今天* 会显示 *主机目前没有计算游戏时间*。可能的原因：

- **网络时钟从未设置。** *主机 › 网络时钟 › 测量时间差*，然后设置它（[网络时钟](https://guard.boreale.dev/zh-hans/docs/features.md#网络时钟)）。
- **时钟被调回。** 时钟往回调后，计时会停止，直到时钟走过原来的时间；无需任何操作。
- **刚刚写入了限制。** 启动一个游戏玩一分钟，然后再查看。
- **家长控制已暂时解除。** 这时游戏时间可能不会计入：*今天 › 重新启用*（或点击标题栏的胶囊标签）。

### 我的限制自己变了

- **手机应用仍然关联着。** 它下次同步会覆盖 PlayGuard 设置的内容：*安全 › 解除手机应用的关联*。
- **其他工具，或者配置。** 限制与 PlayGuard 上次看到的不同时，*今天* 会显示一张琥珀色卡片，*历史记录 › 通过PlayGuard进行的更改* 会列出在 PlayGuard 之外发现的每项更改。在其中一项上按 Ⓐ 会恢复之前的值。
- **计划的配置**已被应用，或者在它的时间段结束后被换回（*游戏时间 › 配置 › 计划的配置*）。

### 孩子玩超过了限制

- **主机只作提醒。** *游戏时间 › 时间到时*：选择 *中断软件*。
- **家长控制一直处于解除状态。** 解除期间，今天的限制处于暂停状态：标题栏的胶囊标签会在每个标签页上说明这一点，点击它会前往 *今天 › 重新启用*。重启主机也会重新锁定。
- **时钟被调回。** 计时会停止，直到时钟走过原来的时间（[什么算作游戏时间](https://guard.boreale.dev/zh-hans/docs/features.md#什么算作游戏时间)）。
- **另一个系统。** emuMMC 和 sysMMC 各有各的家长控制：请在两边都设置，或者隐藏 hekate 的启动菜单（[兼容性](https://guard.boreale.dev/zh-hans/docs/compatibility.md#emummc-和-sysmmc)）。

### 今天又可以使用全部限制时间了

更改时钟（网络时钟或主机时钟）会让今天的游戏时间重新计算。请在孩子玩之前设置时钟，而不是之后。

### PlayGuard 以只读方式打开

主机运行的系统版本比测试过的最新版本还新：PlayGuard 会检查是否有更新的发布版本支持它，并提示更新（[更新的系统版本](https://guard.boreale.dev/zh-hans/docs/compatibility.md#更新的系统版本)）。标题右侧划掉的铅笔表示应用处于只读模式；点击它会打开系统版本画面。

### PlayGuard 总是要求输入 PIN，或者从不要求

*安全 › 用PIN保护PlayGuard*：*更改前*（默认）只在第一次更改之前要求一次，PIN 在 PlayGuard 关闭之前一直有效。*5分钟后再次要求输入PIN* 会在 5 分钟后再次要求。*显示PIN* 总是要求输入。

### 游戏无法启动，或者在 HOME 菜单中显示“!”

*主机 › 已安装的游戏* 会说明每个游戏的问题以及该怎么做；存档数据始终保留（[已安装的游戏](https://guard.boreale.dev/zh-hans/docs/features.md#已安装的游戏)）。

### 受保护的转发器显示错误 2347-0100

它的门禁（`config/playguard/gate-….nro`）已不再是制作转发器时的那个文件：它在 SD 卡上被更改或替换了，所以转发器不会启动它。从 PlayGuard 自己的转发器打开 PlayGuard：它在启动时会重新写入门禁；*主机 › 转发器 › 全部重新安装* 会用它重新制作所有转发器。

### 受保护的转发器一打开就关闭（“发生错误”）

在出现任何 PlayGuard 画面之前，没有错误代码（Atmosphère 在 `atmosphere/crash_reports/` 中的报告指出 `forwarder` 模块，结果为 `2000-0000`）：它是由某个 PlayGuard 制作的，而那个 PlayGuard 的门禁在构建时缺少资源，被转发器的加载程序拒绝了。更新 PlayGuard，然后重新制作它：铃铛和 *主机 › 转发器* 会为每个用较旧加载程序制作的转发器提供这一操作。

### 受保护的转发器输入 PIN 后停在门禁画面上

PIN 已被接受，带有 Homebrew 图标的画面却一直停着，之后什么也没有发生：它的门禁用单个缓冲区绘制了那个画面，然后一直等待绘制下一个画面。更新 PlayGuard，然后重新制作它：铃铛和 *主机 › 转发器* 会为每个用较旧门禁制作的转发器提供这一操作。

### 转发器显示“无法启动软件”

主机拒绝启动它：仍有一项签名检查在起作用。按以下顺序排查：

- **sys-patch 旁边还有 sigpatch 文件。** sys-patch 运行时，`atmosphere/kip_patches/fs_patches`、`loader_patches` 或 `atmosphere/exefs_patches/es_patches` 中还有 `.ips` 文件：两个工具在修补同样的检查，这是 GBAtemp 上 sys-patch 和 sigpatch 讨论帖中最常提到的原因。*主机 › 游戏补丁* 会对此发出提示，在那里按 Ⓐ 可以把它们移到一旁（移到 `config/playguard/sigpatches-aside/`，从不删除；hekate 的 `bootloader/patches.ini` 保持不动）。然后重启主机。
- **sys-patch 没有应用某个特征码。** 转发器需要它的 fs 和 loader 补丁，也就是它针对这个系统版本的所有特征码（`noncasigchk_17.0.0+`、`nocntchk_19.0.0+`、`noacidsigchk_10.0.0+`），而不只是每个模块一个。*关于 › 帮助与诊断 › 导出诊断报告* 会在它的 `game patches` 一行列出 sys-patch 的版本和每个没有应用的特征码；`sd:/config/sys-patch/log.ini` 中也有同样的内容。检查 `config/sys-patch/config.ini` 是否修补这个系统（emuMMC 上为 `patch_emummc=1`），并且没有关闭其中任何一项；sys-patch 的维护者本人表示，23.0.x 不需要比 1.6.2.3 更新的 sys-patch。
- **补丁更改后没有重启。** sys-patch 在开机时打补丁。
- **由较早版本的 PlayGuard 制作的转发器。** 铃铛和 *主机 › 转发器* 会提议重新制作那些用较旧加载程序构建的转发器。

### 铃铛或某个标签页显示琥珀色标记

有需要你处理的事情：Ⓨ 打开列表，在条目上按 Ⓐ 会直接解决，或打开能解决它的地方（[通知](https://guard.boreale.dev/zh-hans/docs/features.md#通知在任何标签页都能看到需要你处理的事情)）。

## 报告问题

出错时会显示系统的代码（例如 `0x0001088E`）以及关于它的已知信息：对于含义已有记录的代码，会说明该怎么做；否则，一份报告会有帮助。

1. *关于 › 帮助与诊断 › 导出诊断报告* 会在 `sd:/config/playguard/logs/reports/` 中保存一个文本文件：系统版本、Atmosphère 版本、启动用了多长时间以及较慢的画面用了多长时间（更改历史、*所有游戏*、已安装游戏的检查、会自动刷新的标签页）、时钟、存储、序列号隐藏和游戏补丁的状态、Atmosphère 的 DNS 重定向如何处理任天堂的家长控制和时间服务器（从 SD 卡上它的 hosts 文件读取，而不是网络测试，并附上 Atmosphère 自己的 `atmosphere/logs/dns_mitm_startup.log` 的末尾）、每次家长控制查询的原始结果，以及日志：上次运行是如何结束的、剩余的内存和 SD 卡空间、最新的崩溃文件和最后记录的 80 行。**它从不包含 PIN 或序列号**。
2. [提交一个 issue](https://github.com/JigSawFr/PlayGuard/issues/new) 并附上它。

### 在线发送报告

在主机联网时，*关于 › 帮助与诊断 › 发送报告…*（开发者模式下在诊断报告上按 Ⓧ）会把它发送到你 GitHub 账户中的一个秘密 gist，发送前会准确说明要发送哪些内容。之前保存在 `logs/reports/` 中的报告也可以用同样的方式发送。

- **发送的内容：** 报告和每个文件各作为一个单独的文件：
  - 最新的崩溃报告：`logs/crashes/` 中 PlayGuard 的最后 3 份，以及 `sd:/atmosphere/crash_reports/` 中 Atmosphère 在过去一周内记录的自制软件崩溃的最后 2 份（其中可能有其他自制软件的崩溃：发送前显示的列表会说明是哪个）；
  - 本次和上一次运行的日志（`logs/sessions/`；较长的日志：开头部分和最后 64 KiB）；
  - 调试文件：游戏时间数据块的参照、开启 *记录游戏时间* 时游戏时间记录的结尾部分、更改历史中最近 20 项更改（不含游戏、用户或配置的名称，只保留日期），以及 PlayGuard 的设置。
- **登录：** 未登录时，PlayGuard 会先提示登录；登录后，紧接在下方的 *GitHub 账户* 可以退出登录；GitHub 拒绝 gist 时，PlayGuard 会提示重新登录。
- **之后：** 它会显示两个二维码，链接显示在第一个下方，它的 ID 每四个字符一组，便于念出或输入：报告，供已经在帮助你的人使用；以及已预先填好链接、你的版本、PlayGuard 的启动方式（在游戏之上、从它在 HOME 菜单上的图标，还是从相册；请补充是哪个菜单）和系统（sysMMC 或 emuMMC）的问题报告表单，扫描即可报告问题。链接也会保留在通知中，每个链接都以网络快捷方式（`.url`）保存在 `logs/uploads/` 中，电脑可以用浏览器打开。

> [!WARNING]
> 在你于 GitHub 上删除 gist 之前，任何拿到链接的人都能阅读报告，用它提交的问题报告也是公开的：确认时会说明这一点。

### PlayGuard 在 SD 卡上保存的内容

全部位于 `sd:/config/playguard/logs/`，都是电脑可以打开的纯文本：

- `sessions/session_<date>.txt`——**每次运行一个文件**：PlayGuard 记录的所有内容（以前只能通过 nxlink 传到电脑上），每一行带有时间和级别（`E` 错误、`W` 警告、`I` 信息），最后一行说明这次运行是如何结束的：`=== end: clean exit ===`，或 `=== end: crash, see logs/crashes/… ===`。文件达到 1 MiB 后不再增长。
- `crashes/crash_<date>.txt`——**每次崩溃一个文件**，第一行说明发生了什么：
  - **CPU 异常**（错误的指针、未定义的指令、栈溢出）：故障类型和它访问的地址、调用栈（backtrace）和寄存器。地址是 `playguard.nro` 内的偏移（`playguard+0x1a2b3c`）：该构建的链接映射（它的 CI 运行中的 `-linker-map` 产物）可以查出对应的函数。之后 PlayGuard 像以前一样结束：主机显示错误画面，Atmosphère 自己的报告保存在 `sd:/atmosphere/crash_reports/` 中；
  - **未捕获的异常**：它的类型和消息，以及调用栈；
  - **没有结束的运行**：在下次启动时发现，即上次运行的会话文件没有结束行（PlayGuard 无法捕获的崩溃、卡住时从 HOME 菜单关闭，或者主机被关机），附上那次运行的最后 60 行。

  PlayGuard 运行期间捕获的崩溃文件，末尾附有崩溃前最后记录的 80 行。
- `reports/`、`uploads/`、`play_timer/`——已保存的报告、在线发送的报告的链接、游戏时间记录。

*关于 › 帮助与诊断* 可以设置保留多少份报告、崩溃文件、会话日志和链接（每个文件夹分别设置；最新的一份始终保留）。

## 开发者模式（研究新的系统版本）

在 *关于 › 版本* 上按 Ⓨ 七次；开发者工具会出现在 *关于 › 帮助与诊断* 的末尾。它增加了：

- 一个**只读**开关——开启后，应用无法更改任何东西：这是研究新系统版本的安全方式（系统版本画面会直接提供这个选项）；
- 在画面上查看诊断报告（Ⓨ 保存，Ⓧ 在线发送），以及在游戏时间标签页中导出它的快捷方式；
- **主机自检**：在手动逐项完成[主机检查清单](https://github.com/JigSawFr/PlayGuard/blob/main/docs/hardware-test.md)（英文）之前，PlayGuard 自己在这台主机上检查的内容——PIN、签名补丁、数据文件夹、PlayGuard 创建的每个转发器（是否在 HOME 菜单上、是否受保护、是否已确认能通过门禁正常工作）、SD 卡上的门禁、Atmosphère 读到的 Homebrew 菜单锁定、回到 PlayGuard 的途径、恢复 sysmodule。它不会更改任何东西，并把结果以 Markdown 写入 `config/playguard/self-test.md`，可以粘贴到 pull request 中；
- 原地**安装其他构建**，在修复发布之前先测试（[见下文](#安装其他构建)）；
- **比较游戏时间数据块**（仅在手机应用已关联并且能够同步时——用 hosts 文件拦截任天堂时不行；*安全* 中也有），用于解析 PlayGuard 尚未显示的设置：把原始数据块保存为参照，在手机应用中更改一项设置，然后回来——PlayGuard 会列出发生变化的值（按需保存在 `logs/reports/` 中）。在手机应用中设置的每天不同的就寝时间，可以用这种方法确认。把这个文件附到 issue 中，或者和报告一起发送（*发送报告…*）。
- **记录游戏时间**：PlayGuard 打开期间每 30 秒在 `logs/play_timer/log.csv` 中记录一行游戏时间计时器报告的内容（剩余时间、已用时间、原始设置数据块…），这是可以直接用电子表格打开的文件；PlayGuard 每次更改之后也会立即记录一行，说明是哪项更改（它的最后一列 `event`），时钟变化超过实际经过的时间时还会加上一条备注。开着它跨过午夜，或者直到时间用完，它能显示一份报告无法显示的东西：已用时间何时重置，主机在快结束时说了什么。这个开关会被记住；它只在开发者模式下记录。目前已知的情况见 [parental-controls.md](https://guard.boreale.dev/zh-hans/docs/parental-controls.md)（英文）。
- **清除缓存**和**删除日志文件**，各自在确认后执行，确认时会说明有多少个文件、占用多少空间：缓存（`cache/` 和每个系统的 `nand/<system>/cache/`：游戏名称和图标、游玩记录的最新数据、构建列表）需要时会从主机重新读取；日志文件包括崩溃文件、会话日志（本次运行正在写入的除外）、已保存的报告和游戏时间记录，而 `uploads/`（在线发送的报告的删除链接）和数据块参照会保留。设置、历史记录、备份、配置和导出文件从不受影响。
- **重新运行迁移**：从以前布局的迁移（`.nro` 旁边的数据文件夹、日志分出子文件夹之前的位置、所有系统共用的内容）各占一行，可以勾选或取消勾选（起初全部勾选；A 切换一项），然后是*运行所选的 N 项*，以及一个列出各项及其移动内容的确认。无论 `migrations.json` 记录了什么，它们都会立即运行（否则每项只在需要它的构建首次启动时运行一次；记录的级别从不降低）；已在原位的文件从不被替换。一个对话框会说明移动了多少项；每一步都记录在会话日志中。
- **帧统计**：每秒在 `logs/frame_stats.txt` 中记录一行，包括绘制画面所用的时间和迟到的帧，用于在主机上比较不同画面的表现。**背景光晕**可以关闭画面后方的柔和光效（这时这一行的模式会以 `-noglow` 结尾），以衡量它的开销。

### 安装其他构建

*安装其他构建* 用一个构建替换 PlayGuard 自己的 `.nro`，在修复发布之前先测试。

**列出的内容**，每一类各在一个标签页中（Ⓛ / Ⓡ 显示在标签页两侧，用于切换；画面打开时显示已安装构建所在的标签页，每个标签页的名称后面都有一个闪光标记和比已安装构建新的构建数量）：

- 最近 20 个发布版本之一（包括预发布，带有标签）；
- `main` 最近 20 个提交之一；
- 本仓库某个分支上打开的拉取请求的最新构建（从不来自 fork：它的代码还没有经过审查）。在拉取请求标签页上（显示时），A 会反过来排序（最新的构建在前，或按编号）：A 的操作提示会说明是哪一种。

**每个构建**在标签上方显示它的标题（提交或拉取请求的主题；提交的完整消息是它的描述）：更改的类型（*新功能*、*修复*、*文档*…）和范围、*不兼容*、提交来自的拉取请求、检查的结果（*CI 通过*、*CI 失败*、*CI 进行中*）、预发布、已安装的构建带勾号、较新的构建带闪光标记；右侧显示它有多旧（以分钟、小时，然后以天计）以及它的提交。构建会先打开它的页面：标签（类型、检查、勾号或闪光标记）、一张信息卡片（构建时间、大小、提交、校验和、GitHub 何时删除它）、**安装**、**在 GitHub 上查看**（它的页面以二维码显示，供手机扫描），检查仍在进行时显示一条警告，然后是发布版本的说明或拉取请求的描述，按 GitHub 的 Markdown 绘制（标题、列表、任务框、代码、引用和提示、表格）。

**可以放心阅读：** 来自 GitHub 的一切都以纯文本显示：不打开任何链接，不运行任何 HTML 或脚本，并会去除可能伪装标题的不可见字符（从右到左覆盖字符、零宽字符、控制字符、无效的 UTF-8）。

**登录。** 发布版本不需要任何条件；其他的是构建工作流的产物，GitHub 只提供给已登录的用户。**GitHub 账户** 通过一个代码和一个供手机扫描的二维码登录。唯一请求的权限是读取构建工作流的文件（Actions）和创建 gist，用于 *发送报告…*；除此之外，令牌只能读取公开内容。它保存在 `github_token` 中，从不随报告发送，*GitHub 账户* 可以退出登录。登录后，Ⓨ（*搜索*）可以输入拉取请求的编号（打开或已关闭的）或提交（7 到 40 位十六进制数字）来查找它的构建，只限本仓库的分支。

**安装：** PlayGuard 下载构建，进行检查（大小、GitHub 记录的 SHA-256、NRO 文件头），用它替换自己的 `.nro`，然后以它重新启动（只要设置了 PIN，就会先要求输入）。它的窗口会说明正在进行哪一步、下载的大小和速度，以及它替换的是哪个构建；开始放置构建后，“取消”就会消失。

- 列表保留 10 分钟，打开时立即显示，并标明它有多旧（Ⓧ 重新获取，构建的检查仍在进行时，画面每 3 分钟也会重新获取）；获取期间，标题栏右侧的进度条显示进度。安装会做什么，在画面底部说明。
- 中途断开的下载（网络中断），下次安装同一个构建时会从中断处继续。
- 产物在 90 天后过期。

**回退。** 被替换的构建会保留（`playguard.nro.previous`）：同一列表中的**回到上一个构建**会在输入 PIN 后把它们再换回来；它以版本、拉取请求和提交来标明那个构建。如果从这里（或通过 *更新方式* 中的“在 PlayGuard 内”）安装的构建在显示第一个画面之前崩溃，PlayGuard 下次启动时会在一个纯文本画面上提示回到上一个构建，在加载任何其他内容之前（A 回到上一个，B 仍然启动它）。设置了 PIN 时，回退会在主机自己的 PIN 画面上要求输入 PIN（而不是 PlayGuard 的锁定画面，崩溃的可能正是它）；不输入则启动已安装的构建。同一个列表随时可以回到任何一个发布版本。

**正在运行的是哪个构建：** 开发者模式下，*关于 › 版本* 会显示提交。每个画面的标题栏在标题旁显示正在运行的版本：发布版本显示在灰色标签中（`1.2.0`），其他构建显示在琥珀色标签中并附上提交，这样测试构建永远不会被误认为发布版本。
