Skip to content

Instantly share code, notes, and snippets.

@nateevans
Forked from gretel/amiibo.sh
Created September 11, 2018 13:38
Show Gist options
  • Select an option

  • Save nateevans/f7ce31eb2479a68c892215c81459c169 to your computer and use it in GitHub Desktop.

Select an option

Save nateevans/f7ce31eb2479a68c892215c81459c169 to your computer and use it in GitHub Desktop.

Revisions

  1. Tom Hensel revised this gist Jul 16, 2017. 1 changed file with 7 additions and 7 deletions.
    14 changes: 7 additions & 7 deletions nanoamii.py
    Original file line number Diff line number Diff line change
    @@ -14,13 +14,13 @@
    import ufr_constants, ufr_errors

    # oh i love argparse
    parser = argparse.ArgumentParser(description='query, read, encode (using amiitool), write and lock NTAG215 (using uFR Nano hardware) for the purpose of researching Nintendo''s Amiibo infrastructure Raw')
    parser.add_argument('--filename', dest='filename', help='name of the file containing binary data')
    parser.add_argument('--read', dest='read', action='store_true', help='read data from tag')
    parser.add_argument('--encode', dest='encode', action='store_true', help='encode data (calls ''encode.sh'' when uid is known)')
    parser.add_argument('--write', dest='write', action='store_true', help='write data to tag')
    parser.add_argument('--lock', dest='lock', action='store_true', help='set dynamic and static lock bytes (!)')
    parser.add_argument('--loop', dest='loop', action='store_true', help='do not exit on completion but loop')
    parser = argparse.ArgumentParser(description='query, read, encode (using amiitool), write and lock NTAG215 (using uFR Nano hardware) for the purpose of researching Nintendo\'s Amiibo infrastructure')
    parser.add_argument('-f', '--filename', dest='filename', help='name of the file containing binary data')
    parser.add_argument('-r', '--read', dest='read', action='store_true', help='read data from tag')
    parser.add_argument('-e', '--encode', dest='encode', action='store_true', help='encode data (calls "encode.sh" when uid is known)')
    parser.add_argument('-w', '--write', dest='write', action='store_true', help='write data to tag')
    parser.add_argument('-x', '--lock', dest='lock', action='store_true', help='set dynamic and static lock bytes (!)')
    parser.add_argument('-l', '--loop', dest='loop', action='store_true', help='do not exit on completion but loop (useful for batch jobs)')
    args = parser.parse_args()

    class NanoAmii(threading.Thread):
  2. Tom Hensel revised this gist Jul 16, 2017. 1 changed file with 7 additions and 7 deletions.
    14 changes: 7 additions & 7 deletions nanoamii.py
    Original file line number Diff line number Diff line change
    @@ -9,18 +9,18 @@
    import argparse
    import os, threading, time, sys
    import pyperclip
    import subprocess
    import traceback
    import ufr_constants, ufr_errors
    import subprocess

    # oh i love argparse
    parser = argparse.ArgumentParser(description='read and write binary blob to nfc read_tag')
    parser = argparse.ArgumentParser(description='query, read, encode (using amiitool), write and lock NTAG215 (using uFR Nano hardware) for the purpose of researching Nintendo''s Amiibo infrastructure Raw')
    parser.add_argument('--filename', dest='filename', help='name of the file containing binary data')
    parser.add_argument('--loop', dest='loop', action='store_true', help='do not exit on completion but loop')
    parser.add_argument('--write', dest='write', action='store_true', help='write data to tag')
    parser.add_argument('--read', dest='read', action='store_true', help='read data from tag')
    parser.add_argument('--encode', dest='encode', action='store_true', help='encode data')
    parser.add_argument('--encode', dest='encode', action='store_true', help='encode data (calls ''encode.sh'' when uid is known)')
    parser.add_argument('--write', dest='write', action='store_true', help='write data to tag')
    parser.add_argument('--lock', dest='lock', action='store_true', help='set dynamic and static lock bytes (!)')
    parser.add_argument('--loop', dest='loop', action='store_true', help='do not exit on completion but loop')
    args = parser.parse_args()

    class NanoAmii(threading.Thread):
    @@ -53,7 +53,7 @@ def __init__(self):
    #print('__init__')
    threading.Thread().__init__()

    print(args)
    print(sys.argv[0], args)
    self.block_len = 0
    self.blocks_num = 0
    self.card_size_linear = c_uint8()
    @@ -107,7 +107,7 @@ def encode(self, filename : str, uid) -> int:
    output = subprocess.check_output(['bash', 'encode.sh', filename, uid])
    args.filename = output.decode('utf-8').strip()
    except subprocess.CalledProcessError as e:
    self.abort('subprocess: %s' % e.output)
    self.abort('error on subprocess: %s' % e.output)
    print(' filename', args.filename)
    self.load_blob(args.filename)

  3. Tom Hensel revised this gist Jul 16, 2017. No changes.
  4. Tom Hensel revised this gist Jul 16, 2017. 3 changed files with 34 additions and 17 deletions.
    21 changes: 11 additions & 10 deletions amiibo.sh
    Original file line number Diff line number Diff line change
    @@ -1,16 +1,13 @@
    #!/bin/bash
    #
    # https://gist.github.com/gretel/dd80c854e22c2afd20f5aebc62015096#file-amiibo-sh
    #
    # This is a companion script to https://github.com/konstantin-kelemen/arduino-amiibo-tools
    # The original post this was crafted for was https://games.kel.mn/en/create-amiibo-clones-with-arduino/
    # For more info go to https://games.kel.mn/en/companion-script-to-simplify-amiibo-cloning-with-arduino/

    # requirements:
    # sha1sum (part of coreutils)
    # xxd (part of vim)
    # hexdump
    # amiitool (https://github.com/socram8888/amiitool)
    #requirements:
    #sha1sum (part of coreutils)
    #xxd (part of vim)
    #hexdump
    #amiitool (https://github.com/socram8888/amiitool)

    hash xxd 2>/dev/null || { echo >&2 "require xxd but it's not installed. Aborting."; exit 1; }
    hash sha1sum 2>/dev/null || { echo >&2 "require sha1sum but it's not installed. Aborting."; exit 1; }
    @@ -75,7 +72,7 @@ pw4="$(printf '%02X\n' $(( 0x55 ^ 0x$taguid4 ^ 0x$taguid6 )))"

    #decrypt the dump
    #echo Using Amiibo Tool to decrypt ${2%%.*}
    ./amiitool -d -k "$1" -i "$2" -o ${base}_dec.bin
    ./amiitool -d -k "$1" -i "$2" -o ${base}_dec.bin || exit 2

    #modify the uid record
    echo "01D4: $uid" | xxd -r - ${base}_dec.bin
    @@ -89,12 +86,16 @@ echo "0208: 000000" | xxd -r - ${base}_dec.bin
    echo "0000: $BCC1" | xxd -r - ${base}_dec.bin
    echo "0002: 0000" | xxd -r - ${base}_dec.bin

    enc_file="${base}_${uid}.bin"

    #reencrypt the uid modified dump
    #echo Using Amiibo Tool to encrypt ${2%%.*}
    ./amiitool -e -k "$1" -i ${base}_dec.bin -o ${base}_enc.bin
    ./amiitool -e -k "$1" -i ${base}_dec.bin -o ${enc_file} || exit 3

    rm ${base}_dec.bin

    echo "${enc_file}"

    # echo "**** START OF HEXDUMP ****"
    # hexdump -v -e " 4/1 \"0x%02X, \" \"\n\"" "enc.bin" > hexdump
    # truncate -s -2 hexdump
    4 changes: 2 additions & 2 deletions encode.sh
    Original file line number Diff line number Diff line change
    @@ -3,11 +3,11 @@
    if [ $# -ne 2 ]
    then
    echo "usage: $0 amiibo.bin uuid"
    exit
    exit 1
    fi

    KEY='retail_key.bin'
    FILE=$1
    UUID=$2

    ./amiibo.sh ${KEY} ${FILE} ${UUID}
    ./amiibo.sh ${KEY} ${FILE} ${UUID}
    26 changes: 21 additions & 5 deletions nanoamii.py
    Original file line number Diff line number Diff line change
    @@ -11,13 +11,15 @@
    import pyperclip
    import traceback
    import ufr_constants, ufr_errors
    import subprocess

    # oh i love argparse
    parser = argparse.ArgumentParser(description='read and write binary blob to nfc read_tag')
    parser.add_argument('--filename', dest='filename', help='name of the file containing binary data')
    parser.add_argument('--loop', dest='loop', action='store_true', help='do not exit on completion but loop')
    parser.add_argument('--write', dest='write', action='store_true', help='write data to tag')
    parser.add_argument('--read', dest='read', action='store_true', help='read data from tag')
    parser.add_argument('--encode', dest='encode', action='store_true', help='encode data')
    parser.add_argument('--lock', dest='lock', action='store_true', help='set dynamic and static lock bytes (!)')
    args = parser.parse_args()

    @@ -28,7 +30,7 @@ def load_blob(self, filename : str) -> bytearray:
    # TODO: fail gracefully if argument missing/file nonexistant
    self.filename = filename
    # read file as binary
    print('FILE', self.filename)
    print('LOAD', self.filename)
    with open(self.filename, 'rb') as binary:
    self.data = bytearray(binary.read())
    # store length
    @@ -51,6 +53,7 @@ def __init__(self):
    #print('__init__')
    threading.Thread().__init__()

    print(args)
    self.block_len = 0
    self.blocks_num = 0
    self.card_size_linear = c_uint8()
    @@ -98,6 +101,16 @@ def open(self) -> int:
    self.connected = False
    return call_result

    def encode(self, filename : str, uid) -> int:
    print('ENCODE', filename, uid)
    try:
    output = subprocess.check_output(['bash', 'encode.sh', filename, uid])
    args.filename = output.decode('utf-8').strip()
    except subprocess.CalledProcessError as e:
    self.abort('subprocess: %s' % e.output)
    print(' filename', args.filename)
    self.load_blob(args.filename)

    def close(self) -> int:
    # reset
    self.card_size_linear = c_uint8()
    @@ -231,7 +244,6 @@ def query(self) -> bool:
    return True

    def contact(self):
    call_result = c_uint8()
    errors = 0

    tag = self.query()
    @@ -250,10 +262,14 @@ def contact(self):
    filename = self.card_uid + '.bin'
    self.save_blob(filename, read_data)

    if(args.encode):
    if(args.filename):
    self.encode(args.filename, self.card_uid)
    else:
    self.abort('filename required')

    if(args.write):
    # write
    if(args.write):
    self.load_blob(args.filename)
    self.load_blob(args.filename)
    errors = self.write_tag()
    if(errors > 0):
    self.abort('error on write')
  5. Tom Hensel revised this gist Jun 23, 2017. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion amiibo.sh
    Original file line number Diff line number Diff line change
    @@ -1,6 +1,6 @@
    #!/bin/bash
    #
    # edited by github@jitter.eu
    # https://gist.github.com/gretel/dd80c854e22c2afd20f5aebc62015096#file-amiibo-sh
    #
    # This is a companion script to https://github.com/konstantin-kelemen/arduino-amiibo-tools
    # The original post this was crafted for was https://games.kel.mn/en/create-amiibo-clones-with-arduino/
  6. Tom Hensel revised this gist Jun 23, 2017. 2 changed files with 116 additions and 0 deletions.
    103 changes: 103 additions & 0 deletions amiibo.sh
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,103 @@
    #!/bin/bash
    #
    # edited by github@jitter.eu
    #
    # This is a companion script to https://github.com/konstantin-kelemen/arduino-amiibo-tools
    # The original post this was crafted for was https://games.kel.mn/en/create-amiibo-clones-with-arduino/
    # For more info go to https://games.kel.mn/en/companion-script-to-simplify-amiibo-cloning-with-arduino/

    # requirements:
    # sha1sum (part of coreutils)
    # xxd (part of vim)
    # hexdump
    # amiitool (https://github.com/socram8888/amiitool)

    hash xxd 2>/dev/null || { echo >&2 "require xxd but it's not installed. Aborting."; exit 1; }
    hash sha1sum 2>/dev/null || { echo >&2 "require sha1sum but it's not installed. Aborting."; exit 1; }
    hash hexdump 2>/dev/null || { echo >&2 "require hexdump but it's not installed. Aborting."; exit 1; }
    hash ./amiitool 2>/dev/null || { echo >&2 "require amiitool but it's not installed or in the currect directory. Aborting."; exit 1; }

    if [ $# -ne 3 ]
    then
    echo "usage: $0 key_file encrypted_dump_file blank_tagid"
    exit
    fi

    if [ "$(sha1sum "$1" |cut -d' ' -f1)" != "bbdbb49a917d14f7a997d327ba40d40c39e606ce" ]
    then
    echo "key_file not sane"
    exit
    fi

    base=${2%%.*}

    #get the empty tag uid:
    taguid=$3

    taguid0="$(echo "$taguid" | cut -b1,2)" # Byte 0 (should bx 0x04)
    taguid1="$(echo "$taguid" | cut -b3,4)" # Byte 1 (we count from 0)
    taguid2="$(echo "$taguid" | cut -b5,6)" # Byte 2


    if [ ${#3} -eq 18 ]; then # Check if user provided a long taguid

    taguid3="$(echo "$taguid" | cut -b9,10)" # Byte 4
    taguid4="$(echo "$taguid" | cut -b11,12)" # Byte 5
    taguid5="$(echo "$taguid" | cut -b13,14)" # Byte 6
    taguid6="$(echo "$taguid" | cut -b15,16)" # Byte 7

    uid="$(echo "$taguid" | cut -b1-16)"
    BCC1="$(echo "$taguid" | cut -b17,18)" # Pull out the BCC1 for use later

    elif [ ${#3} -eq 14 ]; then # Check if user provided a short taguid

    taguid3="$(echo "$taguid" | cut -b7,8)" # Byte 3
    taguid4="$(echo "$taguid" | cut -b9,10)" # Byte 4
    taguid5="$(echo "$taguid" | cut -b11,12)" # Byte 5
    taguid6="$(echo "$taguid" | cut -b13,14)" # Byte 6

    # Convert 7byte to 9byte for script
    BCC0="$(printf '%02X\n' $(( 0x88 ^ 0x$taguid0 ^ 0x$taguid1 ^ 0x$taguid2 )))" # Calculate the BCC0
    BCC1="$(printf '%02X\n' $(( 0x$taguid3 ^ 0x$taguid4 ^ 0x$taguid5 ^ 0x$taguid6 )))" # Calculate the BCC1
    uid="$taguid0$taguid1$taguid2$BCC0$taguid3$taguid4$taguid5$taguid6"
    fi

    if [ ${#uid} -ne 16 ]; then
    echo "please pick a valid 7 or 9 byte uid"
    exit
    fi

    # Generate the password from the tag
    pw1="$(printf '%02X\n' $(( 0xAA ^ 0x$taguid1 ^ 0x$taguid3 )))"
    pw2="$(printf '%02X\n' $(( 0x55 ^ 0x$taguid2 ^ 0x$taguid4 )))"
    pw3="$(printf '%02X\n' $(( 0xAA ^ 0x$taguid3 ^ 0x$taguid5 )))"
    pw4="$(printf '%02X\n' $(( 0x55 ^ 0x$taguid4 ^ 0x$taguid6 )))"

    #decrypt the dump
    #echo Using Amiibo Tool to decrypt ${2%%.*}
    ./amiitool -d -k "$1" -i "$2" -o ${base}_dec.bin

    #modify the uid record
    echo "01D4: $uid" | xxd -r - ${base}_dec.bin

    #add password
    echo "0214: $pw1$pw2$pw3$pw4" | xxd -r - ${base}_dec.bin #pw
    echo "0218: 8080" | xxd -r - ${base}_dec.bin

    #set the default values
    echo "0208: 000000" | xxd -r - ${base}_dec.bin
    echo "0000: $BCC1" | xxd -r - ${base}_dec.bin
    echo "0002: 0000" | xxd -r - ${base}_dec.bin

    #reencrypt the uid modified dump
    #echo Using Amiibo Tool to encrypt ${2%%.*}
    ./amiitool -e -k "$1" -i ${base}_dec.bin -o ${base}_enc.bin

    rm ${base}_dec.bin

    # echo "**** START OF HEXDUMP ****"
    # hexdump -v -e " 4/1 \"0x%02X, \" \"\n\"" "enc.bin" > hexdump
    # truncate -s -2 hexdump
    # echo "" >> hexdump
    # echo "" >> hexdump
    # cat hexdump
    13 changes: 13 additions & 0 deletions encode.sh
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,13 @@
    #!/bin/sh

    if [ $# -ne 2 ]
    then
    echo "usage: $0 amiibo.bin uuid"
    exit
    fi

    KEY='retail_key.bin'
    FILE=$1
    UUID=$2

    ./amiibo.sh ${KEY} ${FILE} ${UUID}
  7. Tom Hensel revised this gist Jun 23, 2017. No changes.
  8. Tom Hensel revised this gist Jun 18, 2017. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion nanoamii.py
    Original file line number Diff line number Diff line change
    @@ -1,6 +1,6 @@
    #!/usr/bin/env python3
    #
    # https://gist.github.com/
    # https://gist.github.com/gretel/dd80c854e22c2afd20f5aebc62015096
    # https://www.d-logic.net/nfc-rfid-reader-sdk/products/nano-nfc-rfid-reader/
    # https://code.d-logic.net/nfc-rfid-reader-sdk/ufr-lib
    #
  9. Tom Hensel created this gist Jun 18, 2017.
    304 changes: 304 additions & 0 deletions nanoamii.py
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,304 @@
    #!/usr/bin/env python3
    #
    # https://gist.github.com/
    # https://www.d-logic.net/nfc-rfid-reader-sdk/products/nano-nfc-rfid-reader/
    # https://code.d-logic.net/nfc-rfid-reader-sdk/ufr-lib
    #

    from ctypes import *
    import argparse
    import os, threading, time, sys
    import pyperclip
    import traceback
    import ufr_constants, ufr_errors

    # oh i love argparse
    parser = argparse.ArgumentParser(description='read and write binary blob to nfc read_tag')
    parser.add_argument('--filename', dest='filename', help='name of the file containing binary data')
    parser.add_argument('--loop', dest='loop', action='store_true', help='do not exit on completion but loop')
    parser.add_argument('--write', dest='write', action='store_true', help='write data to tag')
    parser.add_argument('--read', dest='read', action='store_true', help='read data from tag')
    parser.add_argument('--lock', dest='lock', action='store_true', help='set dynamic and static lock bytes (!)')
    args = parser.parse_args()

    class NanoAmii(threading.Thread):

    def load_blob(self, filename : str) -> bytearray:
    # first argument (required)
    # TODO: fail gracefully if argument missing/file nonexistant
    self.filename = filename
    # read file as binary
    print('FILE', self.filename)
    with open(self.filename, 'rb') as binary:
    self.data = bytearray(binary.read())
    # store length
    self.data_len = len(self.data)
    # TODO: check for min/max length
    print(' read', self.data_len, self.data)

    def save_blob(self, filename : str, data : bytes, mode : str = 'wb'):
    print('SAVE', repr(filename))
    with open(filename, mode) as binary:
    binary.write(data)
    binary.close()

    def main_thread(self):
    # loop
    while self.run:
    self.loop()

    def __init__(self):
    #print('__init__')
    threading.Thread().__init__()

    self.block_len = 0
    self.blocks_num = 0
    self.card_size_linear = c_uint8()
    self.card_size_raw = c_uint8()
    self.card_type = None
    self.card_uid = (c_ubyte * 10)()
    self.connected = False
    self.run = True

    # TODO: abstraction
    self.ufr = cdll.LoadLibrary(os.getcwd() + '/ufr-lib/osx/x86_64/libuFCoder.dylib')
    self.ufr.ReaderSoftRestart()

    self.data = bytearray()
    self.data_len = 0

    if(args.write and args.filename == None):
    self.abort('filename required')

    # start thread
    threading.Thread(target = self.main_thread).start()

    def open(self) -> int:
    reader_fw_bld = c_uint32()
    reader_fw_maj = c_uint32()
    reader_fw_min = c_uint32()
    reader_type = c_uint32()

    print('OPEN')
    # open device
    call_result = self.ufr.ReaderOpen()
    if call_result == ufr_constants.DL_OK:
    self.ufr.GetReaderType(byref(reader_type))
    print(' type', reader_type.value)

    self.ufr.GetReaderFirmwareVersion(byref(reader_fw_min), byref(reader_fw_maj))
    self.ufr.GetBuildNumber(byref(reader_fw_bld))
    print(' firmware %s.%s.%s' % (reader_fw_min.value, reader_fw_maj.value, reader_fw_bld.value))

    #self.ufr.ReaderUISignal(ufr_constants.FUNCT_LIGHT_OK, ufr_constants.FUNCT_SOUND_OK)
    self.ufr.AutoSleepSet(10)
    self.connected = True
    else:
    print(' error', ufr_errors.UFCODER_ERROR_CODES[call_result])
    self.connected = False
    return call_result

    def close(self) -> int:
    # reset
    self.card_size_linear = c_uint8()
    self.card_size_raw = c_uint8()
    self.card_type = c_uint8()
    self.connected = False
    self.card_uid = (c_ubyte * 10)()

    # close device
    call_result = self.ufr.ReaderClose()
    print('CLOSE', hex(call_result))
    return call_result

    def read_tag(self) -> bytearray:
    block = (c_uint8 * self.block_len)()
    block_pos = 0
    errors = 0
    result = bytearray()

    print('READ', self.blocks_num, self.block_len)
    for r in range(0, self.blocks_num):
    block_pos = r
    call_result = self.ufr.BlockRead(byref(block), block_pos, ufr_constants.MIFARE_AUTHENT1A, 0)
    if(call_result == ufr_constants.DL_OK):
    print(' R', block_pos, ufr_errors.UFCODER_ERROR_CODES[call_result], repr(bytes(block)))
    for b in range(0, self.block_len):
    result.append(block[b])
    else:
    errors += 1
    print(' !R', block_pos, ufr_errors.UFCODER_ERROR_CODES[call_result])
    return errors, result

    def write_tag(self) -> int:
    block = bytes()
    block_pos = c_uint8()
    call_result = c_uint8()
    data_pos = 0
    errors = 0

    print('WRITE', self.blocks_num, self.block_len)
    for block_pos in range(0, self.blocks_num):
    if(block_pos < 3 or block_pos == 130):
    # skip lock bytes
    print(' -W', block_pos)
    else:
    block = bytes(self.data[data_pos:data_pos + self.block_len])
    call_result = self.ufr.BlockWrite(block, block_pos, ufr_constants.MIFARE_AUTHENT1A, 0)
    if call_result == ufr_constants.DL_OK:
    print(' W', block_pos, ufr_errors.UFCODER_ERROR_CODES[call_result], repr(block))
    else:
    errors = 1
    print(' !W', block_pos, ufr_errors.UFCODER_ERROR_CODES[call_result], repr(block))
    break
    data_pos += 4
    return errors

    def write_dynlock(self) -> int:
    b = 0x01, 0x00, 0x0F, 0xBD
    block = bytes(b)
    block_pos = 130
    call_result = c_uint8()
    errors = 0

    print('DYNLOCK', block_pos, block)
    call_result = self.ufr.BlockWrite(block, block_pos, ufr_constants.MIFARE_AUTHENT1A, 0)
    if call_result == ufr_constants.DL_OK:
    print(' DL', block_pos, ufr_errors.UFCODER_ERROR_CODES[call_result], repr(block))
    else:
    errors = 1
    print(' !DL', block_pos, ufr_errors.UFCODER_ERROR_CODES[call_result], repr(block))
    return errors

    def write_statlock(self) -> int:
    b = 0x0F, 0xE0, 0x0F, 0xE0
    block = bytes(b)
    block_pos = 2
    call_result = c_uint8()
    errors = 0

    print('STATLOCK', block_pos, block)
    call_result = self.ufr.BlockWrite(block, block_pos, ufr_constants.MIFARE_AUTHENT1A, 0)
    if call_result == ufr_constants.DL_OK:
    print(' SL', block_pos, ufr_errors.UFCODER_ERROR_CODES[call_result], repr(block))
    else:
    errors = 1
    print(' !SL', block_pos, ufr_errors.UFCODER_ERROR_CODES[call_result], repr(block))
    return errors

    def query(self) -> bool:
    card_size_linear = c_uint8()
    card_size_raw = c_uint8()
    card_type = c_uint8()
    card_uidsize = c_uint8()
    card_uid = (c_ubyte * 10)()

    # get tag type
    call_result = self.ufr.GetDlogicCardType(byref(card_type))
    if call_result == ufr_constants.DL_OK:
    print('QUERY', ufr_errors.UFCODER_ERROR_CODES[call_result])
    print(' type', card_type.value, 'name', ufr_constants.CardName(card_type.value))
    self.card_type = card_type.value
    else:
    return False

    # size of memory, length of block
    self.blocks_num = ufr_constants.MaxBlock(self.card_type)
    self.block_len = ufr_constants.BlockLength(self.card_type)

    # get tag identifier (uid)
    call_result = self.ufr.GetCardIdEx(byref(card_type), card_uid, byref(card_uidsize))
    if call_result == ufr_constants.DL_OK:
    # compose uid
    c = ''
    for n in range(card_uidsize.value):
    c = c + format(card_uid[n], '02x')
    print(' uid', c)
    self.card_uid = c
    else:
    self.abort('error getting uid of tag')
    # copy to clipboard
    pyperclip.copy(c)
    #self.save_blob('/tmp/nanoamii.last_uid', c, 'w')

    call_result = self.ufr.GetCardSize(byref(card_size_linear), byref(card_size_raw))
    if call_result == ufr_constants.DL_OK:
    self.card_size_raw = card_size_raw.value
    self.card_size_linear = card_size_linear.value
    print(' size linear', card_size_linear.value, 'raw', card_size_raw.value)
    else:
    self.abort('error getting memory sizes of tag')
    return True

    def contact(self):
    call_result = c_uint8()
    errors = 0

    tag = self.query()
    if(tag != True):
    return

    if(args.read):
    read_data = ''
    errors, read_data = self.read_tag()
    if(errors > 0):
    self.abort('error on read')
    # TODO error handling
    if(args.filename != None):
    filename = args.filename
    else:
    filename = self.card_uid + '.bin'
    self.save_blob(filename, read_data)

    if(args.write):
    # write
    if(args.write):
    self.load_blob(args.filename)
    errors = self.write_tag()
    if(errors > 0):
    self.abort('error on write')

    if(args.lock):
    errors = self.write_dynlock()
    if(errors > 0):
    self.abort('error on writing dynamic lock bytes')
    else:
    errors = self.write_statlock()
    if(errors > 0):
    self.abort('error on writing static lock bytes')
    self.ufr.ReaderUISignal(ufr_constants.FUNCT_LIGHT_OK, ufr_constants.FUNCT_SOUND_OK)

    if(args.loop != True):
    self.run = False
    else:
    time.sleep(1.5)

    def loop(self):
    try:
    if self.connected != True:
    # open
    self.open()
    elif self.connected:
    # connected
    result = self.contact()
    if result == 0xa4:
    # happens on usb disconnection - allow reconnection
    self.close()
    else:
    # ensure
    self.close()
    except:
    # catch exceptions
    print('\nEXCEPTION', traceback.format_exc())
    sys.exit(1)
    finally:
    # dont run wild
    time.sleep(0.5)

    def abort(self, reason):
    self.ufr.ReaderUISignal(ufr_constants.FUNCT_LIGHT_ERROR, ufr_constants.FUNCT_SOUND_ERROR)
    raise SystemExit(reason)

    if __name__ == '__main__':
    #print('__main__')
    nanoamii = NanoAmii()