塞尔达传说梦见岛——我不是小偷

三级用户 Asahui 7小时前 738

在游戏商店里是可以偷东西的,一旦成功偷到东西出商店,一些NPC会叫你小偷。网络上说你的游戏名会变成Thief,但实际上并不会变,只是NPC称呼你为小偷。

我只是好奇试了一下,然后回到商店被老板电死后不小心保存了,然后玛林就一直叫我小偷了,难受。


找方法修复,在中文圈找不到,在外文圈只找到一堆讥讽的贴子并说直至删档重开都无法摆脱。我就纳闷了,这应该是在存档里有状态位标记,为什么就没人给出方案。搜索存档修改器,找到的也没有此项修改。既然没有,那就只能自己动手丰衣足食了。


以下内容有点技术性与枯燥。

首先,梦见岛DX (Links Awakening DX) 是有开源反汇编项目的,项目在此。其次梦见岛DX有个开源的 save editor, 项目在此

在项目里搜索Thief,很容易找到一个变量 wIsThief 在 wram.asm

; Higher digits of the player rupees count
wRupeeCountHigh::
  ds 1 ; DB5D
; Lower digits of the player rupees count
wRupeeCountLow::
  ds 1 ; DB5E
wIsThief::
  ds 1 ; DB6E

wIsThief 地址是 DB6E,但这不是在 sav 文件里的 offset。

查看 save editor 里发现修改卢比在 sav 文件里的 offset 是 0x462 与 0x463,而从上面wram.asm 找到对应该

wRupeeCountHigh DB5D 0x462
wRupeeCountLow  DB5E 0x463

而 wIsThief (DB6E) = wRupeeCountLow (DB5E) + 0x10,猜测在 sav 文件里 offset 距离应该是一样的,所以 wIsThief 在 sav 文件中的 offset 应该 0x463 + 0x10 = 0x473。

用十六进制编辑器打开 sav 文件,go to 0x473,发现值是 02, 确实我偷了两次东西。试着将值改为 00,再读档,玛林不再叫我小偷了,成功。



有三个存档slot,从 save editor 得知每份长度 0x3ad,所以计算得到

第一个小偷状态位置: 0x473

第二个小偷状态位置: 0x473 + 0x3ad = 0x820

第三个小偷状态位置: 0x820 + 0x3ad = 0xbcd


我给 save editor 提交了 PR。如果作者 merge 我的 PR 后,大家就可以直接用 save editor 修改了

3.jpg

在此之前,可以用十六进制编辑器修改上述位置。

有趣的是,这个标志位竟然不是boolean,而一个counter,偷了多少次就是多少值,我就猜想会不会溢出重置为0,试了一下还真会。 我先修改0x473的值为0xFF,然后读档进游戏,先找玛林对话,她叫我小偷我很伤心,然后我再去商店偷一次东西,出来再找玛林,她不叫我小偷了,存档,查看存档文件0xFF的值变成0x00。所以有不修改存档完全走正常流程又像开齐相册又不想NPC叫你小偷的,可以去商店偷够256让counter溢出变回0,但这毅力....而且偷东西也挺难的...


至此,完美撒花~~~~~吗?


为什么 0x473 offset 就是标记偷东西次数?

本人职业病又犯了,不读源码不舒服,以下内容更加学术与枯燥。

能过wIsThief 找到 bank0.asm 代码

