]> git.zerfleddert.de Git - proxmark3-svn/blobdiff - client/scripts/legic.lua
ADD: `analyse nuid` - generates NUID 4byte from a UID 7byte. Mifare Classic Ev1...
[proxmark3-svn] / client / scripts / legic.lua
index dd119b03c289494456152f8a7b4e816357871c5b..73ef47917b893861c4da9ed483d3ad67708932b8 100644 (file)
@@ -1,4 +1,8 @@
 --[[   
+if it don't works with you tag-layout - be so kind and let me know ;-)
+
+Tested on Tags with those Layouts:
+
 (example)      Legic-Prime Layout with 'Kaba Group Header'
       +----+----+----+----+----+----+----+----+
   0x00|MCD |MSN0|MSN1|MSN2|MCC | 60 | ea | 9f |                
       +----+----+----+----+----+----+----+----+
   0x20|UID1|UID2|kghC|
       +----+----+----+
+MCD   = Manufacturer ID
+MSN   = Manufacturer SerialNumber
+60 ea = DCF Low + DCF high
+9f    = raw byte which holds the bits for OLE,WRP,WRC,RD
+ff    = unknown but important
+00    = unimportant
+11    = unknown but important
+Bck   = header-backup-area
+00 00 = Year (00 = 2000) & Week (not important)
+Seg   = Segment Header
+SegC  =  Crc8 over the Segment Header
+Stp   = Stamp (could be more as 4 - up to 7)
+UID   = dec User-ID for online-Mapping
+kghC  = crc8 over MCD + MSN0..MSN2 + UID
+
+
+(example)      Legic-Cash on MIM256/1024 tag' (37 bytes)
+      +----+----+----+----+----+----+----+----+
+  0x00|Seg0|Seg1|Seg2|Seg3|SegC|STP0|STP1|STP2|                
+      +----+----+----+----+----+----+----+----+
+  0x08|STP3|STP4|STP5|STP6| 01 |CURh|CURl|LIMh|
+      +----+----+----+----+----+----+----+----+
+  0x10|LIMm|LIMl|CHKh|CHKl|BALh|BALm|BALl|LRBh|
+      +----+----+----+----+----+----+----+----+
+  0x18|LRBm|LRBl|CHKh|CHKl|SHDh|SHDm|SHDl|LRSh|
+      +----+----+----+----+----+----+----+----+
+  0x20|LRSm|LRSl| CV |CHKh|CHKl|
+      +----+----+----+----+----+
+STP = Stamp (seems to be always 7 bytes)
+01  = unknown but important
+CUR = currency in HEX (ISO 4217)
+LIM = Cash-Limit
+CHK = crc16 over byte-addr 0x05..0x12
+BAL = Balance
+LRB = ID of the reader that changed the balance
+CHK = crc16 over BAL + LRB
+SHD = shadow Balance
+LRS = ID of the reader that changed the shadow balance (?? should be always the same as LRB)
+CV  = Counter value for transactions
+CHK = crc16 over SHD + LRS + CV
+
+(example)      Legic-Prime Layout 'gantner unsegmented user-credential'
+      +----+----+----+----+----+----+----+----+
+  0x00|MCD |MSN0|MSN1|MSN2|MCC | 60 | ea | 08 |                
+      +----+----+----+----+----+----+----+----+
+  0x08|Stp0|Stp1|Stp2|Stp3|Stp4|Dat0|Dat1|uCRC| <- addr 0x08..0x0f is WRP
+      +----+----+----+----+----+----+----+----+
+  0x10|emb0| <- this is only within wrp if addr 0x07==09
+      +----+
+MCD   = Manufacturer ID
+MSN   = Manufacturer SerialNumber
+60 ea = DCF Low + DCF high
+08    = raw byte which holds the bits for OLE,WRP,WRC,RD
+Stp   = Stamp (could be more as 4 - up to 7)
+Dat   = Online-Mapping Data
+uCRC  = crc8 over addr 0x00..0x03+0x07..0x0E
+
+
+(example)      Legic-Prime Layout 'gantner unsegmented Master-Token (IAM) with a stamp_len of 4'
+      +----+----+----+----+----+----+----+----+
+  0x00|MCD |MSN0|MSN1|MSN2|MCC | 20 | f8 | 08 |                
+      +----+----+----+----+----+----+----+----+
+  0x08|Stp0|Stp1|Stp2|Stp3| 00 | 00 | 00 |CRC1|
+      +----+----+----+----+----+----+----+----+
+  0x10| 00 | 00 | 00 | 00 | 00 |CRC2| 
+      +----+----+----+----+----+----+
+MCD   = Manufacturer ID
+MSN   = Manufacturer SerialNumber
+60 ea = DCF Low + DCF high
+08    = raw byte which holds the bits for OLE,WRP,WRC,RD
+Stp   = Stamp (could be more as 4 - up to 7)
+Dat   = Online-Mapping Data
+CRC1  = crc8 over addr 0x00..0x03+0x07..0x0E (special 'gantner crc8')
+CRC2  = MCD + MSB0..2+ addr 0x06 + addr 0x05 + addr 0x07 + Stamp (regular Master-Token-CRC)
 --]]
 
 example = "script run legic"
 author  = "Mosci"
+version = "1.0.3"
+
 desc =
 [[
 
 This script helps you to read, create and modify Legic Prime Tags (MIM22, MIM256, MIM1024)
 it's kinda interactive with following commands in three categories:
 
-    Data I/O           Segment Manipulation        File I/O   
-------------------     --------------------      ---------------
-  rt => read    Tag     ds => dump   Segments     lf => load File
-  wt => write   Tag     as => add    Segment      sf => save File 
-  ct => copy io Tag     es => edit   Segment      xf => xor  File
-  tc => copy oi Tag     ed => edit   Data     
-  di => dump  inTag     rs => remove Segment
-  do => dump outTag     cc => check  Segment-CRC                  
-                        ck => check  KGH                
-                        tk => toggle KGH-Flag
-  q => quit             xc => get    KGH-Str      h => this Help 
+    Data I/O                    Segment Manipulation                   Token-Data
+  -----------------             --------------------                -----------------
+  rt => read    Tag             as => add    Segment                mt => make Token
+  wt => write   Tag             es => edit   Segment Header         et => edit Token data
+                                ed => edit   Segment Data           tk => toggle KGH-Flag
+      File I/O                  rs => remove Segment           
+  -----------------             cc => check  Segment-CRC         
+  lf => load   File             ck => check  KGH                  
+  sf => save   File             ds => dump   Segments            
+  xf => xor to File                                              
+                                                                
+ (partially) known Segments              Virtual Tags                     Script Output
+ ---------------------------    -------------------------------     ------------------------
+ dlc => dump  Legic-Cash        ct => copy  mainTag to backupTag    tac => toggle ansicolors
+ elc => edit  Legic-Cash        tc => copy  backupTag to mainTag                                     
+ d3p => dump  3rd-Party-Cash    tt => switch mainTag & backupTag                                    
+ e3p => edit  3rd-Party-Cash    di => dump  mainTag 
+                                do => dump  backupTag
+                                  
+
  
- Data I/O 
  rt: 'read tag'         - reads a tag placed near to the PM3
  wt: 'write tag'        - writes the content of the 'virtual inTag' to a tag placed near to th PM3
                           without the need of changing anything - MCD,MSN,MCC will be read from the tag
                           before and applied to the output.
+ lf: 'load file'        - load a (xored) file from the local Filesystem into the 'virtual inTag'
+ sf: 'save file'        - saves the 'virtual inTag' to the local Filesystem (xored with Tag-MCC)
+ xf: 'xor file'         - saves the 'virtual inTag' to the local Filesystem (xored with choosen MCC - use '00' for plain values)
  ct: 'copy tag'         - copy the 'virtual Tag' to a second 'virtual TAG' - not usefull yet, but inernally needed
  tc: 'copy tag'         - copy the 'second virtual Tag' to 'virtual TAG' - not usefull yet, but inernally needed
- di: 'dump inTag'       - shows the current content of the 'virtual Tag'
- do: 'dump outTag'      - shows the current content of the 'virtual outTag'
-
- Segment Manipulation 
- (all manipulations happens only in the 'virtual inTAG' - they need to be written with 'wt' to take effect)
+ tt: 'toggle tag'       - copy mainTag to BackupTag and backupTag to mainTag
+ di: 'dump mainTag'     - shows the current content of the 'virtual Tag'
+ do: 'dump backupTag'   - shows the current content of the 'virtual outTag'
  ds: 'dump Segments'    - will show the content of a selected Segment
  as: 'add Segment'      - will add a 'empty' Segment to the inTag
  es: 'edit Segment'     - edit the Segment-Header of a selected Segment (len, WRP, WRC, RD, valid)
                           all other Segment-Header-Values are either calculated or not needed to edit (yet)
- ed: 'edit data'        - edit the Data of a Segment (Stamp & Payload)
+ ed: 'edit data'        - edit the Data of a Segment (ADF-Aera / Stamp & Payload specific Data)
+ et: 'edit Token'       - edit Data of a Token (CDF-Area / SAM, SAM64, SAM63, IAM, GAM specific Data)
+ mt: 'make Token'       - create a Token 'from scratch' (guided)
  rs: 'remove segment'   - removes a Segment (except Segment 00, but this can be set to valid=0 for Master-Token)
  cc: 'check Segment-CRC'- checks & calculates (if check failed) the Segment-CRC of all Segments
  ck: 'check KGH-CRC'    - checks the and calculates a 'Kaba Group Header' if one was detected
                           'Kaba Group Header CRC calculation'
  tk: 'toggle KGH'       - toglle the (script-internal) flag for kgh-calculation for a segment
  xc: 'etra c'           - show string that was used to calculate the kgh-crc of a segment
-  
- Input/Output
- lf: 'load file'        - load a (xored) file from the local Filesystem into the 'virtual inTag'
- sf: 'save file'        - saves the 'virtual inTag' to the local Filesystem (xored with Tag-MCC)
- xf: 'xor file'         - saves the 'virtual inTag' to the local Filesystem (xored with choosen MCC - use '00' for plain values)
  
+dlc: 'dump Legic-Cash'  - show balance and checksums of a Legic-Cash Segment
+elc: 'edit Legic-Cash'  - edit values of a Legic-Cash Segment
+
+d3p: 'dump 3rd Party'   - show balance, history and checksums of a (yet) unknown 3rd-Party Cash Segment
+e3p: 'edit 3rd Party'   - edit Data in 3rd-Party Cash Segment
+tac: 'toggle ansicolors'- switch on and off the colored text-output of this script
+                          default can be changed by setting the variable 'colored_output' to false
 ]]
+currentTag="inTAG"
 
---- requirements
-local utils   = require('utils')
-local getopt  = require('getopt')
+--- 
+-- requirements
+local utils       = require('utils')
+local getopt      = require('getopt')
+local ansicolors  = require('ansicolors')
 
---- global variables / defines
+--- 
+-- global variables / defines
 local bxor    = bit32.bxor
 local bbit    = bit32.extract
 local input   = utils.input
 local confirm = utils.confirm
 
---- Error-Handling & Helper
+---
+-- init ansicolor-values & ansicolors switch
+local colored_output = true
+local acoff  = ""
+local acgreen= ""
+local accyan = ""
+local acred  = ""
+local acyellow = ""
+local acblue = ""
+local acmagenta = ""
+
+--- Helper ---
+---
+-- default colors (change to whatever you want)
+function load_colors(onoff)
+  if (onoff) then
+    -- colors
+    acgreen = ansicolors.green
+    accyan  = ansicolors.cyan
+    acred   = ansicolors.red
+    acyellow= ansicolors.yellow
+    acblue  = ansicolors.blue
+    acmagenta= ansicolors.magenta
+    acoff   = ansicolors.reset
+  else
+    -- 'no color' 
+    acgreen = ""
+    accyan  = ""
+    acred   = ""
+    acyellow= ""
+    acblue  = ""
+    acmagenta= ""
+    acoff   = ""
+  end
+end
+
+---
+-- curency-codes for Legic-Cash-Segments (ISO 4217)
+local currency = {
+  ["03d2"]="EUR", 
+  ["0348"]="USD", 
+  ["033A"]="GBP",
+  ["02F4"]="CHF"
+}
+
+--- 
 -- This is only meant to be used when errors occur
 function oops(err)
-       print("ERROR: ",err)
+       print(acred.."ERROR: "..acoff ,err)
        return nil, err
 end
 
@@ -85,8 +232,8 @@ end
 -- Usage help
 function help()
        print(desc)
-       print("Example usage")
-       print(example)
+       print("Version: "..version)
+       print("Example usage: "..example)
 end
 
 --- 
@@ -96,17 +243,27 @@ function istable(t)
 end
 
 ---
--- put certain bytes into a new table
-function bytesToTable(bytes, bstart, bend)
-  local t={}
-  for i=0, (bend-bstart) do
-    t[i]=bytes[bstart+i]
-  end
-  return t
+-- creates a 'deep copy' of a table (a=b only references)
+function deepCopy(object)
+    local lookup_table = {}
+    local function _copy(object)
+        if type(object) ~= "table" then
+            return object
+        elseif lookup_table[object] then
+            return lookup_table[object]
+        end
+        local new_table = {}
+        lookup_table[object] = new_table
+        for index, value in pairs(object) do
+            new_table[_copy(index)] = _copy(value)
+        end
+        return setmetatable(new_table, getmetatable(object))
+    end
+    return _copy(object)
 end
 
 --- 
--- xor byte (if addr >= 0x22 - start counting from 1 => 23)
+-- xor single byte
 function xorme(hex, xor, index)
        if ( index >= 23 ) then
                return ('%02x'):format(bxor( tonumber(hex,16) , tonumber(xor,16) ))
@@ -144,80 +301,37 @@ function file_check(file_name)
 end
 
 ---
--- read file into virtual-tag
-function readFile(filename)
-  local bytes = {}
-  local tag = {}
-       if (file_check(filename)==false) then
-               return oops("input file: "..filename.." not found")
-       else
-               bytes = getInputBytes(filename)
-               if (bytes == false) then return oops('couldnt get input bytes') 
-    else
-      -- make plain bytes
-      bytes = xorBytes(bytes,bytes[5])
-      -- create Tag for plain bytes
-      tag=createTagTable()
-      -- load plain bytes to tag-table
-      tag=bytesToTag(bytes, tag)
+-- split csv-string into table
+local function split(str, sep)
+    local sep = sep or ','
+    local fields={}
+    local matchfunc = string.gmatch(str, "([^"..sep.."]+)")
+    if not matchfunc then return {str} end
+    for str in matchfunc do
+        table.insert(fields, str)
     end
-       end
-  return tag
+    return fields
 end
 
 ---
--- write bytes to file
--- write to file
-function writeFile(bytes, filename)
-  if (filename~='MylegicClone.hex') then
-    if (file_check(filename)) then
-           local answer = confirm("\nthe output-file "..filename.." alredy exists!\nthis will delete the previous content!\ncontinue?")
-      if (answer==false) then return print("user abort") end
-    end
+-- put a string into a bytes-table
+function str2bytes(s)
+  local res={}
+  if (string.len(s)%2~=0) then return print("stamp should be a even hexstring e.g.: deadbeef or 0badc0de") end
+  for i=1, string.len(s), 2 do
+    table.insert(res, string.sub(s,i,(i+1)))
   end
