1 local cmds
= require('commands')
2 local getopt
= require('getopt')
3 local os
= require('os')
4 local io
= require('io')
5 local bin
= require('bin')
6 local utils
= require('utils')
7 local ansicolors
= require('ansicolors')
8 local amiibo_tools
= require('amiibo_tools')
11 author
= 'George Talusan'
14 This script will try to restore a binary datadump of an Amiibo to a blank NTAG215.
15 It will recalculate PWD and PACK if necessary, set the appropriate password and sector lock bytes.
17 NOTE: PyAmiibo must be installed. The helper script pyscripts/amiibo_change_uid.py depends on PyAmiibo.
19 YMMV if a non-blank NTAG215 is provided!
22 1. script run hf_mfu_amiibo_restore
23 2. script run hf_mfu_amiibo_restore -f myfile -k password
26 script run hf_mfu_amiibo_restore [-h] [-f <filename> -k <password>]
30 -f : filename for the datadump to read (bin)
31 -k : password of blank NTAG 215 (use `hf mfu info` to find it)
34 local DEBUG
= false -- the debug flag
36 local bxor
= bit32
.bxor
37 local sub
= string.sub
38 local format = string.format
41 -- A debug printout-function
42 local function dbg(args
)
43 if not DEBUG
then return end
44 if type(args
) == 'table' then
55 -- This is only meant to be used when errors occur
56 local function oops(err
)
58 core
.clearCommandBuffer()
68 print(ansicolors
.cyan
..'Usage'..ansicolors
.reset
)
70 print(ansicolors
.cyan
..'Arguments'..ansicolors
.reset
)
72 print(ansicolors
.cyan
..'Example usage'..ansicolors
.reset
)
77 local function ExitMsg(msg
)
78 print( string.rep('--',20) )
79 print( string.rep('--',20) )
84 local function main(args
)
85 print( string.rep('--',20) )
86 print( string.rep('--',20) )
88 local result
, err
, hex
89 local inputTemplate
= 'dumpdata.bin'
92 for o
, a
in getopt
.getopt(args
, 'hf:k:') do
93 if o
== 'h' then return help() end
94 if o
== 'f' then inputTemplate
= a
end
95 if o
== 'k' then password
= a
end
98 print(('Loading data from %s'):format(inputTemplate
))
99 hex
, err
= utils
.ReadDumpFile(inputTemplate
)
100 if not hex
then return oops(err
) end
102 if not password
or #password
~= 8 then
103 return oops('Expecting 4 byte password (hint: use `hf mfu info` to get it)')
111 local amiibo_offset
= 0
112 local amiibo_info
= hex
:sub(amiibo_offset
+ 169, amiibo_offset
+ 169 + 15):lower()
113 local amiibo_game
= amiibo_info
:sub(1, 3)
114 local amiibo_type
= amiibo_info
:sub(7, 8)
115 local amiibo_series
= amiibo_info
:sub(13, 14)
117 dbg('raw: '..ansicolors
.green
..amiibo_info
..ansicolors
.reset
)
118 print('game: '..ansicolors
.green
..amiibo_tools
.db
.game_series
[("0x%s"):format(amiibo_game
)]..ansicolors
.reset
)
119 print('character: '..ansicolors
.green
..amiibo_tools
.db
.amiibos
[("0x%s"):format(amiibo_info
)].name
..ansicolors
.reset
)
120 print('type: '..ansicolors
.green
..amiibo_tools
.db
.types
[("0x%s"):format(amiibo_type
)]..ansicolors
.reset
)
121 print('series: '..ansicolors
.green
..amiibo_tools
.db
.amiibo_series
[("0x%s"):format(amiibo_series
)]..ansicolors
.reset
)
123 local uid
= core
.ul_read_uid();
125 return oops("Can't read UID of NTAG215 card. Reposition card and try again.")
128 local tmp
= ('%s.bin'):format(os
.tmpname())
129 local amiibo_file
= io
.open(tmp
, 'w+b')
130 amiibo_file
:write(bin
.pack('H', hex
))
132 local tmp2
= ('%s.bin'):format(os
.tmpname())
134 print('generating new Amiibo binary for NTAG215 '..ansicolors
.green
..uid
)
135 core
.clearCommandBuffer()
136 core
.console(('script run amiibo_change_uid %s %s %s %s'):format(uid
, tmp
, tmp2
, core
.search_file('resources/key_retail', '.bin')))
138 -- let's sanity check the output
139 hex
, err
= utils
.ReadDumpFile(tmp2
)
140 if not hex
or #hex
~= 1080 then
143 return oops('There was a problem generating the output Amiibo')
146 core
.console(('hf mfu restore -f %s -k %s'):format(tmp2
, password
))
148 -- re-write some blocks because `hf mfu restore` won't write out blocks 0-3, and PyAmiibo won't give a PACK/PWD
149 local pwd
, pack
= core
.keygen_algo_b(uid
)
150 core
.console(('hf mfu wrbl -b 3 -d F110FFEE -k %s'):format(password
)) -- CC?
151 core
.console(('hf mfu wrbl -b 134 -d %04X0000 -k %s'):format(pack
, password
)) -- PACK/RFUI
152 core
.console(('hf mfu wrbl -b 133 -d %08X -k %s'):format(pwd
, password
)) -- PWD
153 core
.console(('hf mfu wrbl -b 131 -d 00000004 -k %08X'):format(pwd
)) -- CFG0
154 core
.console(('hf mfu wrbl -b 132 -d 5F000000 -k %08X'):format(pwd
)) -- CFG1
156 local lock_bytes
= hex
:sub(17, 24)
157 dbg('lock_bytes: '..lock_bytes
)
158 core
.console(('hf mfu wrbl -b 2 -d %s -k %08X'):format(lock_bytes
, pwd
)) -- BCC1/static lock
159 core
.console(('hf mfu wrbl -b 130 -d 01000FBD -k %08X'):format(pwd
)) -- dynamic lock/RFUI