.clearEntitiesStatusLoop
    ldi  [hl], a                                  ;; 00:186C $22
    dec  e                                        ;; 00:186D $1D
    jr   nz, .clearEntitiesStatusLoop             ;; 00:186E $20 $FC
    ld   a, [wItemPickedUpInShop]                 ;; 00:1870 $FA $09 $C5
    and  a                                        ;; 00:1873 $A7
    jr   z, .label_1898                           ;; 00:1874 $28 $22
    push af                                       ;; 00:1876 $F5
    ld   a, BANK(label_004_7A5F)                  ;; 00:1877 $3E $04
    call SwitchBank                               ;; 00:1879 $CD $0C $08
    pop  af                                       ;; 00:187C $F1
    call label_004_7A5F                           ;; 00:187D $CD $5F $7A
    ld   hl, wIsThief                             ;; 00:1880 $21 $6E $DB
    inc  [hl]                                     ;; 00:1883 $34
    ld   hl, wHasStolenFromShop                   ;; 00:1884 $21 $46 $DB
    inc  [hl]                                     ;; 00:1887 $34
    ld   a, [wPhotos1]                            ;; 00:1888 $FA $0C $DC
    or   $40                                      ;; 00:188B $F6 $40
    ld   [wPhotos1], a                            ;; 00:188D $EA $0C $DC
    ld   a, $01                                   ;; 00:1890 $3E $01
    ld   [wDidStealItem], a                       ;; 00:1892 $EA $7E $D4
    xor  a                                        ;; 00:1895 $AF
    ldh  [hLinkAnimationState], a                 ;; 00:1896 $E0 $9D

这段代码应该是从商店出来后会运行,代码大概是 SwitchBank 到 bank04 的 label_004_7A5F 运行买(偷)到什么东西(如果是心则加命),然后wIsThief + 1,wHasStolenFromShop + 1,把偷东西的照片设成01。所以确认 wIsThief 是在内存中标记偷东西的次数。

内存的东西要保存到存档文件,通过搜索 save 找到 SaveGameToFile,有一段是 loopSaveMain

    ld   bc, wOverworldRoomStatus                 ;; 01:5E0C $01 $00 $D8
    ld   de, SAVE_MAIN_SIZE                       ;; 01:5E0F $11 $80 $03
.loopSaveMain
    call EnableSRAM                               ;; 01:5E12 $CD $D0 $27
    ld   a, [bc]                                  ;; 01:5E15 $0A
    inc  bc                                       ;; 01:5E16 $03
    call EnableSRAM                               ;; 01:5E17 $CD $D0 $27
    ldi  [hl], a                                  ;; 01:5E1A $22
    dec  de                                       ;; 01:5E1B $1B
    ld   a, e                                     ;; 01:5E1C $7B
    or   d                                        ;; 01:5E1D $B2
    jr   nz, .loopSaveMain                        ;; 01:5E1E $20 $F

大意是设bc是 wOverworldRoomStatus 地址,de是SAVE_MAIN_SIZE,循环SAVE_MAIN_SIZE次从wOverworldRoomStatus地址处往后的值写入SRAM


sram.asm 中可以看到存档内容的排列

DEF SAVE_PREFIX_SIZE EQU 5
DEF SAVE_MAIN_SIZE   EQU $0380
DEF SAVE_DX1_SIZE    EQU $0005 ; wColorDungeonItemFlags
DEF SAVE_DX2_SIZE    EQU $0020 ; wColorDungeonRoomStatus
DEF SAVE_DX3_SIZE    EQU $0003 ; wTunicType, wPhotos1, wPhotos2
assert SAVE_MAIN_SIZE == (wSaveSlotNames - wOverworldRoomStatus)
section "SRAM", sram[$A000], bank[0]
; For some reason the first 256 bytes are skipped in the sram.
    ds $100
SaveGame1:
.prefix:
    ds SAVE_PREFIX_SIZE
.main:
    ds SAVE_MAIN_SIZE
.dx1:
    ds SAVE_DX1_SIZE
.dx2:
    ds SAVE_DX2_SIZE
.dx3:
    ds SAVE_DX3_SIZE
.end:

前0x00 - 0xFF 共256 字节unused,接着存5字节prefix,然后是0x380字节的main,5字节DX1, 0x20字节DX2, 3字节DX3

从 wram.asm 可以找到 wOverworldRoomStatus 是 0xD800 ,前面也找到 wIsThief 是 0xDB6E,而main从 0x100 + 0x5 = 0x105 开始存

main + (wIsThief - wOverworldRoomStatus)= 0x105 + (0xDB6E - 0xD800) = 0x473