-  local line
-       local bcnt=0
-       local fho,err = io.open(filename, "w")
-       if err then oops("OOps ... faild to open output-file ".. filename) end
-  bytes=xorBytes(bytes, bytes[5])
-       for i = 1, #bytes do
-               if (bcnt == 0) then 
-                       line=bytes[i]
-               elseif (bcnt <= 7) then 
-                       line=line.." "..bytes[i]
-               end
-               if (bcnt == 7) then
-                       -- write line to new file
-                       fho:write(line.."\n")
-                       -- reset counter & line
-                       bcnt=-1
-                       line=""
-               end
-               bcnt=bcnt+1
-       end
-       fho:close()
-       print("\nwrote ".. #bytes .." bytes to " .. filename)
-       return true
+  return res
 end
 
---- 
--- read from pm3 into virtual-tag
-function readFromPM3()
-  local tag, bytes, infile 
-  --if (confirm("is the Tag placed onto the Proxmak3 and ready for reading?")) then
-    --print("reading Tag ...")
-    --infile=input("input a name for the temp-file:", "legic.temp")
-               --if (file_check(infile)) then
-               --      local answer = confirm("\nthe output-file "..infile.." alredy exists!\nthis will delete the previous content!\ncontinue?")
-               --      if (answer==false) then return print("user abort") end
-               --end
-    infile="legic.temp"
-    core.console("hf legic reader")
-    core.console("hf legic save "..infile)
-    --print("read temp-file into virtual Tag ...")
-    tag=readFile(infile)
-    return tag
-  --else return print("user abort"); end
+---
+-- put certain bytes into a new table
+function bytesToTable(bytes, bstart, bend)
+  local t={}
+  for i=0, (bend-bstart) do
+    t[i]=bytes[bstart+i]
+  end
+  return t
 end
 
 --- 
@@ -235,61 +349,12 @@ function getInputBytes(infile)
                end
        end
        fhi:close()
+  if (bytes[7]=='00') then return false end
   print(#bytes .. " bytes from "..infile.." loaded")
        return bytes
 end
 
 ---
--- read Tag-Table in bytes-table
-function tagToBytes(tag)
-  if (istable(tag)) then
-    local bytes = {}
-    local i, i2
-    -- main token-data
-    table.insert(bytes, tag.MCD)
-    table.insert(bytes, tag.MSN0)
-    table.insert(bytes, tag.MSN1)
-    table.insert(bytes, tag.MSN2)
-    table.insert(bytes, tag.MCC)
-    table.insert(bytes, tag.DCFl)
-    table.insert(bytes, tag.DCFh)
-    table.insert(bytes, tag.raw)
-    table.insert(bytes, tag.SSC)
-    -- raw token data
-    for i=0, #tag.data do
-      table.insert(bytes, tag.data[i])
-    end
-    -- backup data
-    for i=0, #tag.Bck do
-      table.insert(bytes, tag.Bck[i])
-    end
-    -- token-create-time / master-token crc
-    for i=0, #tag.MTC do
-      table.insert(bytes, tag.MTC[i])
-    end
-    -- process segments
-    if (type(tag.SEG[0])=='table') then
-      for i=0, #tag.SEG do
-        for i2=1, #tag.SEG[i].raw+1 do
-          table.insert(bytes, #bytes+1, tag.SEG[i].raw[i2])
-        end
-        table.insert(bytes, #bytes+1, tag.SEG[i].crc)
-        for i2=0, #tag.SEG[i].data-1 do
-          table.insert(bytes, #bytes+1, tag.SEG[i].data[i2])
-        end
-      end
-    end
-    -- fill with zeros
-    for i=#bytes+1, 1024 do
-      table.insert(bytes, i, '00')
-    end
-    print(#bytes.." bytes of Tag dumped")
-    return bytes
-  end
-  return oops("tag is no table in tagToBytes ("..type(tag)..")")
-end
-
---- virtual TAG functions
 -- create tag-table helper
 function createTagTable()
   local t={  
@@ -334,104 +399,878 @@ function bytesToTag(bytes, tag)
     tag.WRP=("%d"):format(bbit("0x"..bytes[8],0,4))
     tag.WRC=("%d"):format(bbit("0x"..bytes[8],4,3))
     tag.RD=("%d"):format(bbit("0x"..bytes[8],7,1))
+    if (tag.Type=="SAM" and tag.raw=='9f') then
     tag.Stamp_len=(tonumber(0xfc,10)-tonumber(bbit("0x"..tag.DCFh,0,8),10))
+    elseif (tag.Type=="SAM" and (tag.raw=='08' or tag.raw=='09')) then
+      tag.Stamp_len = tonumber(tag.raw,10)
+    end
     tag.data=bytesToTable(bytes, 10, 13)
     tag.Bck=bytesToTable(bytes, 14, 20)
     tag.MTC=bytesToTable(bytes, 21, 22)
     
-    print("Tag-Type: ".. tag.Type)
+    print(acgreen.."Tag-Type: ".. tag.Type..acoff)
     if (tag.Type=="SAM" and #bytes>23) then
       tag=segmentsToTag(bytes, tag)
-      print((#tag.SEG+1).." Segment(s) found")
+      print(acgreen..(#tag.SEG+1).." Segment(s) found"..acoff)
+    -- unsegmented Master-Token
+    -- only tag-data 
+    else 
+      for i=0, #tag.Bck do
+        table.insert(tag.data, tag.Bck[i])
+      end
+      tag.data[#tag.data]=tag.MTC[0]
+      tag.Bck=nil
+      --tag.MTC[0]=tag.MTC[1]
+      --tag.MTC[1]=nil
     end
-    print(#bytes.." bytes for Tag processed")
+    print(accyan..#bytes.." bytes for Tag processed"..acoff)
     return tag
   end
   return oops("tag is no table in: bytesToTag ("..type(tag)..")")
 end
 
+--- 
+-- put segments from byte-table to tag-table 
+function segmentsToTag(bytes, tag)
+  if(#bytes>23) then
+    local start=23
+    local i=-1
+    if (istable(tag)) then
+      repeat
+        i=i+1
+        tag.SEG[i]=getSegmentData(bytes, start, ("%02d"):format(i))
+        if (tag.Type=="SAM") then
+          if (checkKghCrc(tag, i)) then tag.SEG[i].kgh=true end
+        end
+        start=start+tag.SEG[i].len
+      until ((tag.SEG[i].valid==0) or tag.SEG[i].last==1 or i==126)
+      return tag
+    else return oops("tag is no table in: segmentsToTag ("..type(tag)..")") end
+  else print("no Segments: must be a MIM22") end
+end 
+
 ---
--- dump tag-system area
-function dumpCDF(tag)
-  local res=""
-  local i=0
-  local raw=""
+-- read Tag-Table in bytes-table
+function tagToBytes(tag)
   if (istable(tag)) then
-    res = res.."MCD: "..tag.MCD..", MSN: "..tag.MSN0.." "..tag.MSN1.." "..tag.MSN2..", MCC: "..tag.MCC.."\n"
-    res = res.."DCF: "..tag.DCFl.." "..tag.DCFh..", Token_Type="..tag.Type.." (OLE="..tag.OLE.."), Stamp_len="..tag.Stamp_len.."\n"
-    res = res.."WRP="..tag.WRP..", WRC="..tag.WRC..", RD="..tag.RD..", raw="..tag.raw..", SSC="..tag.SSC.."\n"
-    
-    -- credential
-    if (tag.raw..tag.SSC=="9fff") then
-      res = res.."Remaining Header Area\n"
-      for i=0, (#tag.data) do
-        res = res..tag.data[i].." "
-      end
-      res = res.."\nBackup Area\n"
-      for i=0, (#tag.Bck) do
-        res = res..tag.Bck[i].." "
-      end
-      res = res.."\nTime Area\n"
-      for i=0, (#tag.MTC) do
-        res = res..tag.MTC[i].." "
-      end
-    
-    -- Master Token
-    else
-      res = res .."Master-Token Area\n"
-      for i=0, (#tag.data) do
-        res = res..tag.data[i].." "
-      end
-      for i=0, (#tag.Bck) do
-        res = res..tag.Bck[i].." "
-      end
-      for i=0, (#tag.MTC-1) do
-        res = res..tag.MTC[i].." "
-      end
-      res = res .. " MT-CRC: "..tag.MTC[1]
+    local bytes = {}
+    local i, i2
+    -- main token-data
+    table.insert(bytes, tag.MCD)
+    table.insert(bytes, tag.MSN0)
+    table.insert(bytes, tag.MSN1)
+    table.insert(bytes, tag.MSN2)
+    table.insert(bytes, tag.MCC)
+    table.insert(bytes, tag.DCFl)
+    table.insert(bytes, tag.DCFh)
+    table.insert(bytes, tag.raw)
+    table.insert(bytes, tag.SSC)
+    -- raw token data
+    for i=0, #tag.data do
+      table.insert(bytes, tag.data[i])
     end
-    return res
-  else print("no valid Tag in dumpCDF") end
-end
-
----
--- dump single segment
-function dumpSegment(tag, index)
-  local i=index
-  local i2
-  local dp=0 --data-position in table
-  local res="" --result
-  local raw="" --raw-header
-  -- segment
-  if ( (istable(tag.SEG[i])) and tag.Type=="SAM") then 
-    if (istable(tag.SEG[i].raw)) then
-      for k,v in pairs(tag.SEG[i].raw) do
-        raw=raw..v.." "
+    -- backup data
+    if(istable(tag.Bck)) then
+      for i=0, #tag.Bck do
+        table.insert(bytes, tag.Bck[i])
       end
     end
-    
-    -- segment header
-    res = res.."Segment "..("%02d"):format(tag.SEG[i].index)..": "
-    res = res .."raw header:"..string.sub(raw,0,-2)..", flag="..tag.SEG[i].flag..", (valid="..("%x"):format(tag.SEG[i].valid)..", last="..("%x"):format(tag.SEG[i].last).."), "
-    res = res .."len="..("%04d"):format(tag.SEG[i].len)..", WRP="..("%02x"):format(tag.SEG[i].WRP)..", WRC="..("%02x"):format(tag.SEG[i].WRC)..", "
+    -- token-create-time / master-token crc
+    for i=0, #tag.MTC do
+      table.insert(bytes, tag.MTC[i])
+    end
+    -- process segments
+    if (type(tag.SEG[0])=='table') then
+      for i=0, #tag.SEG do
+        for i2=1, #tag.SEG[i].raw+1 do
+          table.insert(bytes, #bytes+1, tag.SEG[i].raw[i2])
+        end
+        table.insert(bytes, #bytes+1, tag.SEG[i].crc)
+        for i2=0, #tag.SEG[i].data-1 do
+          table.insert(bytes, #bytes+1, tag.SEG[i].data[i2])
+        end
+      end
+    end
+    -- fill with zeros
+    for i=#bytes+1, 1024 do
+      table.insert(bytes, i, '00')
+    end
+    return bytes
+  end
+  return oops("tag is no table in tagToBytes ("..type(tag)..")")
+end
+
+--- PM3 I/O ---
+--- 
+-- read from pm3 into virtual-tag
+function readFromPM3()
+  local tag, bytes, infile 
+    infile="legic.temp"
+    core.console("hf legic reader")
+    core.console("hf legic save "..infile)
+    tag=readFile(infile)
+    return tag
+end
+
+---
+-- write virtual Tag to real Tag
+function writeToTag(tag)
+  local bytes
+  local filename='MylegicClone.hex'
+  local taglen=22
+       if(utils.confirm(acred.."\nplace the (empty) Tag onto the PM3\nand confirm writing to this Tag: "..acoff) == false) then
+    return
+  end
+  -- get used bytes / tag-len
+  if(istable(tag.SEG)) then 
+      if (istable(tag.Bck)) then
+      for i=0, #tag.SEG do
+        taglen=taglen+tag.SEG[i].len+5
+      end
+  end
+  local uid_old=tag.MCD..tag.MSN0..tag.MSN1..tag.MSN2
+  -- read new tag into memory so we can xor the new data with the new MCC
+  outTAG=readFromPM3()
+  outbytes=tagToBytes(outTAG)
+  -- copy 'inputbuffer' to 'outputbuffer'
+  tag.MCD  = outbytes[1]
+  tag.MSN0 = outbytes[2]
+  tag.MSN1 = outbytes[3]
+  tag.MSN2 = outbytes[4]
+  tag.MCC  = outbytes[5]
+  -- recheck all segments-crc/kghcrc (only on a credential)
+  if(istable(tag.Bck)) then 
+    checkAllSegCrc(tag)
+    checkAllKghCrc(tag)
+    local uid_new=tag.MCD..tag.MSN0..tag.MSN1..tag.MSN2
+    for i=0, #tag.SEG do
+      if (check43rdPartyCash1(uid_old, tag.SEG[i].data)) then
+        io.write(accyan.."\nfixing known checksums"..acoff.." ... ")
+        if (fix3rdPartyCash1(uid_new, tag.SEG[i].data)) then
+          io.write(acgreen.." done\n"..acoff)
+        else oops("\nsomething went wrong at the repair of the 3rd-party-cash-segment") end
+      end
+    end
+  end
+  bytes=tagToBytes(tag)
+  -- master-token-crc
+  if (tag.Type~="SAM") then bytes[22]=calcMtCrc(bytes) end
+  if (bytes) then 
+    print("write temp-file '"..filename.."'")
+    print(accyan)
+    writeFile(bytes, filename)      
+    --writeToTag(bytes, taglen, 'MylegicClone.hex')
+    print(acoff)
+  end
+ end
+       -- write data to file
+       if (taglen > 0) then
+               WriteBytes = utils.input(acyellow.."enter number of bytes to write?"..acoff, taglen)
+               -- load file into pm3-buffer
+    if (type(filename)~="string") then filename=input(acyellow.."filename to load to pm3-buffer?"..acoff,"legic.temp") end
+               cmd = 'hf legic load '..filename
+               core.console(cmd)
+               -- write pm3-buffer to Tag
+               for i=0, WriteBytes do
+                       if ( i<5 or i>6) then
+                               cmd = ('hf legic write 0x%02x 0x01'):format(i)
+                               core.console(cmd)
+        --print(cmd)
+                       elseif (i == 6) then
+                               -- write DCF in reverse order (requires 'mosci-patch')        
+                               cmd = 'hf legic write 0x05 0x02'
+        print(acgreen..cmd..acoff)
+                               core.console(cmd)
+        --print(cmd)
+                       else
+                               print(acgreen.."skip byte 0x05 - will be written next step"..acoff)
+                       end                             
+                       utils.Sleep(0.2)
+               end
+       end
+end
+
+--- File I/O ---
+---
+-- read file into virtual-tag
+function readFile(filename)
+  print(accyan)
+  local bytes = {}
+  local tag = {}
+       if (file_check(filename)==false) then
+               return oops("input file: "..filename.." not found")
+       else
+               bytes = getInputBytes(filename)
+               if (bytes == false) then return oops('couldnt get input bytes') 
+    else
+      -- make plain bytes
+      bytes = xorBytes(bytes,bytes[5])
+      print("create virtual tag from ".. #bytes .. " bytes")
+      -- create Tag for plain bytes
+      tag=createTagTable()
+      -- load plain bytes to tag-table
+      print(acoff)
+      tag=bytesToTag(bytes, tag)
+    end
+       end
+  return tag
+end
+
+--- 
+-- write bytes to file
+function writeFile(bytes, filename)
+  if (filename~='MylegicClone.hex') then
+    if (file_check(filename)) then
+           local answer = confirm("\nthe output-file "..filename.." alredy exists!\nthis will delete the previous content!\ncontinue?")
+      if (answer==false) then return print("user abort") end
+    end
+  end
+  local line
+       local bcnt=0
+       local fho,err = io.open(filename, "w")
+       if err then oops("OOps ... faild to open output-file ".. filename) end
+  bytes=xorBytes(bytes, bytes[5])
+       for i = 1, #bytes do
+               if (bcnt == 0) then 
+                       line=bytes[i]
+               elseif (bcnt <= 7) then 
+                       line=line.." "..bytes[i]
+               end
+               if (bcnt == 7) then
+                       -- write line to new file
+                       fho:write(line.."\n")
+                       -- reset counter & line
+                       bcnt=-1
+                       line=""
+               end
+               bcnt=bcnt+1
+       end
+       fho:close()
+       print("\nwrote ".. #bytes .." bytes to " .. filename)
+       return true
+end
+
+--- Map related ---
+--- 
+-- make tagMap
+function makeTagMap()
+  local tagMap={}
+  if (#tagMap==0) then 
+    tagMap['name']=input(accyan.."enter Name for this Map: "..acoff , "newTagMap") 
+    tagMap['mappings']={}
+    tagMap['crc8']={}
+    -- insert fixed Tag-CRC
+    table.insert(tagMap.crc8, {name='TAG-CRC', pos=5, seq={1, 4}})
+    tagMap['crc16']={}
+  end
+  print(accyan.."new tagMap created"..acoff)
+  return tagMap                  
+end 
+
+---
+-- save mapping to file
+function saveTagMap(map, filename)
+  if (string.len(filename)>0) then
+    if (file_check(filename)) then
+           local answer = confirm("\nthe output-file "..filename.." alredy exists!\nthis will delete the previous content!\ncontinue?")
+      if (answer==false) then return print("user abort") end
+    end
+  end
+  
+  local line
+       local fho,err = io.open(filename, "w")
+       if err then oops("OOps ... faild to open output-file ".. filename) end
+       
+  -- write line to new file
+  for k, v in pairs(map) do
+    if (istable(v)) then
+      for k2, v2 in pairs(v) do
+        if (k=='mappings') then
+          fho:write(k..","..k2..","..v2['name']..","..v2['start']..","..v2['end']..","..((v2['highlight']) and "1" or "0").."\n")
+        elseif (k=="crc8") then
+            local tmp=""
+            tmp=k..","..k2..","..v2['name']..","..v2['pos']..","
+            tmp=tmp..tbl2seqstr(v2['seq'])
+            fho:write(tmp.."\n")
+        end
+      end
+    else
+      fho:write(k..","..v.."\n")
+    end
+  end
+       fho:close()
+       return true
+end
+
+---
+-- toggle higligh
+function toggleHighlight(tbl)
+  if (tbl['highlight']) then tbl['highlight']=false
+  else tbl['highlight']=true end
+  return tbl
+end
+
+---
+-- return table od seqence-string
+function seqstr2tbl(seqstr)
+  local s=split(seqstr)
+  local res={}
+  if (#s>=1) then
+    for sk, sv in pairs(s) do
+      s2=split(sv, '-')
+      if(#s2==2) then
+        table.insert(res, s2[1])
+        table.insert(res, s2[2])
+      end
+    end
+  end
+  return res
+end
+
+---
+-- return sequence-string from table
+function tbl2seqstr(seqtbl)
+  local res=""
+  if (istable(seqtbl)) then
+    for sk, sv in pairs(seqtbl) do
+      res=res..sv..((sk%2==0) and "," or "-")
+    end
+    if (string.sub(res, string.len(res))==",") then
+      res=string.sub(res, 1, string.len(res)-1)
+    end
+  end
+  return res
+end
+
+---
+-- read map-file into map
+function loadTagMap(filename)
+  local map={mappings={}, crc8={}, crc16={}}
+  local m=0
+  local c=0
+  local line, fields
+  local temp={}
+  local offset=0
+       if (file_check(filename)==false) then
+               return oops("input file: "..filename.." not found")
+       else
+               local fhi,err = io.open(filename)
+       while true do
+               line = fhi:read()
+               if line == nil then 
+        break 
+      else
+        fields=split(line)
+      end
+               if (#fields==2) then 
+        if (fields[1]=='offset') then
+          offset=tonumber(fields[2],10)
+        end
+        -- map-name
+        map[fields[1]]=fields[2] 
+      elseif (fields[1]=='mappings') then
+        m=m+1
+        temp={}
+        -- mapping
+        temp['name']=fields[3]
+        temp['start']=tonumber(fields[4], 10)
+        temp['end']=tonumber(fields[5], 10)
+        if(temp['start']>22) then 
+          temp['start']=temp['start']+offset 
+          temp['end']=temp['end']+offset 
+        end
+        if (tonumber(fields[6], 10)==1) then temp['highlight']= true
+        else temp['highlight']= false end
+        table.insert(map['mappings'], m, temp)
+      elseif (fields[1]=='crc8') then
+        c=c+1
+        temp={}
+        -- crc8
+        temp['name']=fields[3]
+        temp['pos']=tonumber(fields[4], 10)+offset
+        local s=string.sub(line, string.len(fields[1]..","..fields[2]..","..fields[3]..",")+1, string.len(line))
+        temp['seq']=seqstr2tbl(s)
+        for k, v in pairs(temp['seq']) do
+          if(tonumber(v, 10)>22) then v=tonumber(v, 10)+offset end
+          temp['seq'][k]=tonumber(v, 10) 
+        end  
+        table.insert(map.crc8, temp)
+      end
+       end
+       fhi:close()
+       end
+  return map
+end
+
+---
+-- dump tagMap (mappings only)
+function dumpTagMap(tag, tagMap)
+  if(#tagMap.mappings>0) then
+    bytes=tagToBytes(tag)
+    local temp
+    local lastend=0
+    -- start display mappings
+    for k, v in pairs(tagMap.mappings) do
+      if ((lastend+1)<v['start']) then
+        print("...")
+      end
+      if (isPosCrc8(tagMap, v['start'])>0) then
+        if ( checkMapCrc8(tagMap, bytes, isPosCrc8(tagMap, v['start']) ) ) then
+          io.write("("..("%04d"):format(v['start']).."-"..("%04d"):format(v['end'])..") "..acgreen..v['name']..acoff..":")
+        else
+          io.write("("..("%04d"):format(v['start']).."-"..("%04d"):format(v['end'])..") "..acred..v['name']..acoff..":")
+        end
+      else
+        io.write("("..("%04d"):format(v['start']).."-"..("%04d"):format(v['end'])..") "..((v['highlight']) and acmagenta or acyellow)..v['name']..acoff..":")
+      end
+      temp=""
+      for i=((string.len(v['name']))/10), 2 do
+        temp=temp.."\t"
+      end
+      for i=v['start'], v['end'] do
+        temp=temp..bytes[i].." "
+      end
+      print(temp)
+      lastend=v['end']
+    end
+  end
+end
+
+---
+--
+function isPosCrc8(tagMap, pos)
+  local res=0
+  if (#tagMap.crc8>0) then
+    for k, v in pairs(tagMap.crc8) do
+      if(v['pos']==pos) then res=k end
+    end
+  end
+  return res
+end
+
+---
+-- check mapped crc
+function checkMapCrc8(tagMap, bytes, n)
+  local res=false
+  if (#tagMap.crc8>0) then
+    if(istable(tagMap.crc8[n])) then
+      temp=""     
+      for k2, v2 in pairs(tagMap.crc8[n]) do
+        if (istable(v2)) then
+            temp=temp..tbl2seqstr(v2)
+        end
+      end     
+      local tempres=""
+      local tempres=getSequences(bytes, temp)
+      tempres=("%02x"):format(utils.Crc8Legic(tempres))
+      if (bytes[tagMap.crc8[n]['pos']]==tempres) then
+        res=true
+      end    
+    end
+  end
+  return res
+end
+
+---
+-- edit existing Map
+function editTagMap(tag, tagMap)
+  local t = [[
+    Data:  dm = show         dr = dump raw      
+Mappings:  im = insert       am = add       rm = remove
+    CRC8: ac8 = add         sc8 = show     rc8 = remove                    
+        :   q = exit          h = Help
+  ]]
+  --if(#tagMap.mappings==0) then oops("no mappings in tagMap"); return tagMap end
+  print("tagMap edit-mode submenu")
+  repeat 
+    x=input('tagMap submenu:', 'h')
+      if      (x=='h') then print(t)
+      elseif  (x=='dm') then tagMmap=dumpTagMap(tag, tagMap)
+      elseif  (x=='dr') then tagMmap=dumpMap(tag, tagMap)
+      elseif  (x=='rc8') then 
+        if (istable(tagMap.crc8)) then
+          local x1 = selectTableEntry(tagMap.crc8, "select number of CRC8 to remove:")
+          if (istable(tagMap.crc8[x1])) then
+            table.remove(tagMap.crc8, x1)
+          end
+        end
+      elseif  (x=='ac8') then
+        local p=tonumber(input("enter byte-addr of crc8", '0'),10)
+        if (p>0) then
+          local i1=input("enter comma-seperated byte-sequences (e.g.: '1-4,23-26')", '1-4,23-26')
+          local s1=split(i1, ',')
+          if (#s1>0) then
+            local temp={seq={}}
+            for k, v in pairs(s1) do
+              v1=split(v, '-')
+              if(#v1==2) then
+                table.insert(temp.seq, v1[1])
+                table.insert(temp.seq, v1[2])
+              end
+            end
+            temp['pos']=p
+            temp['name']=input("enter a name for the CRC8", "CRC "..(#tagMap.crc8+1))
+            table.insert(tagMap.crc8, temp)
+          end
+        end
+      elseif  (string.sub(x, 1, 3)=='sc8') then 
+        local bytes=tagToBytes(tag)
+        local res, pos
+        -- trigger manually by sc8 <'4digit' checkadd> <'seqeuence-string'>
+        -- e.g.: sc8 0027 1-4,23-36
+        if (string.len(x)>=9) then 
+          pos=tonumber(string.sub(x, 5, 8), 10)
+          x=string.sub(x, 9, string.len(x)) 
+          print("x: "..x.."  - pos:"..pos)
+        else
+          x=selectTableEntry(tagMap.crc8, "select CRC:")
+          if(istable(tagMap.crc8[x])) then
+            pos=tagMap.crc8[x]['pos']
+            x=tbl2seqstr(tagMap.crc8[x]['seq'])
+          end
+        end
+        if (type(x)=='string') then
+          res=("%02x"):format(utils.Crc8Legic(getSequences(bytes, x)))
+          print(accyan.."Sequence:\t"..acoff..x)
+          print(accyan.."Bytes:\t\t"..acoff..getSequences(bytes, x))
+          print(accyan.."calculated: "..acoff..res..accyan.." bytes["..pos.."]: "..acoff..bytes[pos].." ("..compareCrc(utils.Crc8Legic(getSequences(bytes, x)), bytes[pos])..")")
+        end
+      elseif (x=="tm") then
+          x=selectTableEntry(tagMap.mappings, "select number of Mapping:")
+          tagMap.mappings[x]=toggleHighlight(tagMap.mappings[x])
+      elseif  (x=='am') then tagMap=addMapping(tag, tagMap)
+      elseif  (x=='im') then tagMap=addMapping(tag, tagMap, selectTableEntry(tagMap.mappings, "select List-Position for insert:"))
+      elseif  (x=='rm') then tagMap=deleteMapping(tag, tagMap)
+      elseif  (x=='mas') then tagMap=mapTag(tagMap); tagMap=mapAllSegments(tag, tagMap)
+      elseif  (type(actions[string.sub(x, 3)])=='function') then actions[string.sub(x, 3)]()
+      end
+  until x=='q'
+  print("exit sub-Menu")
+  return tagMap
+end
+
+---
+-- dump raw mapped and unmapped
+function dumpMap(tag, tagMap)
+  local dstart=1
+  local dend, cnt
+  local bytes = tagToBytes(tag)
+  local stats = getSegmentStats(bytes)
+  dend=stats[#stats]['end']
+  print(accyan.."Tag uses "..dend.." bytes:"..acoff)
+  for i=dstart, dend do
+    if (check4MappedByte(i, tagMap) and not check4MapCrc8(i, tagMap) and not check4Highlight(i, tagMap)) then io.write(""..acyellow)
+    elseif (check4MapCrc8(i, tagMap)) then 
+      if ( checkMapCrc8(tagMap, bytes, isPosCrc8(tagMap, i) ) ) then
+        io.write(""..acgreen)
+      else 
+        io.write(""..acred)
+      end
+    else 
+      io.write(""..acoff) 
+    end
+    -- highlighted mapping
+    if (check4Highlight(i, tagMap)) then io.write(""..acmagenta) end
+    
+    io.write(bytes[i])
+    if (i%8==0) then io.write("\n") 
+      else io.write(" ") end
+  end
+  
+  io.write("\n"..acoff)
+end
+
+---
+-- show bytes used for crc-calculation
+function getSequences(bytes, seqstr)
+  if (type(seqstr)~="string") then seqstr=input("enter comma-seperated sequences (e.g.: '1-4,23-26')", '1-4,23-26') end
+  local seqs=split(seqstr, ',')
+  local res = ""
+  if(#seqs>0) then
+    for k, v in pairs(seqs) do
+      local seq = split(v,'-')
+      if (#seq>=2) then
+        for i=seq[1], seq[2] do
+          res=res..bytes[i].." "
+        end
+      end
+      if(string.len(res)>0) then res=res.."  " end
+    end
+  else
+    oops("no sequence found in '"..seqstr.."'")
+  end
+  return res
+end
+
+---
+-- check if byte-addr is a know crc
+function check4MapCrc8(addr, tagMap)
+  local res=false
+  for i=1, #tagMap.crc8 do
+    if (addr == tagMap.crc8[i]['pos']) then
+      res=true
+    end
+  end
+  return res
+end
+
+---
+-- check if byte-addr is a know crc
+function check4MapCrc16(addr, tagMap)
+  local res=false
+  for i=1, #tagMap.crc16 do
+    if (addr == tagMap.crc16[i]['pos']) then
+      res=true
+    end
+  end
+  return res
+end
+
+---
+-- check if byte is mapped or not
+function check4MappedByte(addr, tagMap)
+  local res=false
+  for _, v in pairs(tagMap.mappings) do
+    if (addr >= v['start'] and addr <= v['end'] ) then
+      res= true
+    end
+  end
+  return res
+end
+
+---
+-- check if byte is highlighted or not
+function check4Highlight(addr, tagMap)
+  local res=false
+  for _, v in pairs(tagMap.mappings) do
+    if (addr >= v['start'] and addr <= v['end'] ) then
+      res= v['highlight']
+    end
+  end
+  return res
+end
+
+---
+-- add interactive mapping
+function addMapping(tag, tagMap, x)
+  if (type(x)~="number") then x=#tagMap.mappings+1 end 
+  local bytes=tagToBytes(tag)
+  local myMapping={}
+  myMapping['name'] =input(accyan.."enter Maping-Name:"..acoff, string.format("mapping %d", #tagMap.mappings+1))
+  myMapping['start']=tonumber(input(accyan.."enter start-addr:"..acoff, '1'), 10)
+  myMapping['end']  =tonumber(input(accyan.."enter end-addr:"..acoff, #bytes), 10)
+  myMapping['highlight']=confirm("set highlighted")
+  table.insert(tagMap.mappings, x, myMapping)  
+  return tagMap
+end
+
+---
+-- delete mapping
+function deleteMapping(tag, tagMap)
+  if(#tagMap.mappings>0) then
+    local d = selectTableEntry(tagMap.mappings, "select number of Mapping to remove:")
+    if (type(d)=='number') then
+      table.remove(tagMap.mappings, d)
+    else oops("deleteMapping: got type = "..type(d).." - expected type = 'number'")
+    end
+  end
+  return tagMap 
+end
+
+---
+-- select a mapping from a tagmap
+function selectTableEntry(table, action)
+  if (type(action)~="string") then action="select number of item:" end
+  for k, v in pairs(table) do
+    print(accyan..k..acoff.."\t-> "..accyan..v['name']..acoff)
+  end
+  local res = tonumber(input(action , 0), 10)
+  if (istable(table[res])) then
+    return  res
+  else
+    return false
+  end
+end
+
+---
+-- map all segments
+function mapAllSegments(tag, tagMap)
+  local bytes=tagToBytes(tag)
+  local WRP,WRC,WRPC
+  segs=getSegmentStats(bytes)
+  if (istable(segs)) then
+    for k, v in pairs(segs) do
+         -- wrp (write proteted) = byte 2
+         WRP = tonumber(bytes[v['start']+2],16)
+         -- wrc (write control) - bit 4-6 of byte 3
+         WRC = tonumber(bbit("0x"..bytes[v['start']+3],4,3),16)
+      --tagMap=mapTokenData(tagMap, 'Segment '..("%02d"):format(v['index']).." HDR", v['start'], v['start']+3)
+      tagMap=mapTokenData(tagMap, 'Segment '..("%02d"):format(v['index']).." CRC", v['start']+4, v['start']+4, true)
+      table.insert(tagMap.crc8, {name = 'Segment '..("%02d"):format(v['index']).." CRC", pos=v['start']+4, seq={1,4,v['start'],v['start']+3}} )
+      if(WRC>WRP) then 
+        WRPC=WRC
+        tagMap=mapTokenData(tagMap, 'Segment '..("%02d"):format(v['index']).." WRC", v['start']+5, v['start']+5+WRC-1, true)
+      elseif (WRP>WRC and WRC>0) then
+        WRPC=WRP
+        tagMap=mapTokenData(tagMap, 'Segment '..("%02d"):format(v['index']).." WRC", v['start']+5, v['start']+5+WRC-1, true)
+        tagMap=mapTokenData(tagMap, 'Segment '..("%02d"):format(v['index']).." WRP", v['start']+WRC+5, v['start']+5+WRP-1, true)
+      else
+        WRPC=WRP
+        tagMap=mapTokenData(tagMap, 'Segment '..("%02d"):format(v['index']).." WRP", v['start']+5, v['start']+5+WRP-1, true)
+      end
+      tagMap=mapTokenData(tagMap, 'Segment '..("%02d"):format(v['index']).." data", v['start']+5+WRPC, v['end'], false)
+      
+    end
+    print(#segs.." Segments mapped")
+  else
+    oops("autoMapSegments failed: no Segments found")
+  end
+  return tagMap
+end
+
+--- 
+-- map all token data
+function mapTokenData(tagMap, mname, mstart, mend, mhigh)
+  --if ( not mhigh ) then mhigh=false end
+  local myMapping={}
+  myMapping['name'] =mname
+  myMapping['start']=mstart
+  myMapping['end']  =mend
+  myMapping['highlight']=mhigh
+  table.insert(tagMap.mappings, myMapping) 
+  return tagMap
+end
+
+---
+-- map a map
+function mapTag(tagMap)
+  tagMap=makeTagMap()
+  tagMap=mapTokenData(tagMap, 'Tag-ID', 1, 4, true)
+  tagMap=mapTokenData(tagMap, 'Tag-CRC', 5, 5, false)
+  tagMap=mapTokenData(tagMap, 'DCF', 6, 7, true)
+  tagMap=mapTokenData(tagMap, 'THDR-Raw/Stamp-Len', 8, 8, true)
+  tagMap=mapTokenData(tagMap, 'SSC', 9, 9, true)
+  tagMap=mapTokenData(tagMap, 'Header', 10, 13, false)
+  tagMap=mapTokenData(tagMap, 'Backup', 14, 19, true)
+  tagMap=mapTokenData(tagMap, 'Bck-CRC', 20, 20, false)
+  tagMap=mapTokenData(tagMap, 'TokenTime', 21, 22, false)
+  return tagMap
+end
+
+--- Dump Data ---
+---
+-- dump virtual Tag-Data
+function dumpTag(tag)
+  local i, i2
+  local res
+  local dp=0
+  local raw=""
+  -- sytstem area
+  res =acyellow.."\nCDF: System Area"..acoff
+  res= res.."\n"..dumpCDF(tag)
+  -- segments (user-token area)
+  if(tag.Type=="SAM" and tag.raw=='9f') then
+    res = res..acyellow.."\n\nADF: User Area"..acoff
+    for i=0, #tag.SEG do
+      res=res.."\n"..dumpSegment(tag, i).."\n"
+    end
+  end
+  return res
+end
+
+---
+-- dump tag-system area
+function dumpCDF(tag)
+  local res=""
+  local i=0
+  local raw=""
+  local bytes
+  if (istable(tag)) then
+    res = res..accyan.."MCD: "..acoff..tag.MCD..accyan.." MSN: "..acoff..tag.MSN0.." "..tag.MSN1.." "..tag.MSN2..accyan.." MCC: "..acoff..tag.MCC.."\n"
+    res = res.."DCF: "..tag.DCFl.." "..tag.DCFh..", Token_Type="..tag.Type.." (OLE="..tag.OLE.."), Stamp_len="..tag.Stamp_len.."\n"
+    res = res.."WRP="..tag.WRP..", WRC="..tag.WRC..", RD="..tag.RD..", raw="..tag.raw..((tag.raw=='9f') and (", SSC="..tag.SSC.."\n") or "\n")
+    
+    -- credential (end-user tag)
+    if (tag.Type=="SAM" and tag.raw=='9f') then
+      res = res.."Remaining Header Area\n"
+      for i=0, (#tag.data) do
+        res = res..tag.data[i].." "
+      end
+      res = res.."\nBackup Area\n"
+      for i=0, (#tag.Bck) do
+        res = res..tag.Bck[i].." "
+      end
+      res = res.."\nTime Area\n"
+      for i=0, (#tag.MTC) do
+        res = res..tag.MTC[i].." "
+      end
+    
+    
+    -- Master Token specific
+    elseif (tag.Type~="SAM") then
+      res = res .."Master-Token Area\nStamp: "
+      res= res..tag.SSC.." "
+      for i=0, tag.Stamp_len-2 do
+        res = res..tag.data[i].." "
+      end
+      res=res.."\nunused payload\n"
+      for i=0, (#tag.data-tag.Stamp_len-1) do
+        res = res..tag.data[i].." "
+      end
+      bytes=tagToBytes(tag)
+      local mtcrc=calcMtCrc(bytes)
+      res=res.."\nMaster-Token CRC: "
+      res = res ..tag.MTC[1].." ("..((tag.MTC[1]==mtcrc) and "valid" or "error")..")"
+      
+    
+    -- 'Gantner User-Credential' specific
+    elseif (tag.Type=="SAM" and (tag.raw=='08' or tag.raw=='09')) then
+      print(acgreen.."Gantner Detected"..acoff)
+    end
+    
+    return res
+  else print("no valid Tag in dumpCDF") end
+end
+
+---
+-- dump single segment
+function dumpSegment(tag, index)
+  local i=index
+  local i2
+  local dp=0 --data-position in table
+  local res="" --result
+  local raw="" --raw-header
+  -- segment
+  if ( (istable(tag.SEG[i])) and tag.Type=="SAM" and tag.raw=="9f") then 
+    if (istable(tag.SEG[i].raw)) then
+      for k,v in pairs(tag.SEG[i].raw) do
+        raw=raw..v.." "
+      end
+    end
+    
+    -- segment header
+    res = res..accyan.."Segment "..("%02d"):format(tag.SEG[i].index)..acoff..": "
+    res = res .."raw header: "..string.sub(raw,0,-2)..", flag="..tag.SEG[i].flag..", (valid="..("%x"):format(tag.SEG[i].valid)..", last="..("%x"):format(tag.SEG[i].last).."), "
+    res = res .."len="..("%04d"):format(tag.SEG[i].len)..", WRP="..("%02x"):format(tag.SEG[i].WRP)..", WRC="..("%02x"):format(tag.SEG[i].WRC)..", "
     res = res .."RD="..("%02x"):format(tag.SEG[i].RD)..", CRC="..tag.SEG[i].crc.." "
-    res = res .."("..(checkSegmentCrc(tag, i) and "valid" or "error")..")"
+    res = res .."("..(checkSegmentCrc(tag, i) and acgreen.."valid" or acred.."error") ..acoff..")"
     raw=""
+    
 
     -- WRC protected
-    if (tag.SEG[i].WRC>0) then
-      res = res .."\nWRC protected area (Stamp):\n"
-      for i2=dp, tag.SEG[i].WRC-1 do
-        res = res..tag.SEG[i].data[dp].." "
+    if ((tag.SEG[i].WRC>0)) then
+      res = res .."\nWRC protected area:\n"
+      for i2=dp, dp+tag.SEG[i].WRC-1 do
+        res = res..tag.SEG[i].data[i2].." "
         dp=dp+1
       end
     end
     
     -- WRP mprotected
     if (tag.SEG[i].WRP>tag.SEG[i].WRC) then
-      res = res .."\nRemaining write protected area (Stamp):\n"
-      for i2=dp, tag.SEG[i].WRP-tag.SEG[i].WRC-1 do
-        res = res..tag.SEG[i].data[dp].." "
+      res = res .."\nRemaining write protected area:\n"
+      for i2=dp, dp+(tag.SEG[i].WRP-tag.SEG[i].WRC)-1 do
+        res = res..tag.SEG[i].data[i2].." "
         dp=dp+1
       end
     end
@@ -444,7 +1283,7 @@ function dumpSegment(tag, index)
        dp=dp+1
      end
      if (tag.SEG[i].kgh) then 
-       res = res..tag.SEG[i].data[dp].." (KGH: "..(checkKghCrc(tag, i) and "valid" or "error")..")"
+       res = res..tag.SEG[i].data[dp].." (KGH: "..(checkKghCrc(tag, i) and acgreen.."valid" or acred.."error") ..acoff..")"
      else  res = res..tag.SEG[i].data[dp] end
     end
     dp=0
@@ -455,42 +1294,235 @@ function dumpSegment(tag, index)
 end
 
 ---
--- check all segmnet-crc
-function checkAllSegCrc(tag)
-  for i=0, #tag.SEG do
-    crc=calcSegmentCrc(tag, i)
-    tag.SEG[i].crc=crc
+-- return bytes 'sstrat' to 'send' from a table
+function dumpTable(tab, header, tstart, tend)
+  res=""
+  for i=tstart, tend do
+    res=res..tab[i].." "
   end
+  if (string.len(header)==0) then return res
+  else return (header.." #"..(tend-tstart+1).."\n"..res) end
 end
 
 ---
--- check all segmnet-crc
-function checkAllKghCrc(tag)
-  for i=0, #tag.SEG do
-    crc=calcKghCrc(tag, i)
-    if (tag.SEG[i].kgh) then 
-      tag.SEG[i].data[#tag.SEG[i].data-1]=crc
-    end
+-- dump 3rd Party Cash
+function dump3rdPartyCash1(tag , seg)
+  local uid=tag.MCD..tag.MSN0..tag.MSN1..tag.MSN2
+  local stamp=tag.SEG[seg].data[0].." "..tag.SEG[seg].data[1].." "..tag.SEG[seg].data[2]
+  local datastamp=tag.SEG[seg].data[20].." "..tag.SEG[seg].data[21].." "..tag.SEG[seg].data[22]
+  local balance=tonumber(tag.SEG[seg].data[32]..tag.SEG[seg].data[33] ,16)
+  local balancecrc=utils.Crc8Legic(uid..tag.SEG[seg].data[32]..tag.SEG[seg].data[33])
+  local mirror=tonumber(tag.SEG[seg].data[35]..tag.SEG[seg].data[36] ,16)
+  local mirrorcrc=utils.Crc8Legic(uid..tag.SEG[seg].data[35]..tag.SEG[seg].data[36])
+  local lastbal0=tonumber(tag.SEG[seg].data[39]..tag.SEG[seg].data[40] ,16)
+  local lastbal1=tonumber(tag.SEG[seg].data[41]..tag.SEG[seg].data[42] ,16)
+  local lastbal2=tonumber(tag.SEG[seg].data[43]..tag.SEG[seg].data[44] ,16)
+  
+  test=""
+  -- display decoded/known stuff
+  print("\n------------------------------")
+  print("Tag-ID:\t\t      "..uid)
+  print("Stamp:\t\t      "..stamp)   
+  print("UID-Mapping: \t\t"..("%06d"):format(tonumber(tag.SEG[seg].data[46]..tag.SEG[seg].data[47]..tag.SEG[seg].data[48], 16)))
+  print("------------------------------")           
+  print("checksum 1:\t\t    "..tag.SEG[seg].data[31].." ("..compareCrc(utils.Crc8Legic(uid..dumpTable(tag.SEG[seg].data, "", 19, 30)), tag.SEG[seg].data[31])..")")   
+  print("checksum 2:\t\t    "..tag.SEG[seg].data[34].." ("..compareCrc(utils.Crc8Legic(uid..dumpTable(tag.SEG[seg].data, "", 32, 33)), tag.SEG[seg].data[34])..")")
+  print("checksum 3:\t\t    "..tag.SEG[seg].data[37].." ("..compareCrc(utils.Crc8Legic(uid..dumpTable(tag.SEG[seg].data, "", 35, 36)), tag.SEG[seg].data[37])..")") 
+  
+  print("checksum 4:\t\t    "..tag.SEG[seg].data[55].." ("..compareCrc(utils.Crc8Legic(uid..dumpTable(tag.SEG[seg].data, "", 46, 54)), tag.SEG[seg].data[55])..")")
+  print("checksum 5:\t\t    "..tag.SEG[seg].data[62].." ("..compareCrc(utils.Crc8Legic(uid..dumpTable(tag.SEG[seg].data, "", 56, 61)), tag.SEG[seg].data[62])..")") 
+  print("checksum 6:\t\t    "..tag.SEG[seg].data[73].." ("..compareCrc(utils.Crc8Legic(uid..dumpTable(tag.SEG[seg].data, "", 63, 72)), tag.SEG[seg].data[73])..")")
+  print("checksum 7:\t\t    "..tag.SEG[seg].data[89].." ("..compareCrc(utils.Crc8Legic(uid..dumpTable(tag.SEG[seg].data, "", 74, 88)), tag.SEG[seg].data[89])..")")  
+  print("------------------------------")                    
+  print(string.format("Balance:\t\t  %3.2f", balance/100).." ".."("..compareCrc(balancecrc, tag.SEG[seg].data[34])..")")
+  print(string.format("Shadow:\t\t\t  %3.2f", mirror/100).." ".."("..compareCrc(balancecrc, tag.SEG[seg].data[37])..")")     
+  print("------------------------------")                  
+  print(string.format("History 1:\t\t  %3.2f", lastbal0/100))            
+  print(string.format("History 2:\t\t  %3.2f", lastbal1/100))          
+  print(string.format("History 3:\t\t  %3.2f", lastbal2/100))        
+  print("------------------------------\n") 
+end
+
+---
+-- dump Legic-Cash data
+function dumpLegicCash(tag, x)
+  if (istable(tag.SEG[x])) then
+     io.write("in Segment "..tag.SEG[x].index.." :\n")
+     print("--------------------------------\n\tLegic-Cash Values\n--------------------------------")
+     local limit, curr, balance, rid, tcv
+     -- currency of balance & limit
+     curr=currency[tag.SEG[x].data[8]..tag.SEG[x].data[9]]
+     -- maximum balance
+     limit=string.format("%4.2f", tonumber(tag.SEG[x].data[10]..tag.SEG[x].data[11]..tag.SEG[x].data[12], 16)/100)
+     -- current balance
+     balance=string.format("%4.2f", tonumber(tag.SEG[x].data[15]..tag.SEG[x].data[16]..tag.SEG[x].data[17], 16)/100)
+     -- reader-id who wrote last transaction
+     rid=tonumber(tag.SEG[x].data[18]..tag.SEG[x].data[19]..tag.SEG[x].data[20], 16)
+     -- transaction counter value
+     tcv=tonumber(tag.SEG[x].data[29], 16)
+     print("Currency:\t\t "..curr)
+     print("Limit:\t\t\t "..limit)
+     print("Balance:\t\t "..balance)
+     print("Transaction Counter:\t "..tcv)
+     print("Reader-ID:\t\t "..rid.."\n--------------------------------\n")
+   end
+end
+
+---
+--  raw 3rd-party
+function print3rdPartyCash1(tag, x)
+  if (istable(tag.SEG[x])) then
+    local uid=tag.MCD..tag.MSN0..tag.MSN1..tag.MSN2
+     print("\n\t\tStamp  :  "..dumpTable(tag.SEG[x].data, "", 0 , 2))
+     print("\t\tBlock 0:  "..dumpTable(tag.SEG[x].data, "", 3 , 18))
+     print()
+     print("\t\tBlock 1:  "..dumpTable(tag.SEG[x].data, "", 19, 30))
+     print("checksum 1: Tag-ID .. Block 1 => LegicCrc8 = "..tag.SEG[x].data[31].." ("..compareCrc(utils.Crc8Legic(uid..dumpTable(tag.SEG[x].data, "", 19, 30)), tag.SEG[x].data[31])..")")
+     print()
+     print("\t\tBlock 2:  "..dumpTable(tag.SEG[x].data, "", 32, 33))
+     print("checksum 2: Block 2 => LegicCrc8 = "..tag.SEG[x].data[34].." ("..compareCrc(utils.Crc8Legic(uid..dumpTable(tag.SEG[x].data, "", 32, 33)), tag.SEG[x].data[34])..")")
+     print()
+     print("\t\tBlock 3:  "..dumpTable(tag.SEG[x].data, "", 35, 36))
+     print("checksum 3: Block 3 => LegicCrc8 = "..tag.SEG[x].data[37].." ("..compareCrc(utils.Crc8Legic(uid..dumpTable(tag.SEG[x].data, "", 35, 36)), tag.SEG[x].data[37])..")")
+     print()
+     print("\t\tyet unknown: "..tag.SEG[x].data[38])
+     print()
+     print("\t\tHisatory 1:  "..dumpTable(tag.SEG[x].data, "", 39, 40))  
+     print("\t\tHisatory 2:  "..dumpTable(tag.SEG[x].data, "", 41, 42))  
+     print("\t\tHisatory 3:  "..dumpTable(tag.SEG[x].data, "", 43, 44))   
+     print()
+     print("\t\tyet unknown: "..tag.SEG[x].data[45])         
+     print()
+     print("\t\tKGH-UID HEX:  "..dumpTable(tag.SEG[x].data, "", 46, 48))
+     print("\t\tBlock 4:  "..dumpTable(tag.SEG[x].data, "", 49, 54))
+     print("checksum 4: Tag-ID .. KGH-UID .. Block 4 => LegicCrc8 = "..tag.SEG[x].data[55].." ("..compareCrc(utils.Crc8Legic(uid..dumpTable(tag.SEG[x].data, "", 46, 54)), tag.SEG[x].data[55])..")")
+     print()
+     print("\t\tBlock 5:  "..dumpTable(tag.SEG[x].data, "", 56, 61))
+     print("checksum 5: Tag-ID .. Block 5 => LegicCrc8 = "..tag.SEG[x].data[62].." ("..compareCrc(utils.Crc8Legic(uid..dumpTable(tag.SEG[x].data, "", 56, 61)), tag.SEG[x].data[62])..")")
+     print()
+     print("\t\tBlock 6:  "..dumpTable(tag.SEG[x].data, "", 63, 72))
+     print("checksum 6: Tag-ID .. Block 6 => LegicCrc8 = "..tag.SEG[x].data[73].." ("..compareCrc(utils.Crc8Legic(uid..dumpTable(tag.SEG[x].data, "", 63, 72)), tag.SEG[x].data[73])..")")
+     print()
+     print("\t\tBlock 7:  "..dumpTable(tag.SEG[x].data, "", 74, 88))
+     print("checksum 7: Tag-ID .. Block 7 => LegicCrc8 = "..tag.SEG[x].data[89].." ("..compareCrc(utils.Crc8Legic(uid..dumpTable(tag.SEG[x].data, "", 74, 88)), tag.SEG[x].data[89])..")")
+     print()
+     print("\t\tBlock 8:  "..dumpTable(tag.SEG[x].data, "", 90, 94))
   end
 end
 
+--- Token related --
 ---
--- dump virtual Tag-Data
-function dumpTag(tag)
-  local i, i2
-  local res
-  local dp=0
-  local raw=""
-  -- sytstem area
-  res ="\nCDF: System Area"
-  res= res.."\n"..dumpCDF(tag)
-  -- segments (user area)
-  if(istable(tag.SEG[0])) then
-    res = res.."\n\nADF: User Area"
-    for i=0, #tag.SEG do
-      res=res.."\n"..dumpSegment(tag, i).."\n"
+-- make token
+function makeToken()
+  local mt={
+    ['Type']    = {"SAM", "SAM63", "SAM64", "IAM", "GAM"},
+    ['DCF']     = {"60ea", "31fa", "30fa", "80fa", "f0fa"},
+    ['WRP']     = {"15", "2", "2", "2", "2"},
+    ['WRC']     = {"01", "02", "02", "00", "00"},
+    ['RD']      = {"01", "00", "00", "00", "00"},
+    ['Stamp']   = {"00", "00", "00", "00", "00"},
+    ['Segment'] = {"0d", "c0", "04", "00", "be", "01", "02", "03", "04", "01", "02", "03", "04"}
+  }
+  ttype=""
+  for k, v in pairs(mt.Type) do
+    ttype=ttype..k..") "..v.."  "
+  end
+  mtq=tonumber(input("select number for Token-Type\n"..ttype, '1'), 10)
+  if (type(mtq)~="number") then return print("selection invalid!") 
+  elseif (mtq>#mt.Type) then return print("selection invalid!")
+  else print("Token-Type '"..mt.Type[mtq].."' selected") end
+  local raw=calcHeaderRaw(mt.WRP[mtq], mt.WRC[mtq], mt.RD[mtq])
+  local mtCRC="00"
+  
+  bytes={"01", "02", "03", "04", "cb", string.sub(mt.DCF[mtq], 0, 2), string.sub(mt.DCF[mtq], 3), raw,
+         "00", "00", "00", "00", "00", "00", "00", "00",
+         "00", "00", "00", "00", "00", "00"}
+  if (mtq==1) then
+    for i=0, #mt.Segment do
+      table.insert(bytes, mt.Segment[i])
     end
+    bytes[9]="ff"
   end
+  -- fill bytes
+  for i=#bytes, 1023 do table.insert(bytes, "00") end
+  -- if Master-Token -> calc Master-Token-CRC
+  if (mtq>1) then bytes[22]=calcMtCrc(bytes) end
+  local tempTag=createTagTable()
+  -- remove segment if MasterToken
+  if (mtq>1) then tempTag.SEG[0]=nil end
+  return bytesToTag(bytes, tempTag)
+end
+
+--- 
+-- edit token-data
+function editTag(tag)
+  -- for simulation it makes sense to edit everything
+  local edit_sim="MCD MSN0 MSN1 MSN2 MCC DCFl DCFh WRP WRC RD"
+  -- on real tags it makes only sense to edit DCF, WRP, WRC, RD
+  local edit_real="DCFl DCFh WRP WRC RD"
+  if (confirm(acyellow.."do you want to edit non-writeable values (e.g. for simulation)?"..acoff)) then
+    edit_tag=edit_sim
+  else edit_tag=edit_real end
+    
+  if(istable(tag)) then
+    for k,v in pairs(tag) do
+      if(type(v)~="table" and type(v)~="boolean" and string.find(edit_tag, k)) then
+        tag[k]=input("value for: "..accyan..k..acoff, v)
+      end
+    end
+    
+    if (tag.Type=="SAM") then ttype="Header"; else ttype="Stamp"; end
+      if (confirm(acyellow.."do you want to edit "..ttype.." Data?"..acoff)) then
+      -- master-token specific
+      if(istable(tag.Bck)==false) then
+        -- stamp-data length=(0xfc-DCFh)
+        -- on MT: SSC holds the Starting Stamp Character (Stamp0)
+        tag.SSC=input(ttype.."0: ", tag.SSC)
+        -- rest of stamp-bytes are in tag.data 0..n
+        for i=0, (tonumber(0xfc ,10)-("%d"):format('0x'..tag.DCFh))-2 do
+        tag.data[i]=input(ttype.. i+1 ..": ", tag.data[i])
+        end  
+      else
+        --- on credentials byte7 should always be 9f and byte8 ff 
+        -- on Master-Token not (even on SAM63/64 not)
+        -- tag.SSC=input(ttype.."0: ", tag.SSC)
+        for i=0, #tag.data do
+           tag.data[i]=input(ttype.. i ..": ", tag.data[i])
+        end
+      end
+    end
+    
+    bytes=tagToBytes(tag)
+   
+    --- check data-consistency (calculate tag.raw)
+    bytes[8]=calcHeaderRaw(tag.WRP, tag.WRC, tag.RD)
+   
+    --- Master-Token specific
+    -- should be triggered if a SAM was converted to a non-SAM (user-Token to Master-Token)
+    -- or a Master-Token has being edited (also SAM64 & SAM63 - which are in fact Master-Token)
+    if(tag.Type~="SAM" or bytes[6]..bytes[7]~="60ea") then 
+      -- calc new Master-Token crc
+      bytes[22]=calcMtCrc(bytes)   
+    else
+      -- ensure tag.SSC set to 'ff' on credential-token (SAM)
+      bytes[9]='ff'
+      -- if a Master-Token was converted to a Credential-Token
+      -- lets unset the Time-Area to 00 00 (will contain Stamp-Data on MT)
+      bytes[21]='00'
+      bytes[22]='00'
+    end
+   
+    tag=bytesToTag(bytes, tag)
+  end
+end
+
+---
+-- calculates header-byte (addr 0x07)
+function calcHeaderRaw(wrp, wrc, rd)
+  local res
+  wrp=("%02x"):format(tonumber(wrp, 10))
+  rd=tonumber(rd, 16)
+  res=("%02x"):format(tonumber(wrp, 16)+tonumber(wrc.."0", 16)+((rd>0) and tonumber("8"..(rd-1), 16) or 0))         
   return res
 end
 
@@ -513,38 +1545,15 @@ function getTokenType(DCFl)
 end
 
 ---
--- regenerate segment-header (after edit)
-function regenSegmentHeader(segment)
-  local seg=segment
-  local raw = segment.raw
-  local i
-  -- len  bit0..7 | len=12bit=low nibble of byte1..byte0
-  raw[1]=("%02x"):format(bbit("0x"..("%03x"):format(seg.len),0,8))
-  -- high nibble of len  bit6=valid , bit7=last of byte 1 | ?what are bit 5+6 for? maybe kgh?
-  raw[2]=("%02x"):format(bbit("0x"..("%03x"):format(seg.len),4.4)..bbit("0x"..("%02x"):format((seg.valid*64)+(seg.last*128)),0,8))
-  -- WRP
-  raw[3]=("%02x"):format(bbit("0x"..("%02x"):format(seg.WRP),0,8))
-  -- WRC + RD
-  raw[4]=("%02x"):format(bbit("0x"..("%03x"):format(seg.WRC),4,3)..bbit("0x"..("%02x"):format(seg.RD*128),0,8))
-  -- flag
-  seg.flag=string.sub(raw[2],0,1)
-  --print(raw[1].." "..raw[2].." "..raw[3].." "..raw[4])
-  if(#seg.data>(seg.len-5)) then
-    print("current payload: ".. #seg.data .." - desired payload: ".. seg.len-5)
-    print("Data-Length has being reduced: removing ".. #seg.data-(seg.len-5) .." bytes from Payload");
-    for i=(seg.len-5), #seg.data-1 do
-      table.remove(seg.data)
-    end
-  elseif (#seg.data<(seg.len-5)) then
-    print("current payload: ".. #seg.data .." - desired payload: ".. seg.len-5)
-    print("Data-Length has being extended: adding "..(seg.len-5)-#seg.data.." bytes to Payload");
-    for i=#seg.data, (seg.len-5)-1 do
-      table.insert(seg.data, '00')
-    end
+-- clear beackup-area of a virtual tag
+function clearBackupArea(tag)
+  for i=1, #tag.Bck do
+    tag.Bck[i]='00'
   end
-  return seg
+  return tag
 end
 
+--- Segment related --
 ---
 -- get segmemnt-data from byte-table
 function getSegmentData(bytes, start, index)
@@ -588,128 +1597,72 @@ function getSegmentData(bytes, start, index)
     for i=0, (segment.len-5) do
       segment.data[i]=bytes[start+5+i]
     end
-    return segment
-  else return false; 
-  end
-end
-
---- 
--- put segments from byte-table to tag-table 
-function segmentsToTag(bytes, tag)
-  if(#bytes>23) then
-    local start=23
-    local i=-1
-    if (istable(tag)) then
-      repeat
-        i=i+1
-        tag.SEG[i]=getSegmentData(bytes, start, ("%02d"):format(i))
-        if (tag.Type=="SAM") then
-          if (checkKghCrc(tag, i)) then tag.SEG[i].kgh=true end
-        end
-        start=start+tag.SEG[i].len
-      until ((tag.SEG[i].valid==0) or tag.SEG[i].last==1 or i==126)
-      return tag
-    else return oops("tag is no table in: segmentsToTag ("..type(tag)..")") end
-  else print("no Segments: must be a MIM22") end
-end 
-
---- CRC calculation and validation
--- build kghCrc credentials
-function kghCrcCredentials(tag, segid) 
-  local x='00'
-  if (type(segid)=="string") then segid=tonumber(segid,10) end
-  if (segid>0) then x='93' end
-  local cred = tag.MCD..tag.MSN0..tag.MSN1..tag.MSN2..("%02x"):format(tag.SEG[segid].WRP)
-  cred = cred..("%02x"):format(tag.SEG[segid].WRC)..("%02x"):format(tag.SEG[segid].RD)..x
-  for i=0, #tag.SEG[segid].data-2 do
-    cred = cred..tag.SEG[segid].data[i]
-  end
-  return cred
-end
-
----
--- validate kghCRC to segment in tag-table
-function checkKghCrc(tag, segid)
-  if (type(tag.SEG[segid])=='table') then
-    if (tag.data[3]=="11" and tag.raw=="9f" and tag.SSC=="ff") then
-      local data=kghCrcCredentials(tag, segid)
-      if (("%02x"):format(utils.Crc8Legic(data))==tag.SEG[segid].data[tag.SEG[segid].len-5-1]) then return true; end 
-      else return false; end
-  else oops("'Kaba Group header' detected but no Segment-Data found") end
-end
-
----
--- calcuate kghCRC for a given segment 
-function calcKghCrc(tag, segid)
-  -- check if a 'Kaber Group Header' exists
-    local i
-    local data=kghCrcCredentials(tag, segid)
-    return ("%02x"):format(utils.Crc8Legic(data))
-end
-
----
--- build segmentCrc credentials
-function segmentCrcCredentials(tag, segid) 
-  local cred = tag.MCD..tag.MSN0..tag.MSN1..tag.MSN2
-  cred = cred ..tag.SEG[segid].raw[1]..tag.SEG[segid].raw[2]..tag.SEG[segid].raw[3]..tag.SEG[segid].raw[4]
-  return cred
-end
-
----
--- validate segmentCRC for a given segment
-function checkSegmentCrc(tag, segid)
-    local data=segmentCrcCredentials(tag, segid)
-    if (("%02x"):format(utils.Crc8Legic(data))==tag.SEG[segid].crc) then 
-      return true
-    end
-    return false
+    return segment
+  else return false; 
+  end
 end
 
 ---
--- calculate segmentCRC for a given segment
-function calcSegmentCrc(tag, segid)
-  -- check if a 'Kaber Group Header' exists
-    local data=segmentCrcCredentials(tag, segid)
-    return ("%02x"):format(utils.Crc8Legic(data))
+-- get index, start-aadr, length and content 
+function getSegmentStats(bytes)
+  local sStats = {}
+  local sValid, sLast, sLen, sStart, x
+  sStart=23
+  x=0
+  repeat
+    local s={}
+         -- valid = bit 6 of byte 1
+         sValid = bbit("0x"..bytes[sStart+1],6,1)
+         -- last = bit 7 of byte 1
+         sLast = bbit("0x"..bytes[sStart+1],7,1)
+         -- len = (byte 0)+(bit0-3 of byte 1)
+    sLen = tonumber(bbit("0x"..bytes[sStart+1],0,4)..bytes[sStart],16)
+    --print("index: "..("%02d"):format(x).." Len: "..sLen.." start:"..sStart.." end: "..(sStart+sLen-1))
+    s['index']=x
+    s['start']=sStart
+    s['end']=sStart+sLen-1
+    s['len']=sLen
+    if ( (sStart+sLen-1)>sStart ) then
+      table.insert(sStats, s)
+    end
+    sStart=sStart+sLen
+    x=x+1
+  until (sLast==1 or sValid==0 or x==126)
+  if (#sStats>0 ) then return sStats
+  else return false; end
 end
 
---- create master-token
-
 ---
--- write virtual Tag to real Tag
--- write clone-data to tag
-function writeToTag(plainBytes, taglen, filename)
-  local bytes
-       if(utils.confirm("\nplace your empty tag onto the PM3 to read & write\n") == false) then
-    return
+-- regenerate segment-header (after edit)
+function regenSegmentHeader(segment)
+  local seg=segment
+  local raw = segment.raw
+  local i
+  -- len  bit0..7 | len=12bit=low nibble of byte1..byte0
+  raw[1]=("%02x"):format(bbit("0x"..("%03x"):format(seg.len),0,8))
+  -- high nibble of len  bit6=valid , bit7=last of byte 1 | ?what are bit 5+6 for? maybe kgh?
+  raw[2]=("%02x"):format(bbit("0x"..("%03x"):format(seg.len),8,4)+bbit("0x"..("%02x"):format((seg.valid*64)+(seg.last*128)),0,8))
+  -- WRP
+  raw[3]=("%02x"):format(bbit("0x"..("%02x"):format(seg.WRP),0,8))
+  -- WRC + RD
+  raw[4]=("%02x"):format(tonumber((seg.WRC*16)+(seg.RD*128),10))
+  -- flag
+  seg.flag=string.sub(raw[2],0,1)
+  --print(raw[1].." "..raw[2].." "..raw[3].." "..raw[4])
+  if(#seg.data>(seg.len-5)) then
+    print("current payload: ".. #seg.data .." - desired payload: ".. seg.len-5)
+    print(acyellow.."Data-Length has being reduced:"..acgreen.." auto-removing "..acyellow.. #seg.data-(seg.len-5) ..acgreen .." bytes from Payload!"..acoff);
+    for i=(seg.len-5), #seg.data-1 do
+      table.remove(seg.data)
+    end
+  elseif (#seg.data<(seg.len-5)) then
+    print("current payload: ".. #seg.data .." - desired payload: ".. seg.len-5)
+    print(acyellow.."Data-Length has being extended:"..acgreen.." auto-adding "..acyellow..(seg.len-5)-#seg.data ..acgreen .." bytes to Payload!"..acoff);
+    for i=#seg.data, (seg.len-5-1) do
+      table.insert(seg.data, '00')
+    end
   end
-       
-       -- write data to file
-       if (taglen > 0) then
-               WriteBytes = utils.input("enter number of bytes to write?", taglen)
-
-               -- load file into pm3-buffer
-    if (type(filename)~="string") then filename=input("filename to load to pm3-buffer?","legic.temp") end
-               cmd = 'hf legic load '..filename
-               core.console(cmd)
-               
-               -- write pm3-buffer to Tag
-               for i=0, WriteBytes do
-                       if ( i<5 or i>6) then
-                               cmd = ('hf legic write 0x%02x 0x01'):format(i)
-                               core.console(cmd)
-        --print(cmd)
-                       elseif (i == 6) then
-                               -- write DCF in reverse order (requires 'mosci-patch')
-                               cmd = 'hf legic write 0x05 0x02'
-                               core.console(cmd)
-        --print(cmd)
-                       else
-                               print("skipping byte 0x05 - will be written next step")
-                       end                             
-                       utils.Sleep(0.2)
-               end
-       end
+  return seg
 end
 
 ---
@@ -720,7 +1673,7 @@ function editSegment(tag, index)
   if (istable(tag.SEG[index])) then
     for k,v in pairs(tag.SEG[index]) do
       if(string.find(edit_possible,k)) then
-        tag.SEG[index][k]=tonumber(input(k..": ", v),10)
+        tag.SEG[index][k]=tonumber(input(accyan..k..acoff..": ", v),10)
       end
     end
   else print("Segment with Index: "..("%02d"):format(index).." not found in Tag")
@@ -734,10 +1687,13 @@ end
 ---
 -- edit Segment Data
 function editSegmentData(data)
+  local lc=check4LegicCash(data)
+  io.write("\n")
   if (istable(data)) then
     for i=0, #data-1 do
-      data[i]=input("Data"..i..": ", data[i])
+      data[i]=input(accyan.."Data"..i..acoff..": ", data[i])
     end 
+    if (lc) then data=fixLegicCash(data) end
     return data
   else
     print("no Segment-Data found")
@@ -763,7 +1719,7 @@ end
 -- helper to selecting a segment
 function selectSegment(tag)
   local sel
-  if (istable(tag)) then 
+  if (istable(tag.SEG[0])) then 
     print("availabe Segments:\n"..segmentList(tag))
     sel=input("select Segment: ", '00')
     sel=tonumber(sel,10)
@@ -774,7 +1730,7 @@ function selectSegment(tag)
     return false
   end
 end
-
+  
 ---
 -- add segment
 function addSegment(tag)
@@ -807,88 +1763,521 @@ function addSegment(tag)
 end
 
 ---
---
+-- get only the stamp-bytes of a segment
+function getSegmentStamp(seg, bytes)
+  local stamp=""
+  local stamp_len=7
+  --- the 'real' stamp on MIM is not really easy to tell for me since the 'data-block' covers stamp0..stampn+data0..datan
+  -- there a no stamps longer than 7 bytes & they are write-protected by default , and I have not seen user-credntials 
+  -- with stamps smaller 3 bytes (except: Master-Token)
+  -- WRP -> Read/Write Protection 
+  -- WRC -> Read/Write Condition
+  -- RD depends on WRC - if WRC > 0 and RD=1: only reader with matching #WRC of Stamp-bytes in thier Database have Read-Access to the Tag
+  if (seg.WRP<7) then stamp_len=(seg.WRP) end
+  for i=1, (stamp_len) do
+    stamp=stamp..seg.data[i-1]
+  end
+  if (bytes) then 
+    stamp=str2bytes(stamp)
+    return stamp
+  else return stamp end
+end
+
+---
+-- edit stamp of a segment
+function editStamp(new_stamp, uid, data)
+  local stamp=str2bytes(new_stamp)
+  for i=0, #stamp-1 do
+    data[i]=stamp[i+1]
+  end
+  -- now fill in stamp
+  for i=0, (string.len(new_stamp)/2)-1 do
+      data[i]=stamp[i+1]
+  end
+  return fix3rdPartyCash1(uid, data)
+end
+
+---
+-- autoselect special/known segments
+function autoSelectSegment(tag, s)
+  local uid=tag.MCD..tag.MSN0..tag.MSN1..tag.MSN2
+  local x=#tag.SEG+1
+  local res = false
+  io.write("autoSelect ")
+  --- search for desired segment-type
+  -- 3rd Party Segment
+  if (s=="3rdparty") then
+    repeat 
+      io.write(". ")
+      x=x-1
+      res=check43rdPartyCash1(uid, tag.SEG[x].data)
+    until ( res or x==0 )
+   end  
+  -- Legic-Cash Segment
+  if (s=="legiccash") then
+    repeat 
+      io.write(". ")
+      x=x-1
+      res=check4LegicCash(tag.SEG[x].data)
+    until ( res or x==0 )
+   end
+   ---
+   -- segment found
+   if (res) then
+     io.write("\nautoselected Index: "..string.format("%02d", x).."\n")
+     return x
+   end
+   ---
+   -- nothing found   
+   io.write(acyellow.."no Segment found\n"..acoff) 
+   return -1 
+end
+
+---
+-- delete segment (except segment 00)
 function delSegment(tag, index)
-  local i
-  if (type(index)=="string") then index=tonumber(index,10) end
-  if (index > 0) then
-    table.remove(tag.SEG, index)
+  if (istable(tag.SEG[0])) then
+    local i
+    if (type(index)=="string") then index=tonumber(index,10) end
+    if (index > 0) then
+      table.remove(tag.SEG, index)
+      for i=0, #tag.SEG do
+        tag.SEG[i].index=("%02d"):format(i)
+      end
+    end
+    if(istable(tag.SEG[#tag.SEG])) then tag.SEG[#tag.SEG].last=1 end
+    return tag
+  end
+end
+
+---
+-- edit uid 3rd party cash
+function edit3rdUid(mapid, uid, data)
+  mapid=("%06x"):format(tonumber(mapid, 10))
+  data[46]=string.sub(mapid, 1 ,2)
+  data[47]=string.sub(mapid, 3 ,4)
+  data[48]=string.sub(mapid, 5 ,6)
+  return fix3rdPartyCash1(uid, data)
+end
+
+---
+-- edit balance 3rd party cash
+function edit3rdCash(new_cash, uid, data)
+  new_cash=("%04x"):format(new_cash)
+  data[32]=string.sub(new_cash, 0,2)
+  data[33]=string.sub(new_cash, 3,4)
+  data[34]=("%02x"):format(utils.Crc8Legic(uid..new_cash))
+  data[35]=string.sub(new_cash, 0,2)
+  data[36]=string.sub(new_cash, 3,4)
+  data[37]=("%02x"):format(utils.Crc8Legic(uid..new_cash))
+  data[39]=string.sub(new_cash, 0,2)
+  data[40]=string.sub(new_cash, 3,4)
+  data[41]='00'
+  data[42]='00'
+  data[43]='00'
+  data[44]='00'
+  return fix3rdPartyCash1(uid, data)
+end
+
+---
+-- edit 3rd-party cash
+function edit3rdPartyCash1(tag, x)
+   local uid=tag.MCD..tag.MSN0..tag.MSN1..tag.MSN2
+  if (confirm("\nedit Balance?")) then
+    local new_cash=input("enter new Balance≤\nwithout comma and without currency-sign! (0-65535)", "100")
+    tag.SEG[x].data=edit3rdCash(new_cash, uid, tag.SEG[x].data)
+  end
+  -- change User-ID (used for online-account-mapping)
+  if (confirm("\nedit UserID-Mapping?")) then
+    local new_mapid=input("enter new UserID (6-digit value)", "012345")
+    tag.SEG[x].data=edit3rdUid(new_mapid, uid, tag.SEG[x].data)
+  end
+  if (confirm("\nedit Stamp?")) then
+    local new_stamp=input("enter new Stamp", getSegmentStamp(tag.SEG[x]))
+    tag.SEG[x].data=editStamp(new_stamp, uid, tag.SEG[x].data)
+    new_stamp=getSegmentStamp(tag.SEG[x], 'true')
+    print("stamp_bytes: "..#new_stamp)
+    -- replace stamp in 'block 1' also
+    io.write("editing stamp in Block 1 also ")
+    for i=20, (20+#new_stamp-1) do
+      tag.SEG[x].data[i]=new_stamp[i-19]
+      io.write(".");
+    end
+    print(" done")
+    -- fix known checksums
+    tag.SEG[x].data=fix3rdPartyCash1(uid, tag.SEG[x].data)
+  end    
+  return tag  
+end
+
+---
+-- edit Legic Cash
+function editLegicCash(data)
+  local limit, curr, balance, rid, tcv
+  -- currency of balance & limit
+  curr=currency[data[8]..data[9]]
+  -- maximum balance
+  limit=string.format("%4.2f", tonumber(data[10]..data[11]..data[12], 16)/100)
+  -- current balance
+  balance=string.format("%4.2f", tonumber(data[15]..data[16]..data[17], 16)/100)
+  -- reader-id who wrote last transaction
+  rid=tonumber(data[18]..data[19]..data[20], 16)
+  -- transaction counter value
+  tcv=tonumber(data[29], 16)
+  
+  -- edit currency
+  if (confirm(accyan.."change Currency?"..acoff)) then
+    for k, v in pairs(currency) do io.write(k .. " = " .. v .. "\n") end
+    curr=input(accyan.."enter the 4-digit Hex for the new Currency:"..acoff, data[8]..data[9])
+    data[8]=string.sub(curr, 1, 2)
+    data[9]=string.sub(curr, 3, 4)
+  end
+  
+  -- edit limit
+  if (confirm(accyan.."change Limit?"..acoff)) then
+    limit=string.format("%06x", input(accyan.."enter the Decimal for the new Limit:"..acoff, limit))
+    data[10]=string.sub(limit, 1, 2)
+    data[11]=string.sub(limit, 3, 4)
+    data[12]=string.sub(limit, 5, 6)
+  end
+  
+  -- edit balance
+  if (confirm(accyan.."change Balance?"..acoff)) then
+    balance=string.format("%06x", input(accyan.."enter the Decimal for the new Balance:"..acoff, balance))
+    print("Balance: "..balance)
+    data[15]=string.sub(balance, 1, 2)
+    data[16]=string.sub(balance, 3, 4)
+    data[17]=string.sub(balance, 5, 6)
+  end  
+  
+  -- edit transaction-counter
+  if (confirm(accyan.."change Transaction-Counter?"..acoff)) then
+    tcv=string.format("%02x", input(accyan.."enter the 4-digit Hex for the new Currency:"..acoff, data[29]))
+    data[29]=tcv
+  end
+  
+  -- edit reader.id
+  if (confirm(accyan.."change Last-Reader-ID?"..acoff)) then
+    rid=string.format("%06x", input(accyan.."enter the Decimal for the new Balance:"..acoff, rid))
+    print("Balance: "..balance)
+    data[18]=string.sub(rid, 1, 2)
+    data[19]=string.sub(rid, 3, 4)
+    data[20]=string.sub(rid, 5, 6)
+  end
+  
+  return fixLegicCash(data)
+end
+
+---
+-- chack for signature of a 'Legic-Cash-Segment'
+function check4LegicCash(data)
+  if(#data==32) then
+    local stamp_len=(#data-25)
+    local stamp=""
+    for i=0, stamp_len-1 do
+      stamp=stamp..data[i].." "
+    end
+    if (data[7]=="01") then
+      if (("%04x"):format(utils.Crc16(dumpTable(data, "", 0, 12))) == data[13]..data[14]) then
+        if (("%04x"):format(utils.Crc16(dumpTable(data, "", 15, 20))) == data[21]..data[22]) then
+          if (("%04x"):format(utils.Crc16(dumpTable(data, "", 23, 29))) == data[30]..data[31]) then
+            io.write(accyan.."Legic-Cash Segment detected "..acoff)
+            return true
+          end
+        end
+      end
+    end
+  end
+  return false
+end
+
+---
+-- chack for signature of a '3rd Party Cash-Segment' - not all bytes know until yet !!
+function check43rdPartyCash1(uid, data)
+  if(#data==95) then
+    -- too explicit checking will avoid fixing ;-)
+    if (string.find(compareCrc(utils.Crc8Legic(uid..dumpTable(data, "", 19, 30)), data[31]),"valid")) then
+      --if (compareCrc(utils.Crc8Legic(uid..data[32]..data[33]), data[34])=="valid") then
+        --if (compareCrc(utils.Crc8Legic(uid..data[35]..data[36]), data[37])=="valid") then
+         --if (compareCrc(utils.Crc8Legic(uid..dumpTable(data, "", 56, 61)), data[62])=="valid") then
+            --if (compareCrc(utils.Crc8Legic(uid..dumpTable(data, "", 74, 88)), data[89])=="valid") then
+              io.write(accyan.."3rd Party Cash-Segment detected "..acoff)
+              return true
+              --end
+          --end
+          --end
+      --end
+    end 
+  end
+  return false
+end
+
+--- CRC related ---
+---
+-- build segmentCrc credentials
+function segmentCrcCredentials(tag, segid) 
+  if (istable(tag.SEG[0])) then
+    local cred = tag.MCD..tag.MSN0..tag.MSN1..tag.MSN2
+    cred = cred ..tag.SEG[segid].raw[1]..tag.SEG[segid].raw[2]..tag.SEG[segid].raw[3]..tag.SEG[segid].raw[4]
+    return cred
+    else return print(acyellow.."Master-Token / unsegmented Tag!"..acoff) end
+end
+
+---
+-- build kghCrc credentials
+function kghCrcCredentials(tag, segid) 
+  if (istable(tag) and istable(tag.SEG[0])) then
+    local x='00'
+    if (type(segid)=="string") then segid=tonumber(segid,10) end
+    if (segid>0) then x='93' end
+    local cred = tag.MCD..tag.MSN0..tag.MSN1..tag.MSN2..("%02x"):format(tag.SEG[segid].WRP)
+    cred = cred..("%02x"):format(tag.SEG[segid].WRC)..("%02x"):format(tag.SEG[segid].RD)..x
+    for i=0, #tag.SEG[segid].data-2 do
+      cred = cred..tag.SEG[segid].data[i]
+    end
+    return cred
+  end
+end
+
+---
+-- compare two bytes
+function compareCrc(calc, guess)
+  calc=("%02x"):format(calc)
+  if (calc==guess) then return acgreen.."valid"..acoff
+  else return acred.."error "..acoff..calc.."!="..guess end
+end
+
+---
+-- compare 4 bytes
+function compareCrc16(calc, guess)
+  calc=("%04x"):format(calc)
+  if (calc==guess) then return acgreen.."valid"..acoff
+  else return acred.."error "..acoff..calc.."!="..guess end
+end
+
+---
+-- repair / fix crc's of a 'Legic-Cash-Segment'
+function fixLegicCash(data)
+  if(#data==32 and data[7]=="01") then
+    local crc1, crc2, crc3
+    -- set shadow-balance equal to balance
+    data[23]=data[15]
+    data[24]=data[16]
+    data[25]=data[17]
+    -- set shadow-last-reader to last-reader
+    data[26]=data[18]
+    data[27]=data[19]
+    data[28]=data[20]
+    -- calculate all crc's
+    crc1=("%04x"):format(utils.Crc16(dumpTable(data, "", 0, 12)))
+    crc2=("%04x"):format(utils.Crc16(dumpTable(data, "", 15, 20)))
+    crc3=("%04x"):format(utils.Crc16(dumpTable(data, "", 23, 29)))
+    -- set crc's
+    data[13]=string.sub(crc1, 1, 2)
+    data[14]=string.sub(crc1, 3, 4)
+    data[21]=string.sub(crc2, 1, 2)
+    data[22]=string.sub(crc2, 3, 4)
+    data[30]=string.sub(crc3, 1, 2)
+    data[31]=string.sub(crc3, 3, 4)
+    return data
+  end
+end
+
+---
+-- repair / fix (yet known) crc's of a '3rd Party Cash-Segment' - not all bytes know until yet !!
+function fix3rdPartyCash1(uid, data)
+  if(#data==95) then
+    -- checksum 1
+    data[31]=("%02x"):format(utils.Crc8Legic(uid..dumpTable(data, "", 19, 30)))
+    -- checksum 2
+    data[34]=("%02x"):format(utils.Crc8Legic(uid..data[32]..data[33]))
+    -- checksum 3
+    data[37]=("%02x"):format(utils.Crc8Legic(uid..data[35]..data[36]))
+    -- checksum 4
+    data[55]=("%02x"):format(utils.Crc8Legic(uid..dumpTable(data, "", 46, 54)))
+    -- checksum 5
+    data[62]=("%02x"):format(utils.Crc8Legic(uid..dumpTable(data, "", 56, 61)))
+    -- checksum 6
+    data[73]=("%02x"):format(utils.Crc8Legic(uid..dumpTable(data, "", 63, 72)))
+    -- checksum 7
+    data[89]=("%02x"):format(utils.Crc8Legic(uid..dumpTable(data, "", 74, 88)))
+    return data
+  end
+end
+
+---
+-- calculate Master-Token crc
+function calcMtCrc(bytes) 
+  --print(#bytes)
+  local cmd=bytes[1]..bytes[2]..bytes[3]..bytes[4]..bytes[7]..bytes[6]..bytes[8]
+  local len=(tonumber(0xfc ,10)-("%d"):format('0x'..bytes[7]))
+  for i=1, len do
+    cmd=cmd..bytes[8+i]
+  end
+  local res=("%02x"):format(utils.Crc8Legic(cmd))
+  return res
+end
+
+---
+-- calculate segmentCRC for a given segment
+function calcSegmentCrc(tag, segid)
+  if (istable(tag.SEG[0])) then
+  -- check if a 'Kaber Group Header' exists
+    local data=segmentCrcCredentials(tag, segid)
+    return ("%02x"):format(utils.Crc8Legic(data))
+  end
+end
+
+---
+-- calcuate kghCRC for a given segment 
+function calcKghCrc(tag, segid)
+  if (istable(tag.SEG[0])) then
+  -- check if a 'Kaber Group Header' exists
+    local i
+    local data=kghCrcCredentials(tag, segid)
+    return ("%02x"):format(utils.Crc8Legic(data))
+  end
+end
+
+---
+-- check all segmnet-crc
+function checkAllSegCrc(tag)
+  if (istable(tag.SEG[0])) then
+    for i=0, #tag.SEG do
+      crc=calcSegmentCrc(tag, i)
+      tag.SEG[i].crc=crc
+    end
+    else return print(acyellow.."Master-Token / unsegmented Tag"..acoff) end
+end
+
+---
+-- check all segmnet-crc
+function checkAllKghCrc(tag)
+  if (istable(tag.SEG[0])) then
     for i=0, #tag.SEG do
-      tag.SEG[i].index=("%02d"):format(i)
+      crc=calcKghCrc(tag, i)
+      if (tag.SEG[i].kgh) then 
+        tag.SEG[i].data[#tag.SEG[i].data-1]=crc
+      end
     end
   end
-  if(istable(tag.SEG[#tag.SEG])) then tag.SEG[#tag.SEG].last=1 end
-  return tag
+end
+
+---
+-- validate segmentCRC for a given segment
+function checkSegmentCrc(tag, segid)
+    local data=segmentCrcCredentials(tag, segid)
+    if (("%02x"):format(utils.Crc8Legic(data))==tag.SEG[segid].crc) then 
+      return true
+    end
+    return false
+end
+
+---
+-- validate kghCRC to segment in tag-table
+function checkKghCrc(tag, segid)
+  if (type(tag.SEG[segid])=='table') then
+    if (tag.data[3]=="11" and tag.raw=="9f" and tag.SSC=="ff") then
+      local data=kghCrcCredentials(tag, segid)
+      if (("%02x"):format(utils.Crc8Legic(data))==tag.SEG[segid].data[tag.SEG[segid].len-5-1]) then return true; end 
+      else return false; end
+  else oops(acred.."'Kaba Group header' detected but no Segment-Data found"..ansocolors.reset) end
 end
 
 ---
 -- helptext for modify-mode
 function modifyHelp()
   local t=[[
-  
-    Data I/O           Segment Manipulation        File I/O   
-------------------     --------------------      ---------------
-  rt => read    Tag     ds => dump   Segments     lf => load File
-  wt => write   Tag     as => add    Segment      sf => save File 
-  ct => copy io Tag     es => edit   Segment      xf => xor  File
-  tc => copy oi Tag     ed => edit   Data     
-  di => dump  inTag     rs => remove Segment
-  do => dump outTag     cc => check  Segment-CRC                  
-                        ck => check  KGH                
-                        tk => toggle KGH-Flag
-  q => quit             xc => get    KGH-Str      h => this Help
-  ]]                    
+    
+         Data I/O                    Segment Manipulation                Token-Data
+     -----------------               --------------------           ---------------------
+     rt => read    Tag               as => add    Segment           mt => make Token
+     wt => write   Tag               es => edit   Segment Header    et => edit Token data
+                                     ed => edit   Segment Data      tk => toggle KGH-Flag
+         File I/O                    rs => remove Segment           
+     -----------------               cc => check  Segment-CRC       
+     lf => load   File               ck => check  KGH               
+     sf => save   File               ds => dump   Segments          
+     xf => xor to File                                              
+                                                                                                                                        
+                                                                    
+         Virtual Tags                       tagMap                   (partial) known Segments 
+ --------------------------------    ---------------------          ---------------------------
+ ct => copy  mainTag to backupTag    mm => make (new) Map           dlc => dump  Legic-Cash    
+ tc => copy  backupTag to mainTag    em => edit Map submenu         elc => edit  Legic-Cash    
+ tt => switch mainTag & backupTag    lm => load map from file       d3p => dump  3rd-Party-Cash
+ di => dump  mainTag                 sm => save map to file         e3p => edit  3rd-Party-Cash
+ do => dump  backupTag                      
+                           
+                            h => this help                q => quit
+  ]]                      
   return t
 end
 
 --- 
 -- modify Tag (interactive)
 function modifyMode()
-  local i, outTAG, inTAG, outfile, infile, sel, segment, bytes, outbytes
+  local i, backupTAG,  outTAG, inTAG, outfile, infile, sel, segment, bytes, outbytes, tagMap
+  
   actions = {
+    ---
+    -- helptext
      ["h"] = function(x) 
-              print(modifyHelp().."\n".."tags im Memory:"..(istable(inTAG) and " inTAG" or "")..(istable(outTAG) and " outTAG" or ""))
+              print("  Version: "..version); 
+              print(modifyHelp().."\n".."tags im Memory: "..(istable(inTAG) and ((currentTag=='inTAG') and acgreen.."*mainTAG"..acoff or "mainTAG") or "").."  "..(istable(backupTAG) and ((currentTag=='backupTAG') and acgreen.."*backupTAG"..acoff or "backupTAG") or ""))
             end,
-    ["rt"] = function(x) inTAG=readFromPM3(); actions['di']('') end,
+    ---
+    -- read real Tag with PM3 into virtual 'mainTAG'
+    ["rt"] = function(x) 
+                inTAG=readFromPM3(); 
+                --actions.di() 
+              end,
+    ---
+    -- write content of virtual 'mainTAG' to real Tag with PM3
     ["wt"] = function(x)  
-              if(istable(inTAG)) then
-                local taglen=22
-                for i=0, #inTAG.SEG do
-                  taglen=taglen+inTAG.SEG[i].len+5
-                end
-                -- read new tag (output tag)
-                outTAG=readFromPM3()
-                outbytes=tagToBytes(outTAG)
-                -- copy 'inputbuffer' to 'outputbuffer'
-                inTAG.MCD  = outbytes[1]
-                inTAG.MSN0 = outbytes[2]
-                inTAG.MSN1 = outbytes[3]
-                inTAG.MSN2 = outbytes[4]
-                inTAG.MCC  = outbytes[5]
-                -- recheck all segments-crc/kghcrc
-                checkAllSegCrc(inTAG)
-                checkAllKghCrc(inTAG)
-                --get bytes from ready outTAG
-                bytes=tagToBytes(inTAG)
-                if (bytes) then   
-                  writeFile(bytes, 'MylegicClone.hex')         
-                  writeToTag(bytes, taglen, 'MylegicClone.hex')
-                  actions['rt']('') 
-                end
-               end
+                writeToTag(inTAG)
               end,
+    ---
+    -- copy mainTAG to backupTAG        
     ["ct"] = function(x)  
-                print("copy virtual input-TAG to output-TAG")
-                outTAG=inTAG
+                print(accyan.."copy mainTAG to backupTAG"..acoff)  
+                  backupTAG=deepCopy(inTAG)
             end,
+    ---
+    -- copy backupTAG to mainTAG
     ["tc"] = function(x)  
-                print("copy virtual output-TAG to input-TAG")
-                inTAG=outTAG
+                print(accyan.."copy backupTAG to mainTAG"..acoff)
+                inTAG=deepCopy(backupTAG)
             end,
+    ---
+    -- toggle between mainTAG and backupTAG
+    ["tt"] = function(x)  
+                -- copy main to temp
+                outTAG=deepCopy(inTAG)
+                -- copy backup to main
+                inTAG=deepCopy(backupTAG)
+                print(accyan.."toggle to "..accyan..((currentTag=='inTAG') and "backupTAG" or "mainTAG")..acoff)
+                if(currentTag=="inTAG") then currentTag='backupTAG'
+                else currentTag='inTAG' end
+                -- copy temp (main) to backup
+                backupTAG=deepCopy(outTAG)
+            end,
+    ---
+    -- load file into mainTAG
     ["lf"] = function(x)  
-              filename=input("enter filename: ", "legic.temp")
+              
+              if (type(x)=='string' and file_check(x)) then filename=x
+              else  filename=input("enter filename: ", "legic.temp") end
               inTAG=readFile(filename)
+              -- check for existing tagMap
+              if (file_check(filename..".map")) then
+                if(confirm(accyan.."Mapping-File for "..acoff..filename..accyan.." found - load it also?"..acoff)) then
+                  tagMap=loadTagMap(filename..".map")
+                end
+              end         
             end,
+    ---
+    -- save values of mainTAG to a file (xored with MCC of mainTAG)
     ["sf"] = function(x)  
               if(istable(inTAG)) then
                 outfile=input("enter filename:", "legic.temp")
@@ -899,6 +2288,8 @@ function modifyMode()
                 end
                end
               end,
+    ---
+    -- save values of mainTAG to a file (xored with 'specific' MCC) 
     ["xf"] = function(x)  
               if(istable(inTAG)) then
                 outfile=input("enter filename:", "legic.temp")
@@ -911,82 +2302,308 @@ function modifyMode()
                 end
               end
              end,
-    ["di"] = function(x) if (istable(inTAG)) then print("\n"..dumpTag(inTAG).."\n") end end,
-    ["do"] = function(x) if (istable(outTAG)) then print("\n"..dumpTag(outTAG).."\n") end end,
+    ---
+    -- dump mainTAG (and all Segments)
+    ["di"] = function(x) 
+                if (istable(inTAG)) then 
+                  local uid=inTAG.MCD..inTAG.MSN0..inTAG.MSN1..inTAG.MSN2
+                  if(istable(inTAG.SEG[0])) then
+                    for i=0, #inTAG.SEG do
+                      if(check43rdPartyCash1(uid, inTAG.SEG[i].data)) then
+                        io.write(accyan.."in Segment index: "..inTAG.SEG[i].index ..acoff.. "\n")                      
+                      elseif(check4LegicCash(inTAG.SEG[i].data)) then
+                        io.write(accyan.."in Segment index: "..inTAG.SEG[i].index..acoff.."\n")
+                        lc=true; 
+                        lci=inTAG.SEG[i].index;
+                      end
+                    end
+                  end
+                  print("\n"..dumpTag(inTAG).."\n") 
+                  if (lc) then actions["dlc"](lci) end
+                  lc=false
+                end 
+              end,
+    ---
+    -- dump backupTAG (and all Segments)
+    ["do"] = function(x) if (istable(backupTAG)) then print("\n"..dumpTag(backupTAG).."\n") end end,
+    ---
+    -- create a empty tagMap
+    ["mm"] = function(x) 
+                -- clear existing tagMap and init
+                if (istable(inTAG)) then 
+                  tagMap=makeTagMap()
+                end
+              end,
+    ---
+    -- edit a tagMap
+    ["em"] = function(x) 
+                if (istable(inTAG)==false) then 
+                  if (confirm("no mainTAG in memory!\nread from PM3?")) then 
+                    actions['rt']() 
+                  elseif (confirm("load from File?")) then 
+                    actions['lf']() 
+                    else return
+                  end  
+                end
+                if (istable(tagMap)==false) then actions['mm']() end                
+                -- edit
+                tagMap=editTagMap(inTAG, tagMap)
+              end,
+    ---
+    -- save a tagMap
+    ["sm"] = function(x) 
+                if (istable(tagMap)) then
+                  if (istable(tagMap) and #tagMap.mappings>0) then
+                    print(accyan.."Map contains "..acoff..#tagMap..accyan.." mappings"..acoff)
+                    saveTagMap(tagMap, input(accyan.."enter filename:"..acoff, "Legic.map"))
+                  else 
+                    print(acyellow.."no mappings in tagMap!"..acoff)
+                  end
+                end 
+              end,
+    ---
+    -- load a tagMap
+    ["lm"] = function(x) 
+                tagMap=loadTagMap(input(accyan.."enter filename:"..acoff, "Legic.map"))
+              end,
+    ---
+    -- dump single segment
     ["ds"] = function(x) 
-                sel=selectSegment(inTAG)
-                if (sel) then print("\n"..(dumpSegment(inTAG, sel) or "no Segments available").."\n") end 
+                if (type(x)=="string" and string.len(x)>0) then sel=tonumber(x,10)
+                else sel=selectSegment(inTAG) end
+                if (sel) then print("\n"..(dumpSegment(inTAG, sel) or acred.."no Segments available") ..acoff.."\n") end 
               end,
+    ---
+    -- edit segment header
     ["es"] = function(x) 
-              sel=selectSegment(inTAG)
+              if (type(x)=="string" and string.len(x)>0) then sel=tonumber(x,10)
+              else sel=selectSegment(inTAG) end
               if (sel) then 
-                if(istable(inTAG.SEG)) then
+                if(istable(inTAG.SEG[0])) then
                   inTAG=editSegment(inTAG, sel)
                   inTAG.SEG[sel]=regenSegmentHeader(inTAG.SEG[sel])
-              else print("no Segments in Tag") end 
+              else print(acyellow.."no Segments in Tag"..acoff) end 
               end
             end,
+    ---
+    -- add segment
     ["as"] = function(x) 
               if (istable(inTAG.SEG[0])) then
                 inTAG=addSegment(inTAG)
                 inTAG.SEG[#inTAG.SEG-1]=regenSegmentHeader(inTAG.SEG[#inTAG.SEG-1])
                 inTAG.SEG[#inTAG.SEG]=regenSegmentHeader(inTAG.SEG[#inTAG.SEG]) 
-                else print("unsegmented Tag!")
+                else print(accyan.."Master-Token / unsegmented Tag!"..acoff)
               end
             end,
+    ---
+    -- remove segment
     ["rs"] = function(x) 
-              if (istable(inTAG)) then
-                sel=selectSegment(inTAG)
+              if (istable(inTAG.SEG[0])) then
+                if (type(x)=="string" and string.len(x)>0) then sel=tonumber(x,10)
+                else sel=selectSegment(inTAG) end
                 inTAG=delSegment(inTAG, sel)
                 for i=0, #inTAG.SEG do
                   inTAG.SEG[i]=regenSegmentHeader(inTAG.SEG[i])
                 end
               end
             end,
+    ---
+    -- edit data-portion of single segment
     ["ed"] = function(x) 
-              sel=selectSegment(inTAG)
-              if (sel) then 
+              if (type(x)=="string" and string.len(x)>0) then sel=tonumber(x,10)
+              else sel=selectSegment(inTAG) end
+              if (istable(inTAG.SEG[sel])) then 
                 inTAG.SEG[sel].data=editSegmentData(inTAG.SEG[sel].data) 
               end
             end,
-     ["ts"] = function(x) 
-                sel=selectSegment(inTAG)
+    ---
+    -- edit Tag (MCD, MSN, MCC etc.)
+    ["et"] = function(x) 
+                if (istable(inTAG)) then
+                  editTag(inTAG)
+                end
+            end,
+    ---
+    -- make (dummy) Token
+    ["mt"] = function(x) inTAG=makeToken(); actions.di() end,
+    ---
+    -- fix segment-crc on single segment
+    ["ts"] = function(x) 
+               if (type(x)=="string" and string.len(x)>0) then sel=tonumber(x,10)
+               else sel=selectSegment(inTAG) end
                 regenSegmentHeader(inTAG.SEG[sel]) 
               end,
-     ["tk"] = function(x) 
-                sel=selectSegment(inTAG)
+    ---
+    -- toggle kgh-crc-flag on a single segment 
+    ["tk"] = function(x) 
+               if (istable(inTAG) and istable(inTAG.SEG[0])) then
+                if (type(x)=="string" and string.len(x)>0) then sel=tonumber(x,10)
+                else sel=selectSegment(inTAG) end
                 if(inTAG.SEG[sel].kgh) then inTAG.SEG[sel].kgh=false
                 else inTAG.SEG[sel].kgh=true end
+               end
+              end,
+    ---
+    -- calculate LegicCrc8
+    ["k"] = function(x)
+              if (type(x)=="string" and string.len(x)>0) then
+                print(("%02x"):format(utils.Crc8Legic(x))) 
+              end
+             end,
+    ---
+    -- noop
+    ["xb"] = function(x)
+          end,
+    ---
+    -- print string for LegicCrc8-calculation about single segment 
+    ["xc"] = function(x) 
+               if (istable(inTAG) and istable(inTAG.SEG[0])) then
+                 if (type(x)=="string" and string.len(x)>0) then sel=tonumber(x,10)
+                 else sel=selectSegment(inTAG) end 
+                 print("k "..kghCrcCredentials(inTAG, sel)) 
+               end 
+              end,
+    ---
+    -- fix legic-cash checksums
+    ["flc"] = function(x) 
+                if (type(x)=="string" and string.len(x)>0) then x=tonumber(x,10)
+                else x=selectSegment(inTAG) end 
+                inTAG.SEG[x].data=fixLegicCash(inTAG.SEG[x].data)
+              end,
+    ---
+    -- edit legic-cash values fixLegicCash(data)
+    ["elc"] = function(x) 
+                x=autoSelectSegment(inTAG, "legiccash")
+                inTAG.SEG[x].data=editLegicCash(inTAG.SEG[x].data)
+              end,
+    ---
+    -- dump legic-cash human-readable
+    ["dlc"] = function(x) 
+                -- if segment index was user defined
+                if (type(x)=="string" and string.len(x)>0) then 
+                  x=tonumber(x,10)
+                  print(string.format("User-Selected Index %02d", x))
+                -- or try to find match
+                else x=autoSelectSegment(inTAG, "legiccash") end
+                -- dump it
+                dumpLegicCash(inTAG, x)
+              end,
+    ---
+    -- dump 3rd-party-cash-segment
+    ["d3p"] = function(x)
+                local uid=inTAG.MCD..inTAG.MSN0..inTAG.MSN1..inTAG.MSN2
+                -- if segment index was user defined
+                if (type(x)=="string" and string.len(x)>0) then 
+                  x=tonumber(x,10)
+                  print(string.format("User-Selected Index %02d", x))
+                -- or try to find match
+                else x=autoSelectSegment(inTAG, "3rdparty") end                  
+                if (istable(inTAG) and istable(inTAG.SEG[x]) and inTAG.SEG[x].len == 100) then
+                  uid=inTAG.MCD..inTAG.MSN0..inTAG.MSN1..inTAG.MSN2
+                  if (check43rdPartyCash1(uid, inTAG.SEG[x].data)) then
+                    dump3rdPartyCash1(inTAG, x)
+                  end
+                end
+              end,
+    ---
+    -- dump 3rd-party-cash-segment (raw blocks and checksums over 'known areas')
+    ["r3p"] = function(x)
+                local uid=inTAG.MCD..inTAG.MSN0..inTAG.MSN1..inTAG.MSN2
+                -- if segment index was user defined
+                if (type(x)=="string" and string.len(x)>0) then 
+                  x=tonumber(x,10)
+                  print(string.format("User-Selected Index %02d", x))
+                -- or try to find match
+                else x=autoSelectSegment(inTAG, "3rdparty") end
+                print3rdPartyCash1(inTAG, x)
+              end,
+    ---
+    -- edit 3rd-party-cash-segment values (Balance, Mapping-UID, Stamp)
+    ["e3p"] = function(x) 
+                local uid=inTAG.MCD..inTAG.MSN0..inTAG.MSN1..inTAG.MSN2
+                -- if segment index was user defined
+                if (type(x)=="string" and string.len(x)>0) then 
+                  x=tonumber(x,10)
+                  print(string.format("User-Selected Index %02d", x))
+                -- or try to find match
+                else x=autoSelectSegment(inTAG, "3rdparty") end                 
+                if (istable(inTAG) and istable(inTAG.SEG[x]) and inTAG.SEG[x].len == 100) then
+                  inTAG=edit3rdPartyCash1(inTAG, x)
+                    dump3rdPartyCash1(inTAG, x)
+                end
+              end,
+    ---
+    -- force fixing 3rd-party-checksums
+    ["f3p"] = function(x) 
+               if(type(x)=="string" and string.len(x)>=2) then x=tonumber(x, 10)
+               else x=selectSegment(inTAG) end
+               if (istable(inTAG.SEG[x])) then 
+                  local uid=inTAG.MCD..inTAG.MSN0..inTAG.MSN1..inTAG.MSN2
+                  inTAG.SEG[x].data=fix3rdPartyCash1(uid, inTAG.SEG[x].data)
+               end 
               end,
-     ["k"] = function(x) 
-               print(("%02x"):format(utils.Crc8Legic(x)))
+    ---
+    -- get stamp from single segment 
+    ["gs"] = function(x)
+                if(type(x)=="string" and string.len(x)>=2) then x=tonumber(x, 10)
+                else x=selectSegment(inTAG) end
+                local stamp=getSegmentStamp(inTAG.SEG[x])
+                print("Stamp : "..stamp)
+                stamp=str2bytes(stamp)
+                print("lenght: "..#stamp)
               end,
-     ["xc"] = function(x) 
-               --get credential-string for kgh-crc on certain segment
-               --usage: xc <segment-index>
-               print("k "..kghCrcCredentials(inTAG, x))
+    ---
+    -- calculate crc16
+    ["c6"] = function(x) local crc16=string.format("%4.04x", utils.Crc16(x)) 
+                  print(string.sub(crc16, 0,2).." "..string.sub(crc16, 3,4))
+              end,
+    ---
+    -- check & fix segments-crc of all segments
+    ["cc"] = function(x) if (istable(inTAG)) then checkAllSegCrc(inTAG) end end,
+    ---
+    -- set backup-area-bytes to '00'
+    ["cb"] = function(x) 
+                if (istable(inTAG)) then
+                  print(accyan.."purge BackupArea"..acoff)
+                  inTAG=clearBackupArea(inTAG) 
+                end 
+             end, 
+    ---
+    -- check and fix all segments inTAG.SEG[x].kgh toggled 'on'
+    ["ck"] = function(x) if (istable(inTAG)) then checkAllKghCrc(inTAG) end end,
+    ---
+    -- check and fix all segments inTAG.SEG[x].kgh toggled 'on'
+    ["tac"] = function(x) 
+                if (colored_output) then 
+                  colored_output=false
+                else
+                  colored_output=true
+                end 
+                load_colors(colored_output)
               end,
-     ["cc"] = function(x)  if (istable(inTAG)) then checkAllSegCrc(inTAG) end end,
-     ["ck"] = function(x)  if (istable(inTAG)) then checkAllKghCrc(inTAG) end end,
-     ["q"] = function(x)  end,
   }
-  print("modify-modus! enter 'h' for help or 'q' to quit")
   repeat 
+    -- defualt message / prompt
     ic=input("Legic command? ('h' for help - 'q' for quit)", "h")
-    -- command actions
-    if (type(actions[string.lower(string.sub(ic,0,1))])=='function') then
-      actions[string.lower(string.sub(ic,0,1))](string.sub(ic,3))
+    -- command actions decisions (first match, longer commands before shorter)
+    if (type(actions[string.lower(string.sub(ic,0,3))])=='function') then
+      actions[string.lower(string.sub(ic,0,3))](string.sub(ic,5))
     elseif (type(actions[string.lower(string.sub(ic,0,2))])=='function') then
       actions[string.lower(string.sub(ic,0,2))](string.sub(ic,4))
-    else actions['h']('') end
+    elseif (type(actions[string.lower(string.sub(ic,0,1))])=='function') then
+      actions[string.lower(string.sub(ic,0,1))](string.sub(ic,3))
+    else actions.h('') end
   until (string.sub(ic,0,1)=="q")
 end
 
---- main
+---
+-- main function
 function main(args)
+  -- set init colors/switch (can be toggled with 'tac' => 'toggle ansicolors')
+  load_colors(colored_output)
        if (#args == 0 ) then modifyMode() end
   --- variables
-  local inTAG, outTAG, outfile, interactive, crc, ofs, cfs, dfs
+  local inTAG, backupTAG, outTAG, outfile, interactive, crc, ofs, cfs, dfs
        -- just a spacer for better readability
   print()
   --- parse arguments
@@ -1015,25 +2632,28 @@ function main(args)
       print(dumpTag(inTAG))
     end
     bytes=tagToBytes(inTAG)
-    -- xor with given crc
     if (cfs) then 
+      -- xor willl be done in function writeFile
+      -- with the value of byte[5]
       bytes[5]=crc 
     end
     -- write to outfile
     if (bytes) then 
       writeFile(bytes, outfile)
-      -- reed new content into virtual tag 
-      
+      --- read real tag into virtual tag 
+      -- inTAG=readFromPM3() end
+      --- or simply use the bytes that where wriiten
       inTAG=bytesToTag(bytes, inTAG)
       -- show new content
       if (dfs) then  
         print("-----------------------------------------")
-        print(dumpTag(outTAG)) 
+        print(dumpTag(inTAG)) 
       end
     end
   end
   
 end
 
---- start
+---
+-- script start
 main(args)
\ No newline at end of file
Impressum, Datenschutz