跳转至

金手指

Atmosphère 支持 Action-Replay 风格的金手指代码,金手指从 SD 卡加载。

金手指加载流程

默认情况下,Atmosphère 在决定是否附加到新应用程序进程时会执行以下操作:

  • pmloader 检索有关新应用程序进程的信息。
  • 检查用户定义的按键组合是否被按住,如果没有则停止。
  • 默认为"未按住 L 键",但可以通过覆盖键配置。
  • 用于配置的 ini 键是 cheat_enable_key
  • 检查进程是否为真实应用程序,如果不是则停止。
  • 这可以防止将金手指代码应用于自制程序加载器。
  • 尝试从 /atmosphere/contents/<program_id>/cheats/<build_id>.txt 加载金手指,其中 build_id 是应用程序主可执行文件构建 ID 前 8 字节的十六进制表示。
  • 如果未找到金手指,则金手指管理器将停止。
  • 为新应用程序进程打开内核调试会话。
  • 向系统事件发出信号,表示已附加到新的金手指进程。

此行为确保金手指代码仅在用户需要时加载。

dmnt 未激活金手指管理器但用户希望强制激活的情况下,金手指管理器的服务 API 提供了 ForceOpenCheatProcess 命令供自制程序使用。此命令将使金手指管理器尝试强制自身附加到进程。

dmnt 已激活金手指管理器但用户希望使用替代调试器的情况下,金手指管理器的服务 API 提供了 ForceCloseCheatProcess 命令供自制程序使用。此命令将使金手指管理器从进程中分离。

默认情况下,加载的 .txt 文件中列出的所有金手指代码都将启用。用户可以通过编辑 atmosphere!dmnt_cheats_enabled_by_default 系统设置 来配置此行为。

用户可以使用自制程序通过金手指管理器的服务 API 在运行时切换金手指的启用和禁用状态。

金手指代码兼容性

Atmosphère 通过执行小型自定义虚拟机来管理金手指代码。已确保 Atmosphère 的金手指代码格式与现有金手指代码格式完全向后兼容,同时添加了新功能并修复了现有金手指代码应用器中的错误。以下是与现有格式相比的主要变更摘要:

  • 修复了条件指令处理中的若干错误。
  • 现有实现在检测条件块结束时存在根本性缺陷,检查了错误的值。
  • 现有实现也未正确解码指令,而是线性扫描终止符值。如果指令的立即数中恰好编码了终止符,会导致问题。
  • 现有实现未进行边界检查,因此某些条件金手指代码可能导致其读取越界内存,并可能因数据中止而崩溃。
  • 添加了对嵌套条件块的支持。
  • 添加了一条指令,用于在两个寄存器上执行更复杂的任意算术运算。
  • 添加了一条指令,允许将寄存器的内容写入由另一个寄存器指定的内存地址。
  • 现有实现未正确与应用程序进程同步,因此在某些情况下(尤其是在加载屏幕期间)会导致严重延迟。Atmosphère 的实现已修复此问题。

金手指代码格式

以下提供了用于管理金手指代码的虚拟机指令格式文档。

通常,指令类型编码在第一个指令 u32 的高半字节中。

代码类型 0x0:将静态值存储到内存

代码类型 0x0 允许将静态值写入内存地址。

编码

0TMR00AA AAAAAAAA VVVVVVVV (VVVVVVVV)

  • T: 内存写入宽度(1、2、4 或 8 字节)。
  • M: 要写入的内存区域(0 = 主 NSO,1 = 堆,2 = 别名,3 = Aslr,4 = 非相对)。
  • R: 用作内存区域基址偏移的寄存器。
  • A: 从内存区域基址使用的立即偏移量。
  • V: 要写入的值。

代码类型 0x1:开始条件块

代码类型 0x1 将内存内容与静态值进行比较。

如果条件不满足,则跳过所有指令,直到遇到适当的中止或否则条件块终止符。

编码

1TMCXrAA AAAAAAAA VVVVVVVV (VVVVVVVV)

  • T: 内存读取宽度(1、2、4 或 8 字节)。
  • M: 要读取的内存区域(0 = 主 NSO,1 = 堆,2 = 别名,3 = Aslr,4 = 非相对)。
  • C: 要使用的条件,见下文。
  • X: 操作数类型,见下文。
  • r: 偏移寄存器(操作数类型 1)。
  • A: 从内存区域基址使用的立即偏移量。
  • V: 要比较的值。

条件

  • 1: >
  • 2: >=
  • 3: <
  • 4: <=
  • 5: ==
  • 6: !=

操作数类型

  • 0: 内存基址 + 相对偏移
  • 1: 内存基址 + 偏移寄存器 + 相对偏移

