client/luascripts/hf_14a_read_ltocm.lua
local cmds = require('commands')
local getopt = require('getopt')
local lib14a = require('read14a')
local utils = require('utils')
local ansicolors  = require('ansicolors')

copyright = ''
author = 'Kevin'
version = 'v1.0.2'
desc = [[
This is a script that reads LTO-CM  ISO14443a tags.
It starts from block 0 and ends at default block 254.
]]
example = [[
    -- default
    script run hf_14a_read-ltocm

    -- stop at block 10
    script run hf_14a_read-ltocm -e 10
]]
usage = [[
script run hf_14a_read-ltocm -h -s -e
]]
arguments = [[
      h   this helptext
      s   start block in decimal
      e   end block in decimal
]]

-- Some globals
local DEBUG = false -- the debug flag
local lshift = bit32.lshift
local band = bit32.band
---
-- A debug printout-function
local function dbg(args)
    if not DEBUG then return end
    if type(args) == 'table' then
        local i = 1
        while args[i] do
            dbg(args[i])
            i = i+1
        end
    else
        print('###', args)
    end
end
---
-- This is only meant to be used when errors occur
local function oops(err)
    print('ERROR:', err)
    core.clearCommandBuffer()
    return nil, err
end
---
-- Usage help
local function help()
    print(copyright)
    print(author)
    print(version)
    print(desc)
    print(ansicolors.cyan..'Usage'..ansicolors.reset)
    print(usage)
    print(ansicolors.cyan..'Arguments'..ansicolors.reset)
    print(arguments)
    print(ansicolors.cyan..'Example usage'..ansicolors.reset)
    print(example)
end

local function sendRaw(rawdata, options)

    local flags = lib14a.ISO14A_COMMAND.ISO14A_NO_DISCONNECT + lib14a.ISO14A_COMMAND.ISO14A_RAW

    if options.connect then
        flags = flags + lib14a.ISO14A_COMMAND.ISO14A_CONNECT
    end

    if options.no_select then
        flags = flags + lib14a.ISO14A_COMMAND.ISO14A_NO_SELECT
    end

    if options.append_crc then
        flags = flags + lib14a.ISO14A_COMMAND.ISO14A_APPEND_CRC
    end

    local arg2 = #rawdata / 2
    if options.bits7 then
       arg2 = arg2 + tonumber(lshift(7, 16))
    end

    local command = Command:newMIX{cmd = cmds.CMD_HF_ISO14443A_READER,
                                arg1 = flags, -- Send raw
                                -- arg2 contains the length, which is half the length
                                -- of the ASCII-string rawdata
                                arg2 = arg2,
                                data = rawdata}
    return command:sendMIX(options.ignore_response)
end

---
-- get hex data from response
local function getdata(usbpacket)
    local cmd_response = Command.parse(usbpacket)
    local len = tonumber(cmd_response.arg1) * 2
    return string.sub(tostring(cmd_response.data), 0, len)
end

---
-- helper function to send payloads easier
local function send(payload, options)
    local res, err = sendRaw(payload, options)
    if err then
        lib14a.disconnect()
        return oops(err)
    end
    core.clearCommandBuffer()
    if options.ignore_response then
        return "ok", nil
    else
        return res, err
    end
end

---
-- The main entry point
function main(args)

    local startblock = 0
    local endblock = 254
    -- Read the parameters
    for o, a in getopt.getopt(args, 'hs:e:') do
        if o == 'h' then return help() end
        if o == 's' then startblock = a end
        if o == 'e' then endblock = a end
    end

-- Original loop
--    core.console("hf 14a raw -a -k -b 7 45")
--    local cmd_select = string.format("hf 14a raw -c -k 9370%s", serial_number)
--    core.console(cmd_select)
--    for i = 0, 254 do
--      local cmd_rd_blk = string.format("hf 14a raw -c -k 30 %02x", i)
--      core.console(cmd_rd_blk)
--      core.console("hf 14a raw -k 80")
--    end
--   core.console("hf 14a raw -r")

    -- Wakeup
    local payload = "45"
    local res, err = send(payload,{connect = true, no_select = true, ignore_response = false, append_crc = false, bits7 = true})
    if err then return end

    -- start selecting
    payload = "9320"
    res, err = send(payload,{ignore_response = false, append_crc = false})
    if err then return end

    local serial_number = getdata(res)
    payload = "9370"..serial_number
    res, err = send(payload,{ignore_response = false, append_crc = true})
    if err then return end

    -- Show tag info
    print(('\nFound LTO-CM serial number: [%s]\n'):format(serial_number))

    -- Dumping data
    print('blk | data             ')
    print('----+------------------')
    local block_data = {}
    for block = startblock, endblock do

        payload = string.format('30%02x', block)
        res, err = send(payload , {ignore_response = false, append_crc = true})
        if err then return end

        local d0_d15 = getdata(res)

        payload = "80"
        res, err = send(payload, {ignore_response = false, append_crc = false})
        if err then return end

        local d16_d31 = getdata(res)

        -- remove crc bytes
        d0_d15 = string.sub(d0_d15, 0, #d0_d15 - 4)
        d16_d31 = string.sub(d16_d31, 0, #d16_d31 - 4)

        print(block, d0_d15..d16_d31)
        table.insert(block_data, d0_d15..d16_d31)
    end
    print("----+------------------")
    lib14a.disconnect()

    local filename, err = utils.WriteDumpFile(serial_number, block_data)
    if err then return oops(err) end

    print(string.format('\nDumped data into %s', filename))
end

main(args)