这就是为什么wIsThief在WRAM内存是0xDB6E,存到sav文件里的0x473 offset

而且一份save slot大小是0x3ad,与 save editor 里定义的一样

 0x5 + 0x380 + 0x5+ 0x20 + 0x3 = 0x3ad


读懂了源码与理清了存档文件结构,可以修改更多在save editor里没有东西,例如 wIsMarinFollowingLink (DB73) 在 0x478,wIsMarinInAnimalVillage (DB74) 在 0x479,只有玛林跟着林克时,才能做很多有趣的事,看到很多有趣的对话以前拍三张照片,但玛林在动物村离队后这些就都错过了。此时将0x478改为01,将0x479改为00,玛林又再跟着你了


真完结撒花!!!!


上一篇:到了到了,我的大mini也到了,感谢dm123大佬!
下一篇:讨论一下NS2卡带相关
热门回复
  • 四级用户 wNg333 7小时前
    5


    屏幕截图 2025-04-25 145220.png

    • 老男人游戏网配套论坛
      17
        立即登录 立即注册
最新回复 (15)
  • 三级用户 icemould 7小时前
    2 2
    nb!
  • 四级用户 wn_wlmq 7小时前
    0 3
    厉害
  • 四级用户 wNg333 7小时前
    5 4


    屏幕截图 2025-04-25 145220.png

  • 三级用户 叹逍遥 7小时前
    0 5
    大佬
  • 六级用户 Chris可劳迪喵 7小时前
    0 6
    能认真研究起来真棒!
  • 五级用户 CAN1780 6小时前
    1 7
    牛逼
  • 六级用户 ougapia 6小时前
    0 8
    不做小偷相册就不齐,逼死强迫症
  • 六级用户 lanshan 6小时前
    0 9
    怪不得我没法在编程的路上继续走下去  实在是没兴趣了
    大佬牛逼
  • 六级用户 RangerMarsh 6小时前
    0 10
    大佬太牛了!
  • 三级用户 Asahui 6小时前
    1 11
    ougapia 不做小偷相册就不齐,逼死强迫症

    存档可以直接开相册,有ROM hack也可以不做小偷开相册,但是总得来说还是修改存档更简单。无修改正常流程,只能做小偷才能开齐相册,但正如我上面所说,可以拼毅力再偷255次,使标志位溢出清零,这是唯一无修改的方式

    啊,原来我没写上?我写在Pull request里了,更新一下

  • 版主 fjh212 6小时前
    0 12
    膜拜技术大佬,修改清零以后,是不是回商店也不会被电了?
  • 三级用户 Asahui 6小时前
    0 13
    fjh212 膜拜技术大佬,修改清零以后,是不是回商店也不会被电了?
    只要偷东西回商店就会被电,这是另一个控制位 wHasStolenFromShop 在存档的0x44a。帖子里分析,偷了东西出商店,就会使 wIsThief (0x473) + 1,使 wHasStolenFromShop (0x44a) + 1, 开启偷东西的相册。此时回商店,被老板电死,会使 wHasStolenFromShop 变回0并使death count + 1。所以你确实可以偷东西出商店后保存,修改存档0x44a将值从01改成00保存文件,然后再读档,进商店就不会被电。但我觉得没多大必要,偷了被电死只是增加死亡次数,除非想要零死亡。游戏会自己重置wHasStolenFromShop,改存档没必要,想要零死亡我直接改存档也更快。
  • 四级用户 set77 5小时前
    0 14
    偷了东西回商店就会被电死,要再偷255次···岂不是不可能完成
  • 五级用户 正人君子 4小时前
    0 15
    有意思,技术贴支持,这是我塞尔达的启蒙作啊
  • 四级用户 soimra 3小时前
    0 16
    哥!请收下我的膝盖........偷256次没人会这么做吧,有这时间早通关多少次了。
    • 老男人游戏网配套论坛
      17
        立即登录 立即注册
发新帖
本论坛禁止发布SWITCH和PS4相关资源,若有其他侵权内容,请致邮3360342659#qq.com(#替换成@)删除。