代码类型 0x2:结束条件块

代码类型 0x2 标记条件块的结束(由代码类型 0x1 或代码类型 0x8 启动)。

当执行否则时,将跳过所有指令,直到遇到适当的中止条件块终止符。

编码

2X000000

  • X: 结束类型(0 = 结束,1 = 否则)。

代码类型 0x3:开始/结束循环

代码类型 0x3 允许在固定次数的循环中迭代。

开始循环编码

300R0000 VVVVVVVV

  • R: 用作循环计数器的寄存器。
  • V: 要循环的迭代次数。

结束循环编码

310R0000

  • R: 用作循环计数器的寄存器。

代码类型 0x4:使用静态值加载寄存器

代码类型 0x4 允许将寄存器设置为常量值。

编码

400R0000 VVVVVVVV VVVVVVVV

  • R: 要使用的寄存器。
  • V: 要加载的值。

代码类型 0x5:使用内存值加载寄存器

代码类型 0x5 允许将值从内存加载到寄存器中,可以使用固定地址,也可以通过解引用目标寄存器实现。

从固定地址加载编码

5TMR00AA AAAAAAAA

  • T: 内存读取宽度(1、2、4 或 8 字节)。
  • M: 要写入的内存区域(0 = 主 NSO,1 = 堆,2 = 别名,3 = Aslr,4 = 非相对)。
  • R: 要加载值的寄存器。
  • A: 从内存区域基址使用的立即偏移量。

从寄存器地址加载编码

5T0R10AA AAAAAAAA

  • T: 内存读取宽度(1、2、4 或 8 字节)。
  • R: 要加载值的寄存器。(此寄存器也用作基础内存地址)。
  • A: 从寄存器 R 使用的立即偏移量。

从寄存器地址加载编码

5T0R2SAA AAAAAAAA

  • T: 内存读取宽度(1、2、4 或 8 字节)。
  • R: 要加载值的寄存器。
  • S: 用作基础内存地址的寄存器。
  • A: 从寄存器 R 使用的立即偏移量。

带偏移寄存器的固定地址加载编码

5TMR3SAA AAAAAAAA

  • T: 内存读取宽度(1、2、4 或 8 字节)。
  • M: 要写入的内存区域(0 = 主 NSO,1 = 堆,2 = 别名,3 = Aslr,4 = 非相对)。
  • R: 要加载值的寄存器。
  • S: 用作偏移寄存器的寄存器。
  • A: 从内存区域基址使用的立即偏移量。

代码类型 0x6:将静态值存储到寄存器内存地址

代码类型 0x6 允许将固定值写入由寄存器指定的内存地址。

编码

6T0RIor0 VVVVVVVV VVVVVVVV

  • T: 内存写入宽度(1、2、4 或 8 字节)。
  • R: 用作基础内存地址的寄存器。
  • I: 递增寄存器标志(0 = 不递增 R,1 = 将 R 递增 T)。
  • o: 偏移寄存器启用标志(0 = 不将 r 添加到地址,1 = 将 r 添加到地址)。
  • r: 当 o 为 1 时用作偏移的寄存器。
  • V: 要写入内存的值。

代码类型 0x7:传统算术

代码类型 0x7 允许对寄存器执行算术运算。

然而,它已被代码类型 0x9 弃用,仅保留用于向后兼容。

编码

7T0RC000 VVVVVVVV

  • T: 算术运算宽度(1、2、4 或 8 字节)。
  • R: 要应用算术运算的寄存器。
  • C: 要应用的算术运算,见下文。
  • V: 用于算术运算的值。

算术类型

  • 0: 加法
  • 1: 减法
  • 2: 乘法
  • 3: 左移
  • 4: 右移

代码类型 0x8:开始按键条件块

代码类型 0x8 根据是否按下按键组合进入或跳过条件块。

编码

8kkkkkkk

  • k: 要检查的键位掩码,见下文。

注意:对于多个按钮组合,位掩码应进行 OR 运算。

键位值

注意:这是 hidKeysDown() 的直接输出。

  • 0000001: A
  • 0000002: B
  • 0000004: X
  • 0000008: Y
  • 0000010: 左摇杆按下
  • 0000020: 右摇杆按下
  • 0000040: L
  • 0000080: R
  • 0000100: ZL
  • 0000200: ZR
  • 0000400: 加号
  • 0000800: 减号
  • 0001000: 左
  • 0002000: 上
  • 0004000: 右
  • 0008000: 下
  • 0010000: 左摇杆左
  • 0020000: 左摇杆上
  • 0040000: 左摇杆右
  • 0080000: 左摇杆下
  • 0100000: 右摇杆左
  • 0200000: 右摇杆上
  • 0400000: 右摇杆右
  • 0800000: 右摇杆下
  • 1000000: SL
  • 2000000: SR

代码类型 0x9:执行算术运算

代码类型 0x9 允许对寄存器执行算术运算。

寄存器算术编码

9TCRS0s0

  • T: 算术运算宽度(1、2、4 或 8 字节)。
  • C: 要应用的算术运算,见下文。
  • R: 存储结果的寄存器。
  • S: 用作左操作数的寄存器。
  • s: 用作右操作数的寄存器。

立即值算术编码

9TCRS100 VVVVVVVV (VVVVVVVV)

  • T: 算术运算宽度(1、2、4 或 8 字节)。
  • C: 要应用的算术运算,见下文。
  • R: 存储结果的寄存器。
  • S: 用作左操作数的寄存器。
  • V: 用作右操作数的值。

算术类型

  • 0: 加法
  • 1: 减法
  • 2: 乘法
  • 3: 左移
  • 4: 右移
  • 5: 逻辑与
  • 6: 逻辑或
  • 7: 逻辑非(忽略右操作数)
  • 8: 逻辑异或
  • 9: 无/移动(忽略右操作数)
  • 10: 浮点加法,T==4 单精度 T==8 双精度
  • 11: 浮点减法,T==4 单精度 T==8 双精度
  • 12: 浮点乘法,T==4 单精度 T==8 双精度
  • 13: 浮点除法,T==4 单精度 T==8 双精度

代码类型 0xA:将寄存器存储到内存地址

代码类型 0xA 允许将寄存器写入内存。

编码

ATSRIOxa (aaaaaaaa)

  • T: 内存写入宽度(1、2、4 或 8 字节)。
  • S: 要写入内存的寄存器。
  • R: 用作基地址的寄存器。
  • I: 递增寄存器标志(0 = 不递增 R,1 = 将 R 递增 T)。
  • O: 偏移类型,见下文。
  • x: 当 O 为 1 时用作偏移的寄存器,当 O 为 3、4 或 5 时为内存类型。
  • a: 当 O 为 2、4 或 5 时用作偏移的值。

偏移类型

  • 0: 无偏移
  • 1: 使用偏移寄存器
  • 2: 使用固定偏移
  • 3: 内存区域 + 基址寄存器
  • 4: 内存区域 + 相对地址(忽略地址寄存器)
  • 5: 内存区域 + 相对地址 + 偏移寄存器

代码类型 0xB:保留

代码类型 0xB 目前保留供将来使用。


代码类型 0xC-0xF:扩展宽度指令

代码类型 0xC-0xF 指示虚拟机将第一个双字的高两个半字节视为指令类型,而不仅仅是高半字节。

这为将来使用保留了额外的 64 个操作码。


代码类型 0xC0:开始寄存器条件块

代码类型 0xC0 将寄存器的内容与另一个值进行比较。此代码支持多种操作数类型,见下文。

如果条件不满足,则跳过所有指令,直到遇到适当的条件块终止符。

编码

C0TcSX##
C0TcS0Ma aaaaaaaa
C0TcS1Mr
C0TcS2Ra aaaaaaaa
C0TcS3Rr
C0TcS400 VVVVVVVV (VVVVVVVV)
C0TcS5X0
  • T: 内存写入宽度(1、2、4 或 8 字节)。
  • c: 要使用的条件,见下文。
  • S: 源寄存器。
  • X: 操作数类型,见下文。
  • M: 内存类型(操作数类型 0 和 1)。
  • R: 地址寄存器(操作数类型 2 和 3)。
  • a: 相对地址(操作数类型 0 和 2)。
  • r: 偏移寄存器(操作数类型 1 和 3)。
  • X: 其他寄存器(操作数类型 5)。
  • V: 要比较的值(操作数类型 4)。

操作数类型

  • 0: 内存基址 + 相对偏移
  • 1: 内存基址 + 偏移寄存器
  • 2: 寄存器 + 相对偏移
  • 3: 寄存器 + 偏移寄存器
  • 4: 静态值
  • 5: 其他寄存器

条件

  • 1: >
  • 2: >=
  • 3: <
  • 4: <=
  • 5: ==
  • 6: !=

代码类型 0xC1:保存或恢复寄存器

代码类型 0xC1 执行寄存器的保存或恢复。

编码

C10D0Sx0

  • D: 目标索引。
  • S: 源索引。
  • x: 操作数类型,见下文。

操作数类型

  • 0: 恢复寄存器
  • 1: 保存寄存器
  • 2: 清除保存的值
  • 3: 清除寄存器

代码类型 0xC2:使用掩码保存或恢复寄存器

代码类型 0xC2 使用位掩码保存或恢复多个寄存器。

编码

C2x0XXXX

  • x: 操作数类型,见下文。
  • X: 16 位位掩码,位 i == 保存或恢复寄存器 i。

操作数类型

  • 0: 恢复寄存器
  • 1: 保存寄存器
  • 2: 清除保存的值
  • 3: 清除寄存器

代码类型 0xC3:读取或写入静态寄存器

代码类型 0xC3 使用给定寄存器读取或写入静态寄存器。

编码

C3000XXx

  • XX: 静态寄存器索引,0x00 到 0x7F 用于读取,0x80 到 0xFF 用于写入。
  • x: 寄存器索引。

代码类型 0xC4:开始扩展按键条件块

代码类型 0xC4 根据是否按下按键组合进入或跳过条件块。

编码

C4r00000 kkkkkkkk kkkkkkkk

  • r: 自动重复,见下文。
  • kkkkkkkkkk: 要检查的键位掩码,对应 hidKeysDown() 的输出。

注意:对于多个按钮组合,位掩码应进行 OR 运算。

自动重复

  • 0: 条件块仅在键位掩码匹配时执行一次。掩码必须停止匹配才能重置以进行下一次触发。
  • 1: 只要键位掩码匹配,条件块就会执行。

键位值

注意:这是 hidKeysDown() 的直接输出。

  • 00000000 00000001: A
  • 00000000 00000002: B
  • 00000000 00000004: X
  • 00000000 00000008: Y
  • 00000000 00000010: 左摇杆按下
  • 00000000 00000020: 右摇杆按下
  • 00000000 00000040: L
  • 00000000 00000080: R
  • 00000000 00000100: ZL
  • 00000000 00000200: ZR
  • 00000000 00000400: 加号
  • 00000000 00000800: 减号
  • 00000000 00001000: 左
  • 00000000 00002000: 上
  • 00000000 00004000: 右
  • 00000000 00008000: 下
  • 00000000 00010000: 左摇杆左
  • 00000000 00020000: 左摇杆上
  • 00000000 00040000: 左摇杆右
  • 00000000 00080000: 左摇杆下
  • 00000000 00100000: 右摇杆左
  • 00000000 00200000: 右摇杆上
  • 00000000 00400000: 右摇杆右
  • 00000000 00800000: 右摇杆下
  • 00000000 01000000: 左 Joy-Con 的 SL
  • 00000000 02000000: 左 Joy-Con 的 SR
  • 00000000 04000000: 右 Joy-Con 的 SL
  • 00000000 08000000: 右 Joy-Con 的 SR
  • 00000000 10000000: Poké Ball Plus (Palma) 控制器上的顶部按钮
  • 00000000 20000000: 验证
  • 00000000 40000000: 手持模式下左侧 NES/HVC 控制器上的 B 按钮
  • 00000000 80000000: N64 控制器上的左 C 按钮
  • 00000001 00000000: N64 控制器上的上 C 按钮
  • 00000002 00000000: N64 控制器上的右 C 按钮
  • 00000004 00000000: N64 控制器上的下 C 按钮

代码类型 0xF0:双倍扩展宽度指令

代码类型 0xF0 指示虚拟机将第一个双字的高三个半字节视为指令类型,而不仅仅是高半字节。

这为将来使用保留了额外的 16 个操作码。


代码类型 0xFF0:暂停进程

代码类型 0xFF0 暂停当前进程。

编码

FF0?????


代码类型 0xFF1:恢复进程

代码类型 0xFF1 恢复当前进程。

编码

FF1?????


代码类型 0xFFF:调试日志

代码类型 0xFFF 将调试日志写入 SD 卡上的 /atmosphere/cheat_vm_logs/ 文件夹。

编码

FFFTIX##
FFFTI0Ma aaaaaaaa
FFFTI1Mr
FFFTI2Ra aaaaaaaa
FFFTI3Rr
FFFTI4X0
  • T: 内存写入宽度(1、2、4 或 8 字节)。
  • I: 日志 ID。
  • X: 操作数类型,见下文。
  • M: 内存类型(操作数类型 0 和 1)。
  • R: 地址寄存器(操作数类型 2 和 3)。
  • a: 相对地址(操作数类型 0 和 2)。
  • r: 偏移寄存器(操作数类型 1 和 3)。
  • X: 值寄存器(操作数类型 4)。

操作数类型

  • 0: 内存基址 + 相对偏移
  • 1: 内存基址 + 偏移寄存器
  • 2: 寄存器 + 相对偏移
  • 3: 寄存器 + 偏移寄存器
  • 4: 寄存器值