Welcome To Our Shell

Mister Spy & Souheyl Bypass Shell

Current Path : /var/www/web-klick.de/dsh/

Linux ift1.ift-informatik.de 5.4.0-216-generic #236-Ubuntu SMP Fri Apr 11 19:53:21 UTC 2025 x86_64
Upload File :
Current File : /var/www/web-klick.de/dsh/posch.lua

--*****************************************************************************

SERIALNUMBER    = '0171393'  
GIT_COMMIT      = '0.99c (14a84b20)'
FIRMWARE_VER    = 'V_16.7_edison'
POINTS_PER_EURO = 0.8  --  1.0: 6,33 Prozent, 0.8: 5,01 Prozent  (190/3000*100*PPE)
HAS_KNOB        = 0
LANGUAGE_FILTER = "DE,EN,UK,FR,ES,IT"
LANGUAGE_FILTER = "DE,EN,UK,FR"

COINVALUE       = { 0.00, 0.10, 0.20, 0.50, 1.00, 2.00, }
CURRENT         = 'EUR'


sec_key = '4f90dc16502e3d4bbf9edb3e083ed715f003869e7b65a27bdf3b30cd0b443157358c7f97fe6f69f52e16156f72c797f18b8d77983bb49a08036293554b42e094'
pub_key = 'd6429ce315db4e73341cdf7976c843ab6f8aa0290f4ba3d73317013570a3e87aaa065082422520b4ddfb35e38141c5ad98c9ad269d12d9f9affcb227b7e478d5'
ak1     = 'aa065082422520b4ddfb35e38141c5ad98c9ad269d12d9f9affcb227b7e478d5'
ak2     = '358c7f97fe6f69f52e16156f72c797f18b8d77983bb49a08036293554b42e094'


--  Parkticket Firmware
--  vgabriel / cgabriel
--  Copyright 31.01.2021 IfT GmbH

---PATCH---

RESET_FLASH = 0
DISABLED    = 0

sec_key1 = sec_key  --  Asymmetrische Signierung des Flash
pub_key1 = pub_key  --  mit EC 25519

sec_key3 = string.sub(sec_key,65,96)  --  RC4 Signierung des Flash
pub_key3 = string.sub(sec_key,65,96)  --  das ist wesentlich schneller

sec_key1 = string.sub(sec_key,33,96)  --  Salsa20 Signierung für Ticketslots
pub_key1 = string.sub(sec_key,33,96)  --  

--*****************************************************************************

--  CORE_END

--*****************************************************************************


function preload_1010()

-- port from golang: https://golang.org/src/time/time.go
--

local secondsPerMinute = 60
local secondsPerHour   = 60 * 60
local secondsPerDay    = 24 * secondsPerHour
local secondsPerWeek   = 7 * secondsPerDay
local daysPer400Years  = 365*400 + 97
local daysPer100Years  = 365*100 + 24
local daysPer4Years    = 365*4 + 1

local unixBase = (1969*365 + 1969//4 - 1969//100 + 1969//400) * secondsPerDay

-- daysBefore[m] counts the number of days in a non-leap year
-- before month m begins. There is an entry for m=12, counting
-- the number of days before January of next year (365).
local daysBefore = {
                0,
                31,
                31 + 28,
                31 + 28 + 31,
                31 + 28 + 31 + 30,
                31 + 28 + 31 + 30 + 31,
                31 + 28 + 31 + 30 + 31 + 30,
                31 + 28 + 31 + 30 + 31 + 30 + 31,
                31 + 28 + 31 + 30 + 31 + 30 + 31 + 31,
                31 + 28 + 31 + 30 + 31 + 30 + 31 + 31 + 30,
                31 + 28 + 31 + 30 + 31 + 30 + 31 + 31 + 30 + 31,
                31 + 28 + 31 + 30 + 31 + 30 + 31 + 31 + 30 + 31 + 30,
                31 + 28 + 31 + 30 + 31 + 30 + 31 + 31 + 30 + 31 + 30 + 31,
}

local January   = 1
local February  = 2
local March     = 3
local April     = 4
local May       = 5
local June      = 6
local July      = 7
local August    = 8
local September = 9
local October   = 10
local November  = 11
local December  = 12

local Sunday    = 1
local Monday    = 2
local Tuesday   = 3
local Wednesday = 4
local Thursday  = 5
local Friday    = 6
local Saturday  = 7

-- absTimestamp returns the absolute second from {year=1, month=1, day=1, hour=0, mintue=0, second=0}
local function absTimestamp(ts)
    return ts + unixBase
end

local function isLeap(year)
    return year%4 == 0 and (year%100 ~= 0 or year%400 == 0)
end

local function absWeekday(abs)
    -- January 1 of the absolute year, like January 1 of 2001, was a Monday.
    local sec = (abs+1*secondsPerDay) % secondsPerWeek
    return (sec // secondsPerDay) + 1
end

-- convert timestamp to year, month, day, yday
local function absDate(abs)
    local day = abs // secondsPerDay

    local n, y
    -- Account for 400 year cycles.
    n = day // daysPer400Years
    y = 400 * n
    day = day - daysPer400Years * n

    -- Cut off 100-year cycles.
    -- The last cycle has one extra leap year, so on the last day
    -- of that year, day / daysPer100Years will be 4 instead of 3.
    -- Cut it back down to 3 by subtracting n>>2.
    n = day // daysPer100Years
    n = n - (n >> 2)
    y = y + 100 *n
    day = day - daysPer100Years * n

    -- Cut off 4-year cycles.
    -- The last cycle has a missing leap year, which does not
    -- affect the computation.
    n = day // daysPer4Years
    y = y + 4 * n
    day = day - daysPer4Years * n

    -- Cut off years within a 4-year cycle.
    -- The last year is a leap year, so on the last day of that year,
    -- day / 365 will be 4 instead of 3. Cut it back down to 3
    -- by subtracting n>>2.
    n = day // 365
    n = n - (n >> 2)
    y = y + n
    day = day - 365 * n

    local year = y + 1
    local yday = day + 1

    if isLeap(year) then
        -- Leap year
        if day > 31+29-1 then
            -- After leap day; pretend it wasn't there.
            day = day - 1
        elseif day == 31+29-1 then
            return year, February, 29, yday
        end
    end

    -- Estimate month on assumption that every month has 31 days.
    -- The estimate may be too low by at most one month, so adjust.
    local month = (day // 31) + 1
    local monthDayEnd = daysBefore[month + 1]
    local monthDayBegin

    if day >= monthDayEnd then
        month = month + 1
        monthDayBegin = monthDayEnd
    else
        monthDayBegin = daysBefore[month]
    end

    day = day - monthDayBegin + 1
    return year, month, day, yday
end

local M = {}

M.TZ = 8
function M.localdate(sec, tz, t)
    local tz = tz or M.TZ
    return M.date(sec + tz*secondsPerHour, t)
end

 function M.date(sec, t)
    local abs = absTimestamp(sec)
    local t = t or {}
    t.year, t.month, t.day, t.yday = absDate(abs)
    t.wday = absWeekday(abs)

    local seconds = abs % secondsPerDay
    t.hour = seconds // secondsPerHour
    seconds = seconds - (t.hour * secondsPerHour)
    t.min = seconds // secondsPerMinute
    t.sec = seconds - (t.min * secondsPerMinute)
    return t
end

function M.date_reverse (year,month,day,hour,min,sec)
    local days = (year-1970)*365 + math.floor((year-1970)/4)
    days       = days + daysBefore[month] + day
    local secs = 86400*days + hour*3600 + min*60 + sec
    return secs
end

function M.date_format (format_string,sec)
    local t = M.date(sec)
    format_string = string.gsub(format_string,'%%y',string.format('%04u',tonumber(t.year)))
    format_string = string.gsub(format_string,'%%m',string.format('%02u',tonumber(t.month)))
    format_string = string.gsub(format_string,'%%d',string.format('%02u',tonumber(t.day)))
    format_string = string.gsub(format_string,'%%H',string.format('%02u',tonumber(t.hour)))
    format_string = string.gsub(format_string,'%%M',string.format('%02u',tonumber(t.min)))
    format_string = string.gsub(format_string,'%%S',string.format('%02u',tonumber(t.sec)))
    return format_string
end

function M.date_format1 (format_string,sec)
    format_string = string.gsub(format_string,'%%y',string.sub(sec,1,4))
    format_string = string.gsub(format_string,'%%m',string.sub(sec,5,6))
    format_string = string.gsub(format_string,'%%d',string.sub(sec,7,8))
    format_string = string.gsub(format_string,'%%H',string.sub(sec,9,10))
    format_string = string.gsub(format_string,'%%M',string.sub(sec,11,12))
    format_string = string.gsub(format_string,'%%S',string.sub(sec,13,14))
    return format_string
end

local tmp = {}

function M.format(sec, utc)
    if utc then
        M.date(sec, tmp)
    else
        M.localdate(sec, nil, tmp)
    end

    return string.format('%4d-%02d-%02d %02d:%02d:%02d',
        tmp.year, tmp.month, tmp.day, tmp.hour, tmp.min, tmp.sec)
end

-- test functions
--
function M.run_random_test()
    for i=1, 100 do
        local ts = math.random(0, 2^31)
        local t1 = os.date('!*t', ts)
        local t2 = M.date(ts)

        for k,v in pairs(t1) do
            if k ~= 'isdst' then
                assert(v == t2[k], k)
            end
        end
    end
end

function M.run_full_test()
    local total = 2^31
    for i=1, total do
        local t1 = os.date('!*t', i)
        local t2 = M.date(i, tmp)

        for k,v in pairs(t1) do
            if k ~= 'isdst' then
                assert(v == t2[k], k)
            end
        end

        if i % 100000 == 0 then
            print('------------------------:', total, i)
        end
    end
end

function M.run_profile_test(times)
    local times = times and times > 0 or 1000000

    local from = math.random(2^31 - times)
    local to = from + times

    local t1 = os.clock()
    for ts = from, to do
        os.date('!*t', ts)
    end

    local t = {}
    local t2 = os.clock()
    for ts = from, to do
        M.date(ts, t)
    end
    local t3 = os.clock()

    print(string.format('%d times call %s cost %s seconds', times, 'os.date', t2-t1))
    print(string.format('%d times call %s cost %s seconds', times, 'M.date', t3-t2))
end

return M



end

date1 = preload_1010()


function preload_1005()

------------------------------------------------------------
-- rc4 encryption / decryption

local byte, char, concat = string.byte, string.char, table.concat


local function keysched(key)
    -- key must be a 16-byte string
    assert(#key == 16)
    local s = {}
    local j,ii,jj
    for i = 0, 255 do s[i+1] = i end
    j = 0
    for i = 0, 255 do
        ii = i+1
        j = (j + s[ii] + byte(key, (i % 16) + 1)) & 0xff
        jj = j+1
        s[ii], s[jj] = s[jj], s[ii]
    end
    return s
end

local function step(s, i, j)
    i = (i + 1) & 0xff
    local ii = i + 1
    j = (j + s[ii]) & 0xff
    local jj = j + 1
    s[ii], s[jj] = s[jj], s[ii]
    local k = s[ ((s[ii] + s[jj]) & 0xff) + 1 ]
    return s, i, j, k
end

local function rc4raw(key, plain)
    -- raw encryption
    -- key must be a 16-byte string
    local s = keysched(key)
    local i, j = 0, 0
    local k
    local t = {}
    for n = 1, #plain do
        s, i, j, k = step(s, i, j)
        t[n] = char(byte(plain, n) ~ k)
    end
    return concat(t)
end

local function rc4(key, plain, drop)
    -- encrypt 'plain', return encrypted text
    -- key must be a 16-byte string
    -- optional drop (default = 256): ignore first 'drop' iterations
    drop = drop or 256
    local s = keysched(key)
    local i, j = 0, 0
    local k
    local t = {}
    -- run and ignore 'drop' iterations
    for _ = 1, drop do
        s, i, j = step(s, i, j)
    end
    -- now start to encrypt
    for n = 1, #plain do
        s, i, j, k = step(s, i, j)
        t[n] = char(byte(plain, n) ~ k)
    end
    return concat(t)
end

return     { -- module
    rc4raw = rc4raw,
    rc4 = rc4,
    encrypt = rc4,
    decrypt = rc4,
    }

end

rc4 = preload_1005()



        
function preload_1006()


local spack, sunpack = string.pack, string.unpack

------------------------------------------------------------------------

local function FF(a, b, c, d, x, s, ac)
    a = (a + ((b & c) | ((~b) & d)) + x + ac) & 0xffffffff
    a = ((a << s) | (a >> (32-s))) & 0xffffffff
    a = (a + b) & 0xffffffff
    return a
end

local function GG(a, b, c, d, x, s, ac)
    a = (a + ((b & d) | c & (~d) ) + x + ac) & 0xffffffff
    a = ((a << s) | (a >> (32-s))) & 0xffffffff
    a = (a + b) & 0xffffffff
    return a
end

local function HH(a, b, c, d, x, s, ac)
    a = (a + ((b ~ c ~ d)) + x + ac) & 0xffffffff
    a = ((a << s) | (a >> (32-s))) & 0xffffffff
    a = (a + b) & 0xffffffff
    return a
end

local function II(a, b, c, d, x, s, ac)
    a = (a + (c ~ (b | ~d)) + x + ac) & 0xffffffff
    a = ((a << s) | (a >> (32-s))) & 0xffffffff
    a = (a + b) & 0xffffffff
    return a
end

local function transform(state, input, i, t)
    -- process the 64-byte input block in string 'input' at offset 'i'
    -- t is a uint32[16] array. It is passed as a parameter
    -- for performance reasons
    --
    local a, b, c, d = state[1], state[2], state[3], state[4]

    -- load array
    for j = 1, 16 do
        t[j] = sunpack('<I4', input, i)
        i = i + 4
    end

    -- Round 1
    a = FF (a, b, c, d, t[ 1], 7, 0xd76aa478)
    d = FF (d, a, b, c, t[ 2], 12, 0xe8c7b756)
    c = FF (c, d, a, b, t[ 3], 17, 0x242070db)
    b = FF (b, c, d, a, t[ 4], 22, 0xc1bdceee)
    a = FF (a, b, c, d, t[ 5], 7, 0xf57c0faf)
    d = FF (d, a, b, c, t[ 6], 12, 0x4787c62a)
    c = FF (c, d, a, b, t[ 7], 17, 0xa8304613)
    b = FF (b, c, d, a, t[ 8], 22, 0xfd469501)
    a = FF (a, b, c, d, t[ 9], 7, 0x698098d8)
    d = FF (d, a, b, c, t[10], 12, 0x8b44f7af)
    c = FF (c, d, a, b, t[11], 17, 0xffff5bb1)
    b = FF (b, c, d, a, t[12], 22, 0x895cd7be)
    a = FF (a, b, c, d, t[13], 7, 0x6b901122)
    d = FF (d, a, b, c, t[14], 12, 0xfd987193)
    c = FF (c, d, a, b, t[15], 17, 0xa679438e)
    b = FF (b, c, d, a, t[16], 22, 0x49b40821)

    -- Round 2
    a = GG (a, b, c, d, t[ 2], 5, 0xf61e2562)
    d = GG (d, a, b, c, t[ 7], 9, 0xc040b340)
    c = GG (c, d, a, b, t[12], 14, 0x265e5a51)
    b = GG (b, c, d, a, t[ 1], 20, 0xe9b6c7aa)
    a = GG (a, b, c, d, t[ 6], 5, 0xd62f105d)
    d = GG (d, a, b, c, t[11], 9,  0x2441453)
    c = GG (c, d, a, b, t[16], 14, 0xd8a1e681)
    b = GG (b, c, d, a, t[ 5], 20, 0xe7d3fbc8)
    a = GG (a, b, c, d, t[10], 5, 0x21e1cde6)
    d = GG (d, a, b, c, t[15], 9, 0xc33707d6)
    c = GG (c, d, a, b, t[ 4], 14, 0xf4d50d87)
    b = GG (b, c, d, a, t[ 9], 20, 0x455a14ed)
    a = GG (a, b, c, d, t[14], 5, 0xa9e3e905)
    d = GG (d, a, b, c, t[ 3], 9, 0xfcefa3f8)
    c = GG (c, d, a, b, t[ 8], 14, 0x676f02d9)
    b = GG (b, c, d, a, t[13], 20, 0x8d2a4c8a)

    -- Round 3
    a = HH (a, b, c, d, t[ 6], 4, 0xfffa3942)
    d = HH (d, a, b, c, t[ 9], 11, 0x8771f681)
    c = HH (c, d, a, b, t[12], 16, 0x6d9d6122)
    b = HH (b, c, d, a, t[15], 23, 0xfde5380c)
    a = HH (a, b, c, d, t[ 2], 4, 0xa4beea44)
    d = HH (d, a, b, c, t[ 5], 11, 0x4bdecfa9)
    c = HH (c, d, a, b, t[ 8], 16, 0xf6bb4b60)
    b = HH (b, c, d, a, t[11], 23, 0xbebfbc70)
    a = HH (a, b, c, d, t[14], 4, 0x289b7ec6)
    d = HH (d, a, b, c, t[ 1], 11, 0xeaa127fa)
    c = HH (c, d, a, b, t[ 4], 16, 0xd4ef3085)
    b = HH (b, c, d, a, t[ 7], 23,  0x4881d05)
    a = HH (a, b, c, d, t[10], 4, 0xd9d4d039)
    d = HH (d, a, b, c, t[13], 11, 0xe6db99e5)
    c = HH (c, d, a, b, t[16], 16, 0x1fa27cf8)
    b = HH (b, c, d, a, t[ 3], 23, 0xc4ac5665)

    -- Round 4
    a = II (a, b, c, d, t[ 1], 6, 0xf4292244)
    d = II (d, a, b, c, t[ 8], 10, 0x432aff97)
    c = II (c, d, a, b, t[15], 15, 0xab9423a7)
    b = II (b, c, d, a, t[ 6], 21, 0xfc93a039)
    a = II (a, b, c, d, t[13], 6, 0x655b59c3)
    d = II (d, a, b, c, t[ 4], 10, 0x8f0ccc92)
    c = II (c, d, a, b, t[11], 15, 0xffeff47d)
    b = II (b, c, d, a, t[ 2], 21, 0x85845dd1)
    a = II (a, b, c, d, t[ 9], 6, 0x6fa87e4f)
    d = II (d, a, b, c, t[16], 10, 0xfe2ce6e0)
    c = II (c, d, a, b, t[ 7], 15, 0xa3014314)
    b = II (b, c, d, a, t[14], 21, 0x4e0811a1)
    a = II (a, b, c, d, t[ 5], 6, 0xf7537e82)
    d = II (d, a, b, c, t[12], 10, 0xbd3af235)
    c = II (c, d, a, b, t[ 3], 15, 0x2ad7d2bb)
    b = II (b, c, d, a, t[10], 21, 0xeb86d391)

    state[1] = (state[1] + a) & 0xffffffff
    state[2] = (state[2] + b) & 0xffffffff
    state[3] = (state[3] + c) & 0xffffffff
    state[4] = (state[4] + d) & 0xffffffff
end --transform()

local function md5(input)
    -- initialize state
    local state = { 0x67452301, 0xefcdab89, 0x98badcfe, 0x10325476 }
    local inputlen = #input
    local inputbits = inputlen * 8 -- input length in bits
    local r = inputlen -- number of unprocessed bytes
    local i = 1 -- index in input string
    local ibt = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0} -- input block uint32[16]
    -- process as many 64-byte blocks as possible
    while r >= 64 do
        -- process block
        transform(state, input, i, ibt)
        i = i + 64 -- update input index
        r = r - 64 -- update number of unprocessed bytes
    end
    -- finalize.  must append to input a mandatory 0x80 byte, some
    --  padding, and the input bit-length ('inputbits')
    local lastblock -- the rest of input .. some padding .. inputbits
    local padlen -- padding length in bytes
    if r < 56 then padlen = 55 - r else padlen = 119 - r end
    lastblock = input:sub(i) -- remaining input
        .. '\x80' .. ('\0'):rep(padlen)  --padding
        .. spack('<I8', inputbits) -- length in bits
    assert(#lastblock == 64 or #lastblock == 128)
    transform(state, lastblock, 1, ibt)
    if #lastblock == 128 then
        transform(state, lastblock, 65, ibt)
    end
    -- return the digest
    local digest = spack('<I4I4I4I4', state[1], state[2], state[3], state[4])
    return digest
end --md5()


--~ print(bin.stohex(md5'abc'))
--~ print(bin.stohex(md5''))

return {  -- md5 module
    hash = md5,
}

end

md5lib = preload_1006()


function preload_1008()



local byte, char, concat = string.byte, string.char, table.concat

local b10chars = '1375629804'

local function encode(s)
    local q, b
    local et = {}
    local zn = 0     -- number of leading zero bytes in s
    -- assume s is a large, little-endian binary number
    -- with base256 digits (each byte is a 'digit')
    local nt = {} -- number to divide in base 256, big endian
    local dt = {} -- result of nt // 10, in base 256
    local more = true  -- used to count leading zero bytes
    for i = 1, #s do
        b = byte(s, i)
        if more and b == 0 then
            zn = zn + 1
        else
            more = false
        end
        nt[i] = b
    end
    if #s == zn then --take care of strings empty or with only nul bytes
        return string.rep('1', zn)
    end
    more = true
    while more do
        local r = 0
        more = false
        for i = 1, #nt do
            b = nt[i] + (256 * r)
            q = b // 10
            -- if q is not null at least once, we are good
            -- for another division by 10
            more = more or q > 0
            r = b % 10
            dt[i] = q
        end
        -- r is the next base10 digit. insert it before previous ones
        -- to get a big-endian base10 number
        table.insert(et, 1, char(byte(b10chars, r+1)))
        -- now copy dt into nt before another round of division by 10
        nt = {}
        for i = 1, #dt do nt[i] = dt[i] end
        dt = {}
    end--while
    -- don't forget the leading zeros ('1' is digit 0 in bitcoin base10 alphabet)
    return string.rep('1', zn) .. concat(et)
end --encode()

-- inverse base10 map, used by b10decode:  b10charmap maps characters in
-- base10 alphabet to their _offset_ in b10chars (0-based, not 1-based...)
--  eg.    for digit '1' b64charmap[65] == 0  and for 'z', b64charmap[122] == 57
--
local b10charmap = {};
for i = 1, 10 do b10charmap[byte(b10chars, i)] = i - 1  end

local function decode(s)
    -- reject invalid encoded strings
    if string.find(s, '[^'..b10chars..']') then
        return nil, 'invalid char'
    end
    -- process leading zeros - count and remove them
    local zn -- number of leading zeros (base10 digits '1')
    zn = #(string.match(s, '^(1+)') or '')
    s = string.gsub(s, '^(1+)', '')
    -- special case: the string is empty or contains only null bytes
    if s == '' then
        return string.rep('\x00', zn)
    end
    --
    -- process significant digits
    local dn -- decoded number as an array of bytes (little-endian)
    local d -- base10 digit, as an integer
    local b -- a byte in dn
    local m -- a byte multiplied by 10 (used for product)
    local carry
    dn = { b10charmap[byte(s, 1)] } --init with most significant digit
    for i = 2, #s do --repeat until no more digits
        -- multiply dn by 10, then add next digit
        d = b10charmap[byte(s, i)] -- next digit
        carry = 0
        -- multiply dn by 10
        for j = 1, #dn do
            b = dn[j]
            m = b * 10 + carry
            b = m & 0xff
            carry = m >> 8
            dn[j] = b
        end
        if carry > 0 then dn[#dn + 1] = carry end
        -- add next digit to dn
        carry = d
        for j = 1, #dn do
            b = dn[j] + carry
            carry = b >> 8
            dn[j] = b & 0xff
        end
        if carry > 0 then dn[#dn + 1] = carry end
    end
    -- now dn contains the decoded number (little endian)
    -- must add leading zeros and reverse dn to build binary string
    local ben = {} -- big-endian number as array of chars
    local ln = #dn
    for i = 1, ln do
        ben[i] = char(dn[ln-i+1])
    end
    return string.rep('\x00', zn) .. concat(ben)
end --decode()


return { -- base10 module
    encode = encode,
    decode = decode,
    }

end

base10 = preload_1008()




function preload_1008a()


local byte, char, concat = string.byte, string.char, table.concat

local b27chars = 'ABCDE'..'FGHJK'..'LMNPQ'..'RSTWX'..'Y'..'345679'

local function encode(s)
	local q, b
	local et = {}
	local zn = 0 	-- number of leading zero bytes in s
	-- assume s is a large, little-endian binary number
	-- with base256 digits (each byte is a 'digit')
	local nt = {} -- number to divide in base 256, big endian
	local dt = {} -- result of nt // 27, in base 256
	local more = true  -- used to count leading zero bytes
	for i = 1, #s do
		b = byte(s, i)
		if more and b == 0 then
			zn = zn + 1
		else
			more = false
		end
		nt[i] = b
	end
	if #s == zn then --take care of strings empty or with only nul bytes
		return string.rep('1', zn)
	end
	more = true
	while more do
		local r = 0
		more = false
		for i = 1, #nt do
			b = nt[i] + (256 * r)
			q = b // 27
			-- if q is not null at least once, we are good
			-- for another division by 27
			more = more or q > 0
			r = b % 27
			dt[i] = q
		end
		-- r is the next base27 digit. insert it before previous ones
		-- to get a big-endian base27 number
		table.insert(et, 1, char(byte(b27chars, r+1)))
		-- now copy dt into nt before another round of division by 27
		nt = {}
		for i = 1, #dt do nt[i] = dt[i] end
		dt = {}
	end--while
	-- don't forget the leading zeros ('1' is digit 0 in bitcoin base27 alphabet)
	return string.rep('1', zn) .. concat(et)
end --encode()

-- inverse base27 map, used by b27decode:  b27charmap maps characters in
-- base27 alphabet to their _offset_ in b27chars (0-based, not 1-based...)
--  eg.	for digit '1' b64charmap[65] == 0  and for 'z', b64charmap[122] == 57
--
local b27charmap = {};
for i = 1, 27 do b27charmap[byte(b27chars, i)] = i - 1  end

local function decode(s)
	-- reject invalid encoded strings
	if string.find(s, '[^'..b27chars..']') then
		return nil, 'invalid char'
	end
	-- process leading zeros - count and remove them
	local zn -- number of leading zeros (base27 digits '1')
	zn = #(string.match(s, '^(1+)') or '')
	s = string.gsub(s, '^(1+)', '')
	-- special case: the string is empty or contains only null bytes
	if s == '' then
		return string.rep('\x00', zn)
	end
	--
	-- process significant digits
	local dn -- decoded number as an array of bytes (little-endian)
	local d -- base27 digit, as an integer
	local b -- a byte in dn
	local m -- a byte multiplied by 27 (used for product)
	local carry
	dn = { b27charmap[byte(s, 1)] } --init with most significant digit
	for i = 2, #s do --repeat until no more digits
		-- multiply dn by 27, then add next digit
		d = b27charmap[byte(s, i)] -- next digit
		carry = 0
		-- multiply dn by 27
		for j = 1, #dn do
			b = dn[j]
			m = b * 27 + carry
			b = m & 0xff
			carry = m >> 8
			dn[j] = b
		end
		if carry > 0 then dn[#dn + 1] = carry end
		-- add next digit to dn
		carry = d
		for j = 1, #dn do
			b = dn[j] + carry
			carry = b >> 8
			dn[j] = b & 0xff
		end
		if carry > 0 then dn[#dn + 1] = carry end
	end
	-- now dn contains the decoded number (little endian)
	-- must add leading zeros and reverse dn to build binary string
	local ben = {} -- big-endian number as array of chars
	local ln = #dn
	for i = 1, ln do
		ben[i] = char(dn[ln-i+1])
	end
	return string.rep('\x00', zn) .. concat(ben)
end --decode()


return { -- base27 module
	encode = encode,
	decode = decode,
	}

end


base27 = preload_1008a()



--*****************************************************************************

--  CORE_START

--*****************************************************************************
--core


function preload_1001()


function preload_1002()


local app, concat = table.insert, table.concat

------------------------------------------------------------

-- salsa quarter round (rotl inlined)
local function qround(st,x,y,z,w)
    -- st is a salsa state: an array of 16 u32 words
    -- x,y,z,w are indices in st
    local a, b, c, d = st[x], st[y], st[z], st[w]
    local t
    t = (a + d) & 0xffffffff
    -- b = b ~ rotl32(t, 7)
    b = b ~ ((t << 7) | (t >> 25)) & 0xffffffff
    t = (b + a) & 0xffffffff
    -- c = c ~ rotl32(t, 9)
    c = c ~ ((t << 9) | (t >> 23)) & 0xffffffff
    t = (c + b) & 0xffffffff
    -- d = d ~ rotl32(t, 13)
    d = d ~ ((t << 13) | (t >> 19)) & 0xffffffff
    t = (d + c) & 0xffffffff
    -- a = a ~ rotl32(t, 18)
    a = a ~ ((t << 18) | (t >> 14)) & 0xffffffff
    st[x], st[y], st[z], st[w] = a, b, c, d
    return st
end



-- salsa20 state and working state are allocated once and reused
-- by each invocation of salsa20_block()
local salsa20_state = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}
local salsa20_working_state = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}

local salsa20_block = function(key, counter, nonce)
    -- key: u32[8]
    -- counter: u32[2]
    -- nonce: u32[2]
    local st = salsa20_state         -- state
    local wst = salsa20_working_state     -- working state
    -- initialize state
    st[1], st[6], st[11], st[16] =
        0x61707865, 0x3320646e, 0x79622d32, 0x6b206574
    for i = 1, 4 do
        st[i+1] = key[i]
        st[i+11] = key[i+4]
    end
    st[7], st[8], st[9], st[10] = nonce[1], nonce[2], counter[1], counter[2]
    -- copy state to working_state
    for i = 1, 16 do wst[i] = st[i] end
    -- run 20 rounds, ie. 10 iterations of 8 quarter rounds
    for _ = 1, 10 do           --RFC reference:
        qround(wst, 1,5,9,13)    --1.  QUARTERROUND ( 0, 4, 8,12)
        qround(wst, 6,10,14,2)   --2.  QUARTERROUND ( 5, 9,13, 1)
        qround(wst, 11,15,3,7)   --3.  QUARTERROUND (10,14, 2, 6)
        qround(wst, 16,4,8,12)   --4.  QUARTERROUND (15, 3, 7,11)
        qround(wst, 1,2,3,4)     --5.  QUARTERROUND ( 0, 1, 2, 3)
        qround(wst, 6,7,8,5)     --6.  QUARTERROUND ( 5, 6, 7, 4)
        qround(wst, 11,12,9,10)  --7.  QUARTERROUND (10,11, 8, 9)
        qround(wst, 16,13,14,15) --8.  QUARTERROUND (15,12,13,14)
    end
    -- add working_state to state
    for i = 1, 16 do st[i] = (st[i] + wst[i]) & 0xffffffff end
    -- return st, an array of 16 u32 words used as a keystream
    return st
end --salsa20_block()

local function hsalsa20_block(key, counter, nonce)
    local st = salsa20_block(key, counter, nonce)
    return {
        (st[1] - 0x61707865) & 0xffffffff,
        (st[6] - 0x3320646e) & 0xffffffff,
        (st[11] - 0x79622d32) & 0xffffffff,
        (st[16] - 0x6b206574) & 0xffffffff,
        (st[7] - nonce[1]) & 0xffffffff,
        (st[8] - nonce[2]) & 0xffffffff,
        (st[9] - counter[1]) & 0xffffffff,
        (st[10] - counter[2]) & 0xffffffff,
    }
end

-- pat16: used to unpack a 64-byte string as 16 uint32 and vice versa
local pat16 = '<I4I4I4I4I4I4I4I4I4I4I4I4I4I4I4I4'

-- pat8: used to unpack a 32-byte string as 8 uint32 and vice versa
local pat8 = '<I4I4I4I4I4I4I4I4'

local function salsa20_encrypt_block(key, counter, nonce, pt, ptidx)
    -- encrypt a 64-byte block of plain text.
    -- key: 32 bytes as an array of 8 uint32
    -- counter: 8 bytes as an array of 2 uint32
    -- nonce: 8 bytes as an array of 2 uint32
    -- pt: plain text string,
    -- ptidx: index of beginning of block in plain text (origin=1)
    -- if less than 64 bytes are left at position ptidx, it is padded
    --    with null bytes before encryption and result is stripped
    --    accordingly.
    -- return encrypted block as a string  (length <= 16)
    local rbn = #pt - ptidx + 1 -- number of remaining bytes in pt
    if rbn < 64 then
        local tmp = string.sub(pt, ptidx)
        pt = tmp .. string.rep('\0', 64 - rbn) --pad last block
        ptidx = 1
    end
    assert(#pt >= 64)
    local ba = table.pack(string.unpack(pat16, pt, ptidx))
    local keystream = salsa20_block(key, counter, nonce)
    for i = 1, 16 do
        ba[i] = ba[i] ~ keystream[i]
    end
    local es = string.pack(pat16, table.unpack(ba))
    if rbn < 64 then
        es = string.sub(es, 1, rbn)
    end
    return es
end --salsa20_encrypt_block

local salsa20_encrypt = function(key, counter, nonce, pt)
    -- encrypt plain text 'pt', return encrypted text
    -- key: 32 bytes as a string
    -- counter: an uint64 (must be incremented for each block)
    -- nonce: 8 bytes as a string
    -- pt: plain text string
    assert(#key == 32, '#key must be 32')
    assert(#nonce == 8, '#nonce must be 8')
    local keya = table.pack(string.unpack('<I4I4I4I4I4I4I4I4', key))
    local noncea = table.pack(string.unpack('<I4I4', nonce))
    local countera = {counter & 0xffffffff, counter >> 32}
    local t = {} -- used to collect all encrypted blocks
    local ptidx = 1
    while ptidx <= #pt do
--        feed_the_dog()
        app(t, salsa20_encrypt_block(keya, countera, noncea, pt, ptidx))
        ptidx = ptidx + 64
        countera[1] = countera[1] + 1
        if countera[1] > 0xffffffff then
            countera[1] = 0
            countera[2] = countera[2] + 1
        end
    end
    return (concat(t))
end --salsa20_encrypt()

local function salsa20_stream(key, counter, nonce, length)
    assert(#key == 32, '#key must be 32')
    assert(#nonce == 8, '#nonce must be 8')
    local keya = table.pack(string.unpack('<I4I4I4I4I4I4I4I4', key))
    local noncea = table.pack(string.unpack('<I4I4', nonce))
    local countera = {counter & 0xffffffff, counter >> 32}
    local t = {} -- used to collect all encrypted blocks
    while length > 0 do
--        feed_the_dog()
        local keystream = salsa20_block(keya, countera, noncea)
        local block = string.pack(pat16, table.unpack(keystream))
        if length <= 64 then block = block:sub(1, length) end
        app(t, block)
        length = length - 64
        countera[1] = countera[1] + 1
        if countera[1] > 0xffffffff then
            countera[1] = 0
            countera[2] = countera[2] + 1
        end
    end
    return (concat(t))
end

local hsalsa20 = function(key, counter, nonce)
    assert(#key == 32, '#key must be 32')
    assert(#nonce == 8, '#nonce must be 8')
    local keya = table.pack(string.unpack('<I4I4I4I4I4I4I4I4', key))
    local noncea = table.pack(string.unpack('<I4I4', nonce))
    local countera = {counter & 0xffffffff, counter >> 32}
    local stream = hsalsa20_block(keya, countera, noncea)
    return string.pack(pat8, table.unpack(stream))
end

------------------------------------------------------------
return {
    encrypt = salsa20_encrypt,
    decrypt = salsa20_encrypt,
    stream = salsa20_stream,
    hsalsa20 = hsalsa20,
    --
    key_size = 32,
    nonce_size = 8,
    }

--end of salsa20

end


salsa20 = preload_1002()

        
function preload_1003()


------------------------------------------------------------

-- set25519() not used

local function car25519(o)
    local c
    for i = 1, 16 do
        o[i] = o[i] + 65536 -- 1 << 16
        -- lua '>>' doesn't perform sign extension...
        -- so the following >>16 doesn't work with negative numbers!!
        -- ...took a bit of time to find this one :-)
        -- c = o[i] >> 16
        c = o[i] // 65536
        if i < 16 then
            o[i+1] = o[i+1] + (c - 1)
        else
            o[1] = o[1] + 38 * (c - 1)
        end
        o[i] = o[i] - (c << 16)
    end
end --car25519()

local function sel25519(p, q, b)
    local c = ~(b-1)
    local t
    for i = 1, 16 do
        t = c & (p[i] ~ q[i])
        p[i] = p[i] ~ t
        q[i] = q[i] ~ t
    end
end --sel25519

local function pack25519(o, n)
    -- out o[32], in n[16]
    local m, t = {}, {}
    local b
    for i = 1, 16 do t[i] = n[i] end
    car25519(t)
    car25519(t)
    car25519(t)
    for _ = 1, 2 do
        m[1] = t[1] - 0xffed
        for i = 2, 15 do
            m[i] = t[i] - 0xffff - ((m[i-1] >> 16) & 1)
            m[i-1] = m[i-1] & 0xffff
        end
        m[16] = t[16] - 0x7fff - ((m[15] >> 16) & 1)
        b = (m[16] >> 16) & 1
        m[15] = m[15] & 0xffff
        sel25519(t, m, 1-b)
    end
    for i = 1, 16 do
        o[2*i-1] = t[i] & 0xff
        o[2*i] = t[i] >> 8
    end
end -- pack25519

-- neq25519() not used
-- par25519() not used

local function unpack25519(o, n)
    -- out o[16], in n[32]
    for i = 1, 16 do
        o[i] = n[2*i-1] + (n[2*i] << 8)
    end
    o[16] = o[16] & 0x7fff
end -- unpack25519

local function A(o, a, b) --add
    for i = 1, 16 do o[i] = a[i] + b[i] end
end

local function Z(o, a, b) --sub
    for i = 1, 16 do o[i] = a[i] - b[i] end
end

local function M(o, a, b) --mul  gf, gf -> gf
    local t = {}
    for i = 1, 32 do t[i] = 0  end
    for i = 1, 16 do
        for j = 1, 16 do
            t[i+j-1] = t[i+j-1] + (a[i] * b[j])
        end
    end
    for i = 1, 15 do t[i] = t[i] + 38 * t[i+16] end
    for i = 1, 16 do o[i] = t[i] end
    car25519(o)
    car25519(o)
end

local function S(o, a)  --square
    M(o, a, a)
end

local function inv25519(o, i)
    local c = {}
    for a = 1, 16 do c[a] = i[a] end
    for a = 253, 0, -1 do
        S(c, c)
        if a ~= 2 and a ~= 4 then M(c, c, i) end
    end
    for a = 1, 16 do o[a] = c[a] end
--~     pt(o)
end

--pow2523() not used

local t_121665 = {0xDB41,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0}

local function crypto_scalarmult(q, n, p)
    -- out q[], in n[], in p[]
    local z = {}
    local x = {}
    local a = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}
    local b = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}
    local c = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}
    local d = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}
    local e = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}
    local f = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}
    for i = 1, 31 do z[i] = n[i] end
    z[32] = (n[32] & 127) | 64
    z[1] = z[1] & 248
--~     pt(z)
    unpack25519(x, p)
--~     pt(x)
    for i = 1, 16 do
        b[i] = x[i]
        a[i] = 0
        c[i] = 0
        d[i] = 0
    end
    a[1] = 1
    d[1] = 1
    for i = 254, 0, -1 do              --  urspruenglich 254
        if i%12 == 0 and file_open ~= nil then
            start_timer(26,WAIT_26,0,1)
            coroutine.yield()
        end
        local r = (z[(i>>3)+1] >> (i & 7)) & 1
        sel25519(a,b,r)
        sel25519(c,d,r)
        A(e,a,c)
        Z(a,a,c)
        A(c,b,d)
        Z(b,b,d)
        S(d,e)
        S(f,a)
        M(a,c,a)
        M(c,b,e)
        A(e,a,c)
        Z(a,a,c)
        S(b,a)
        Z(c,d,f)
        M(a,c,t_121665)
        A(a,a,d)
        M(c,c,a)
        M(a,d,f)
        M(d,b,x)
        S(b,e)
        sel25519(a,b,r)
        sel25519(c,d,r)
    end
    for i = 1, 16 do
        x[i+16] = a[i]
        x[i+32] = c[i]
        x[i+48] = b[i]
        x[i+64] = d[i]
    end
    -- cannot use pointer arithmetics...
    local x16, x32 = {}, {}
    for i = 1, #x do
        if i > 16 then x16[i-16] = x[i] end
        if i > 32 then x32[i-32] = x[i] end
    end
    inv25519(x32,x32)
    M(x16,x16,x32)
    pack25519(q,x16)
    return 0
end -- crypto_scalarmult

local t_9 = { -- u8 * 32
    9,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
    0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
    }

local function crypto_scalarmult_base(q, n)
    -- out q[], in n[]
    return crypto_scalarmult(q, n, t_9)
end

------------------------------------------------------------------------
-- convenience function (using binary strings instead of byte tables)
--
-- curve point and scalars are represented as 32-byte binary strings
-- (encoded as little endian)

local function scalarmult(n, p)
    -- n, a scalar (little endian) as a 32-byte string
    -- p, a curve point as a 32-byte string
    -- return the scalar product np as a 32-byte string
    local qt, nt, pt = {}, {}, {} 
    for i = 1, 32 do 
        nt[i] = string.byte(n, i) 
        pt[i] = string.byte(p, i) 
    end
    crypto_scalarmult(qt, nt, pt)
    local q = string.char(table.unpack(qt))
    return q
end

-- base: the curve point generator = 9

local base = '\9' .. ('\0'):rep(31)


return {
    crypto_scalarmult   = crypto_scalarmult,
    crypto_scalarmult_base = crypto_scalarmult_base,
    --
    -- convenience function and definition
    --
    scalarmult = scalarmult,
    base = base,
    --
}
 -- end of ec25519 module


end

ec25519 = preload_1003()


        
function preload_1004()


-----------------------------------------------------------
-- poly1305

local sunp = string.unpack

local function poly_init(k)
    -- k: 32-byte key as a string
    -- initialize internal state
    local st = {
        r = {
            (sunp('<I4', k,  1)     ) & 0x3ffffff,  --r0
            (sunp('<I4', k,  4) >> 2) & 0x3ffff03,  --r1
            (sunp('<I4', k,  7) >> 4) & 0x3ffc0ff,  --r2
            (sunp('<I4', k, 10) >> 6) & 0x3f03fff,  --r3
            (sunp('<I4', k, 13) >> 8) & 0x00fffff,  --r4
        },
        h = { 0,0,0,0,0 },
        pad = {    sunp('<I4', k, 17),  -- 's' in rfc
            sunp('<I4', k, 21),
            sunp('<I4', k, 25),
            sunp('<I4', k, 29),
        },
        buffer = '', --
        leftover = 0,
        final = false,
    }--st
    return st
end --poly_init()

local function poly_blocks(st, m)
    -- st: internal state
    -- m: message:string
    local bytes = #m
    local midx = 1
    local hibit = st.final and 0 or 0x01000000 -- 1 << 24
    local r0 = st.r[1]
    local r1 = st.r[2]
    local r2 = st.r[3]
    local r3 = st.r[4]
    local r4 = st.r[5]
    local s1 = r1 * 5
    local s2 = r2 * 5
    local s3 = r3 * 5
    local s4 = r4 * 5
    local h0 = st.h[1]
    local h1 = st.h[2]
    local h2 = st.h[3]
    local h3 = st.h[4]
    local h4 = st.h[5]
    local d0, d1, d2, d3, d4, c
    --
    while bytes >= 16 do  -- 16 = poly1305_block_size
        -- h += m[i]  (in rfc:  a += n with 0x01 byte)
        h0 = h0 + ((sunp('<I4', m, midx     )     ) & 0x3ffffff)
        h1 = h1 + ((sunp('<I4', m, midx +  3) >> 2) & 0x3ffffff)
        h2 = h2 + ((sunp('<I4', m, midx +  6) >> 4) & 0x3ffffff)
        h3 = h3 + ((sunp('<I4', m, midx +  9) >> 6) & 0x3ffffff)
        h4 = h4 + ((sunp('<I4', m, midx + 12) >> 8) | hibit)--0x01 byte
        --
        -- h *= r % p (partial)
        d0 = h0*r0 + h1*s4 + h2*s3 + h3*s2 + h4*s1
        d1 = h0*r1 + h1*r0 + h2*s4 + h3*s3 + h4*s2
        d2 = h0*r2 + h1*r1 + h2*r0 + h3*s4 + h4*s3
        d3 = h0*r3 + h1*r2 + h2*r1 + h3*r0 + h4*s4
        d4 = h0*r4 + h1*r3 + h2*r2 + h3*r1 + h4*r0
        --
                      c = (d0>>26) & 0xffffffff ; h0 = d0 & 0x3ffffff
        d1 = d1 + c ; c = (d1>>26) & 0xffffffff ; h1 = d1 & 0x3ffffff
        d2 = d2 + c ; c = (d2>>26) & 0xffffffff ; h2 = d2 & 0x3ffffff
        d3 = d3 + c ; c = (d3>>26) & 0xffffffff ; h3 = d3 & 0x3ffffff
        d4 = d4 + c ; c = (d4>>26) & 0xffffffff ; h4 = d4 & 0x3ffffff
        h0 = h0 + (c*5) ; c = h0>>26 ; h0 = h0 & 0x3ffffff
        h1 = h1 + c
        --
        midx = midx + 16 -- 16 = poly1305_block_size
        bytes = bytes - 16
    end --while
    st.h[1] = h0
    st.h[2] = h1
    st.h[3] = h2
    st.h[4] = h3
    st.h[5] = h4
    st.bytes = bytes -- remaining bytes. must be < 16 here
    st.midx = midx -- index of first remaining bytes
    return st
end --poly_blocks()

local function poly_update(st, m)
    -- st: internal state
    -- m: message:string
    st.bytes, st.midx = #m, 1
    -- process full blocks if any
    if st.bytes >= 16 then
        poly_blocks(st, m)
    end
    --handle remaining bytes
    if st.bytes == 0 then -- no bytes left
        -- nothing to do? no add 0x01? - apparently not.
    else
        local buffer =     string.sub(m, st.midx)
            .. '\x01' .. string.rep('\0', 16 - st.bytes -1)
        assert(#buffer == 16)
        st.final = true  -- this is the last block
--~         p16(buffer)
        poly_blocks(st, buffer)
    end
    --
    return st
end --poly_update

local function poly_finish(st)
    --
    local c, mask     --u32
    local f      --u64
    -- fully carry h
    local h0 = st.h[1]
    local h1 = st.h[2]
    local h2 = st.h[3]
    local h3 = st.h[4]
    local h4 = st.h[5]
    --
                 c = h1 >> 26; h1 = h1 & 0x3ffffff
    h2 = h2 +     c; c = h2 >> 26; h2 = h2 & 0x3ffffff
    h3 = h3 +     c; c = h3 >> 26; h3 = h3 & 0x3ffffff
    h4 = h4 +     c; c = h4 >> 26; h4 = h4 & 0x3ffffff
    h0 = h0 + (c*5); c = h0 >> 26; h0 = h0 & 0x3ffffff
    h1 = h1 + c
    --
    --compute h + -p
    local g0 = (h0 + 5) ; c = g0 >> 26; g0 = g0 & 0x3ffffff
    local g1 = (h1 + c) ; c = g1 >> 26; g1 = g1 & 0x3ffffff
    local g2 = (h2 + c) ; c = g2 >> 26; g2 = g2 & 0x3ffffff
    local g3 = (h3 + c) ; c = g3 >> 26; g3 = g3 & 0x3ffffff
    local g4 = (h4 + c - 0x4000000) &0xffffffff  -- (1 << 26)
    --
    -- select h if h < p, or h + -p if h >= p
    mask = ((g4 >> 31) -1) & 0xffffffff
    --
    g0 = g0 & mask
    g1 = g1 & mask
    g2 = g2 & mask
    g3 = g3 & mask
    g4 = g4 & mask
    --
    mask = (~mask)  & 0xffffffff
    h0 = (h0 & mask) | g0
    h1 = (h1 & mask) | g1
    h2 = (h2 & mask) | g2
    h3 = (h3 & mask) | g3
    h4 = (h4 & mask) | g4
    --
    --h = h % (2^128)
    h0 = ((h0      ) | (h1 << 26)) & 0xffffffff
    h1 = ((h1 >>  6) | (h2 << 20)) & 0xffffffff
    h2 = ((h2 >> 12) | (h3 << 14)) & 0xffffffff
    h3 = ((h3 >> 18) | (h4 <<  8)) & 0xffffffff
    --
    -- mac = (h + pad) % (2^128)
    f = h0 + st.pad[1]             ; h0 = f & 0xffffffff
    f = h1 + st.pad[2] + (f >> 32) ; h1 = f & 0xffffffff
    f = h2 + st.pad[3] + (f >> 32) ; h2 = f & 0xffffffff
    f = h3 + st.pad[4] + (f >> 32) ; h3 = f & 0xffffffff
    --
    local mac = string.pack('<I4I4I4I4', h0, h1, h2, h3)
    -- (should zero out the state?)
    --
    return mac
end --poly_finish()

local function poly_auth(m, k)
    -- m: msg string
    -- k: key string (must be 32 bytes)
    -- return mac 16-byte string
    assert(#k == 32)
    local st = poly_init(k)
    poly_update(st, m)
    local mac = poly_finish(st)
    return mac
end --poly_auth()

local function poly_verify(m, k, mac)
    local macm = poly_auth(m, k)
    return macm == mac
end --poly_verify()

------------------------------------------------------------
-- return poly1305 module

return {
    init = poly_init,
    update = poly_update,
    finish = poly_finish,
    auth = poly_auth,
    verify = poly_verify,
    }

end

local poly1305 = preload_1004()


local function public_key(sk)
    assert(type(sk) == 'string', 'sk must be a string')
    assert(#sk == 32, '#sh must be 32')
    sk = table.pack(sk:byte(1, 32))
    local pk = {}
    ec25519.crypto_scalarmult_base(pk, sk)
    return string.char(table.unpack(pk))
end

local function unpack_nonce(nonce)
    assert(#nonce == 24, '#nonce must be 24')
    local nonce1 = nonce:sub(1, 8)
    local counter = string.unpack('<I8', nonce:sub(9,16))
    local nonce2 = nonce:sub(17,24)
    return counter, nonce1, nonce2
end

local function secretbox(pt, nonce, key)
    assert(#key == 32, '#key must be 32')
    local counter, nonce1, nonce2 = unpack_nonce(nonce)
    local key2 = salsa20.hsalsa20(key, counter, nonce1)
    local et = salsa20.encrypt(key2, 0, nonce2, string.rep('\0', 32) .. pt)
    local key3 = et:sub(1, 32)
    et = et:sub(33)
    local mac = poly1305.auth(et, key3)
    return mac .. et
end

local function secretbox_open(et, nonce, key)
    assert(#key == 32, '#key must be 32')
    assert(#et >= 16, '#et must be at least 16')
    local counter, nonce1, nonce2 = unpack_nonce(nonce)
    local key2 = salsa20.hsalsa20(key, counter, nonce1)
    local key3 = salsa20.stream(key2, 0, nonce2, 32)
    local mac = et:sub(1, 16)
    local mac2 = poly1305.auth(et:sub(17), key3)
    if mac2 ~= mac then return nil, 'invalid MAC' end
    local pt = salsa20.encrypt(key2, 0, nonce2, string.rep('\0', 16) .. et)
    return pt:sub(33)
end

local function stream_key(pk, sk)
    assert(#pk == 32, '#pk must be 32')
    assert(#sk == 32, '#pk must be 32')
    pk = table.pack(pk:byte(1, 32))
    sk = table.pack(sk:byte(1, 32))
    local k = {}
    ec25519.crypto_scalarmult(k, sk, pk)
    k = string.char(table.unpack(k))
    return salsa20.hsalsa20(k, 0, string.rep('\0', 8))
 end

local function box(pt, nonce, pk_b, sk_a)
    return secretbox(pt, nonce, stream_key(pk_b, sk_a))
end

local function box_open(et, nonce, pk_a, sk_b)
    return secretbox_open(et, nonce, stream_key(pk_a, sk_b))
end

return {
    public_key = public_key,
    secretbox = secretbox,
    secretbox_open = secretbox_open,
    stream_key = stream_key,
    box = box,
    box_open = box_open,
}

end


crypto = preload_1001()


        
function preload_1009()

-- some local definitions

local strf = string.format
local byte, char = string.byte, string.char
local spack, sunpack = string.pack, string.unpack

local app, concat = table.insert, table.concat

local transform_table = {

    ['0']    = '0000',
    ['1']    = '0001',
    ['2']    = '0010',
    ['3']    = '0011',
    ['4']    = '0100',
    ['5']    = '0101',
    ['6']    = '0110',
    ['7']    = '0111',
    ['8']    = '1000',
    ['9']    = '1001',
    ['a']    = '1010',
    ['b']    = '1011',
    ['c']    = '1100',
    ['d']    = '1101',
    ['e']    = '1110',
    ['f']    = '1111',
    
    ['0000'] = '0',
    ['0001'] = '1',
    ['0010'] = '2',
    ['0011'] = '3',
    ['0100'] = '4',
    ['0101'] = '5',
    ['0110'] = '6',
    ['0111'] = '7',
    ['1000'] = '8',
    ['1001'] = '9',
    ['1010'] = 'a',
    ['1011'] = 'b',
    ['1100'] = 'c',
    ['1101'] = 'd',
    ['1110'] = 'e',
    ['1111'] = 'f'   }
    

------------------------------------------------------------------------

local function bintohex (h)

    local text = {}
    h          = table.concat(h)
    while h ~= '' do
        h1 = string.sub(h,1,4)
        h  = string.sub(h,5)
        table.insert(text,transform_table[h1])
    end
    return table.concat(text)
    
end

------------------------------------------------------------------------

local function hextobin (h)

    local text = {}
    while h ~= '' do
        h1 = string.sub(h,1,1)
        h  = string.sub(h,2)
        local x = transform_table[h1]
        table.insert(text,string.sub(x,1,1))
        table.insert(text,string.sub(x,2,2))
        table.insert(text,string.sub(x,3,3))
        table.insert(text,string.sub(x,4,4))
    end
    return text
    
end
    
------------------------------------------------------------------------


local function stohex(s, ln, sep)
    -- stohex(s [, ln [, sep)
    -- return the hex encoding of string s
    -- ln: (optional) a newline is inserted after 'ln' bytes
    --    ie. after 2*ln hex digits. Defaults to no newlines.
    -- sep: (optional) separator between bytes in the encoded string
    --    defaults to nothing (if ln is nil, sep is ignored)
    -- example:
    --    stohex('abcdef', 4, ':') => '61:62:63:64\n65:66'
    --    stohex('abcdef') => '616263646566'
    --
    if #s == 0 then return '' end
    if not ln then -- no newline, no separator: do it the fast way!
        return (s:gsub('.',
            function(c) return strf('%02x', byte(c)) end
            ))
    end
    sep = sep or '' -- optional separator between each byte
    local t = {}
    for i = 1, #s - 1 do
        t[#t + 1] = strf('%02x%s', s:byte(i),
                (i % ln == 0) and '\n' or sep)
    end
    -- last byte, without any sep appended
    t[#t + 1] = strf('%02x', s:byte(#s))
    return concat(t)
end --stohex()

local function hextos(hs, unsafe)
    -- decode an hex encoded string. return the decoded string
    -- if optional parameter unsafe is defined, assume the hex
    -- string is well formed (no checks, no whitespace removal).
    -- Default is to remove white spaces (incl newlines)
    -- and check that the hex string is well formed
--    local tonumber = tonumber

        local function tonumber1 (c, base)
        
            local nr = 0
            while 0 == 0 do
                nr = nr * 16
                local c1 = string.sub(c,1,1)
                if     c1 == 'a' or c1 == 'A' then nr = nr + 10
                elseif c1 == 'b' or c1 == 'B' then nr = nr + 11
                elseif c1 == 'c' or c1 == 'C' then nr = nr + 12
                elseif c1 == 'd' or c1 == 'D' then nr = nr + 13
                elseif c1 == 'e' or c1 == 'E' then nr = nr + 14
                elseif c1 == 'f' or c1 == 'F' then nr = nr + 15
                else nr = nr + tonumber(c1) end
                if #c == 1 then
                    break
                end
                c = string.sub(c,2)
            end
            return nr
        end


    if not unsafe then
        hs = string.gsub(hs, '%s+', '') -- remove whitespaces
        if string.find(hs, '[^0-9A-Za-z]') or #hs % 2 ~= 0 then
--            error('invalid hex string')
            return('')
        end
    end
    return hs:gsub('(%x%x)', function(c)
                 return char( tonumber1(c, 16))
                 end) 
--        function(c) return string.char(tonumber(c, 16)) end
--        )
end -- hextos

local function rotr32(i, n)
    -- rotate right on 32 bits
    return ((i >> n) | (i << (32 - n))) & 0xffffffff
end

local function rotl32(i, n)
    -- rotate left on 32 bits
    return ((i << n) | (i >> (32 - n))) & 0xffffffff
end




local function xor1(key, plain)
    -- return a string which is a xor of plain and key
    -- plain and key may have arbitrary length.
    -- the result has the same length as plain.
    -- naive implementation, one byte at a time (ok for small strings)
    local ot = {}
    local ki, kln = 1, #key
    for i = 1, #plain do
        ot[#ot + 1] = char(byte(plain, i) ~ byte(key, ki))
        ki = ki + 1
        if ki > kln then ki = 1 end
    end
    return concat(ot)
end --xor1

local function xor8(key, plain)
    -- return a string which is a xor of plain and key
    -- plain may have arbitrary length.
    -- ** key length (in bytes) must be a multiple of 8 **
    -- the result has the same length as plain.
    -- (result is computed one uint64 at a time)
    assert(#key % 8 == 0, 'key not a multiple of 8 bytes')
    local ka = {} -- key as an array of uint64
    for i = 1, #key, 8 do
        -- !!beware below: () around sunpack are needed:
        -- app aka table.insert takes optionally 3 args
        -- and sunpack returns 2 args...
        app(ka, (sunpack('<I8', key, i)))
    end
    local kaln = #ka
    local rbn = #plain -- remaining bytes in plain
    local kai = 1  -- index in ka
    local ot = {}  -- table to collect output
    local ibu    -- an input block, as a uint64
    local ob    -- an output block as a string
    for i = 1, #plain, 8 do
        if rbn < 8 then
            local buffer = string.sub(plain, i) .. string.rep('\0', 8 - rbn)
            ibu = sunpack('<I8', buffer)
            ob = string.sub(spack('<I8', ibu ~ ka[kai]), 1, rbn)
        else
            ibu = sunpack('<I8', plain, i)
            ob = spack('<I8', ibu ~ ka[kai])
            rbn = rbn - 8
            kai = (kai < kaln) and (kai + 1) or 1
        end
        app(ot, ob)
    end
    return concat(ot)
end --xor8

------------------------------------------------------------------------
return  { -- bin module
    stohex = stohex,
    hextos = hextos,
    --
    rotr32 = rotr32,
    rotl32 = rotl32,
    --
    xor1 = xor1,
    xor8 = xor8,
    --
        hextobin = hextobin,
        bintohex = bintohex,
    }

end

bin = preload_1009()


--*************************************************************************
-- MAIN ROUTINES
--*************************************************************************
--core


FA_OPEN_EXISTING = 0x00
FA_READ          = 0x01 
FA_WRITE         = 0x02 
FA_CREATE_NEW    = 0x04 
FA_CREATE_ALWAYS = 0x08 
FA_OPEN_ALWAYS   = 0x10 
ladebalken       = 0
sd_dir           = '---'
sd_subdirs       = {}
add_write        = FA_WRITE|FA_READ
over_write       = FA_CREATE_ALWAYS|FA_WRITE
WriteOnceSize    = 2048
sdcard_debug_txt = ''
lang_nr          = 0   
transl           = {}
t_cache          = {}

--*************************************************************************
--core

function on_init ()
--    uart_setup(0,9600,8,1,1)
    uart_set_timeout(20,10)

    if file_open == nil then
        local patch_file = "patch_"..string.format("%07u",tonumber(SERIALNUMBER))..".enc"
        local file_handle = io.open(patch_file)
        if file_handle ~= nil then
            file_handle:close()
            load_patch(patch_file)
        end
    end
    
    app = App:new(0)
    app.debug = 0

end

uart_free_protocol = 1

--**********************************************************************
--core

function on_control_notify (sid,control_nr,val)
    return app:on_control_notify(sid,control_nr,val)
end

--**********************************************************************
--core

function on_screen_change (sid)
    if app ~= nil then
--        print('ID',sid) 
        app:on_screen_change(sid)
    end
end

--**********************************************************************
--core

function on_uart_recv_data(packet)
    app:on_uart_recv_data(packet,"comes from real hardware")
end

--**********************************************************************
--core

function on_timer (tid)
    if tid == 11 then
        load_patch(PATCH_filename)
    elseif tid == 12 then
        load_parts()
    else
        return app:on_timer_pre(tid)
    end
end

--**********************************************************************
--core

function t (expression,nr)

    if nr == nil then
        nr = lang_nr
    end
    
--    local erg = t_cache[tonumber(nr) .. expression]
--    if erg ~= nil then
--        return erg
--    end

    local x     = string.find(translation_table,"\n%C-;"..expression..";")
    if x == nil then
        return expression
    end
    local ttab1 = string.sub(translation_table,x+1)
    x           = string.find(ttab1,"\n")
    local ttab2 = string.sub(ttab1,1,x-1)
    x = ";.-"
    local ttab3 = string.match(ttab2,x:rep(nr)..";(.-);")
    if ttab3 == nil or ttab3 == "-" then
        erg = ""
    else        
        erg = ttab3
    end

--     t_cache[tonumber(nr) .. expression] = erg
     return(erg)

end

--**********************************************************************
--core

function my_debug_print(debugInfo)
    --print(debugInfo)
    --sdcard_debug_txt=sdcard_debug_txt..debugInfo
    --debugInfo=get_text(2,8)..debugInfo
    --set_text(2,8,debugInfo)
    --set_text(3,8,debugInfo)
--    if open_debug_uatr == 1
--    then
--        print(debugInfo)
--    end
end


--**********************************************************************
--core


--Write data to the end of the file
--@file:file path
--@data:The type of '@data' only be [string] or [byte array]
--@open_mode:open file mode



--**********************************************************************
--core


function my_getdataLen_Type(data)
    
    my_debug_print('---------- my_getdataLen_Type ----------')

    local datalen = -1
    --获取数据类型
    local data_type = type(data)
    
    --计算数据长度
    if data_type == 'string'
    then
        datalen = string.len(data)
        
    elseif data_type == 'table'
    then
        datalen = #(data)
    end
    
    my_debug_print('Lua_debug data Type and Len: '..data_type..' / '..datalen)
    
    return datalen,data_type
end


--**********************************************************************
--core

function my_write_filedata(file, data, open_mode)
    
    my_debug_print('---------- my_write_filedata ----------')
    my_debug_print('Lua_debug: file -> '..file..' / data -> '..type(data)..' / open_mode -> '..open_mode)
    --local sc_prompt=1    
    local count     = 0
    local write_cnt = 0
    local seek_ops  = 0
    local all_byte  = 0
    
    --获取待写入数据的数据类型和长度
    local wrire_len, data_type = my_getdataLen_Type(data)
    
    local write_byte_Tb = {}
    local open_state = file_open(file, open_mode)
    
    if open_state == true
    then
        --获取当前文件大小,仅在追加文件末尾写入执行
        if open_mode == add_write
        then
            all_byte = file_size()
        end
        
        if wrire_len > 0
        then
            --计算'@data'要写多少次
            write_cnt =  math.modf(wrire_len / WriteOnceSize)
            if wrire_len % WriteOnceSize > 0
            then
                write_cnt = write_cnt + 1
            end
            my_debug_print('Lua_debug: need write allcnt -> '..write_cnt)
            
            for i = 1, write_cnt
            do
                --复位写字节数组
                write_byte_Tb = {}
                
                --计算写的位置
                seek_ops = (i - 1) * WriteOnceSize +all_byte
                file_seek(seek_ops)
                --文件偏移失败
                if offst_result == false 
                then
                    print('When writing the file, an offset error occurred. please try again! ! !')
                    break
                end
                my_debug_print('Lua_debug: cur seek_ops  -> '..seek_ops)
                
                --计算本次写的个数
                count = WriteOnceSize
                if i == write_cnt
                then
                    if wrire_len % WriteOnceSize > 0
                    then
                        count = wrire_len % WriteOnceSize
                    end
                end
                my_debug_print('Lua_debug: cur write  -> '..write_cnt..'th / wrire count '..count)
                
                --填充写入flash的字节数组
                for j = 1, count 
                do
                    if data_type == 'string'
                    then
                        --字符串类型,将每个字符转换为字节数组
                        write_byte_Tb[j - 1] = tonumber(string.byte(data, ((i - 1) * WriteOnceSize + j), ((i - 1) * WriteOnceSize + j)))
                        
                    elseif data_type == 'table'
                    then
                        --数组类型,字节赋值
                        write_byte_Tb[j - 1] = data[((i - 1) * WriteOnceSize + j)]
                    end
                end
                
                local IswriteOK = file_write(write_byte_Tb)
                if IswriteOK == false
                then
                    i = i - 1
                end    
            end
        end
        
    else
        print('The file does not exist, please check the contents of the SD card! ! !')
    end
    
    --关闭文件
    file_close()
end

--**********************************************************************
--core

function my_read_filedata(file)
    my_debug_print('---------- my_read_filedata ----------')
    my_debug_print('Lua_debug: file -> '..file)

    local count    = 0
    local read_cnt = 0
    local offset    = 0
    local all_byte = 0

    local read_byte_Tb = {}
    local read_char_Tb = {}

    local read_str     = ''
    local open_state = file_open(file, FA_READ)
    if open_state == true then
        --获取当前文件大小
        all_byte = file_size()
        if all_byte > 0 then
            read_cnt =  math.modf(all_byte/WriteOnceSize)
            if all_byte % WriteOnceSize > 0
            then
                read_cnt = read_cnt + 1
            end
            my_debug_print('Lua_debug: need read allcnt -> '..read_cnt)
            
            for i = 1, read_cnt
            do
                --复位读字节数组
                read_byte_Tb = {}
                read_char_Tb = {}
                
                --计算读取的偏移位置
                offset = (i - 1) * WriteOnceSize
                local offst_result = file_seek(offset)
                --文件偏移失败
                if offst_result == false 
                then
                    print('When reading the file, an offset error occurred. please try again! ! !')
                    
                    break
                end
                my_debug_print('Lua_debug: cur offset  -> '.. offset)

                --计算本次读的个数
                count = WriteOnceSize
                if i == read_cnt
                then
                    if all_byte % WriteOnceSize > 0
                    then
                        count = all_byte % WriteOnceSize
                    end
                end
                my_debug_print('Lua_debug: cur read  -> '..i..'th / wrire count '..count)
                
                --读取字节转换为字符并拼接成字符串
                read_byte_Tb = file_read(count)
                if #(read_byte_Tb) > 0
                then
                    for j = 0, #(read_byte_Tb)
                    do
                        read_char_Tb[j + 1] = string.char(read_byte_Tb[j])
                    end
                    read_str = read_str..table.concat(read_char_Tb)
                elseif read_byte_Tb ==nil
                then
                    print(' File read error. please try again! ! !')
                    break;
                end
            end
        end
        file_close()
    end
    return read_str
end

--**********************************************************************
--core

function on_sd_inserted (dir)
    if DISABLED == 0 then
        sd_dir     = dir
        list_dir(sd_dir)
       
    end
end

--**********************************************************************
--core

function feed_the_dog ()
    if file_open ~= nil then
        feed_dog()
    end
end

--**********************************************************************
--core

function on_list_dir (path,filename)

    feed_the_dog()
    table.insert(sd_subdirs,filename)
    if string.sub(filename,1,5) == 'patch' then
        PATCH_filename = filename
        start_timer(11,500,0,1)
        ladebalken = 5
        set_text(25,5,ladebalken)
    end

end

--**********************************************************************
--core

function load_patch (filename)

    stop_timer(10)
    ladebalken = ladebalken + 5
    set_text(25,1,ladebalken)
    patch_file = filename
    local patch_enc = ""

    if file_open == nil then
        local file1 = io.open(filename)
        print("LOAD PARTS",filename)
        local file2 = ""
        while 0 == 0 do
            if file1 == nil then
                break
            end
            local line1 = file1:read()
            if line1 == nil then
                file1:close()
                break
            end
            file2 = file2 .. line1 .. "\n"
        end
        patch_enc = file2

    else
--        set_text(14,11,sd_dir..'/'..filename)
        patch_enc = my_read_filedata(sd_dir..'/'..filename)
    end

    PATCH_plaintext   = ''
    PATCH_parts       = 0
    PATCH_zaehler     = 0
    PATCH_fortschritt = 1
    PATCH_nr          = 0
    PATCH_zeilen      = {}
    
    feed_the_dog()
    for zeile in string.gmatch(patch_enc,'[^\n]+') do
        table.insert(PATCH_zeilen,zeile)
    end

    PATCH_offset = ladebalken or 0
    PATCH_rest   = 23
    start_timer(12,1,0,1)

end

--*********************************************************************
--core

function load_parts ()

    stop_timer(10)
    feed_the_dog()
    local ciph = ''

    PATCH_nr = PATCH_nr + 1
    while PATCH_zaehler < #PATCH_zeilen do
        feed_the_dog()
        PATCH_zaehler = PATCH_zaehler + 1
        local zeile = PATCH_zeilen[PATCH_zaehler]
        if string.sub(zeile,1,2) == "--" then
            if PATCH_parts == 0 then
                PATCH_parts = tonumber(string.sub(zeile,3))
            else
                local chunk = ""
                if #ciph > 20 then
                   chunk = App.decrypt('',sec_key1,trim(ciph))
                end
                PATCH_plaintext   = PATCH_plaintext .. chunk
                ciph        = ''
                ladebalken = math.floor((100-PATCH_offset-PATCH_rest)*PATCH_zaehler/#PATCH_zeilen) + PATCH_offset
                set_value(25,1,ladebalken)
                break
            end
        else
            ciph = ciph .. zeile .. "\n"
        end
    end

    if PATCH_zaehler == #PATCH_zeilen and #PATCH_plaintext > 20 then
--        set_text(14,11,string.sub(PATCH_plaintext,10,25))
        feed_the_dog()
        patch_call = load(PATCH_plaintext)
        feed_the_dog()
        GIT_COMMIT0           = GIT_COMMIT
        local SAVE_sd_dir     = sd_dir
        local SAVE_sd_subdirs = sd_subdirs
        patch_call()
        PATCH_plaintext       = ""
        sd_dir     = SAVE_sd_dir
        sd_subdirs = SAVE_sd_subdirs
        ladebalken = 77
        start_timer(10,1,0,1)
    else
        start_timer(12,100,0,1)
    end
    
end

--*********************************************************************
--core

function trim (text)
    return (text:gsub('^%s*(.-)%s*$', '%1'))
end


--*************************************************************************
--  CLASSES
--*************************************************************************

App          = {}
App.__index  = App

--*************************************************************************
--core


function App:new (simulation)

    local self        = setmetatable({},App)
    self.simulation   = simulation
    
    if self.simulation ~= 1 then
        set_text(25,1,0)
        change_screen(25)  -- warte auf das Laden der Hotfixes
--        ladebalken          = 0
        ladebalken_interval = 8  --  x 100  Millisekunden Boot time
        start_timer(10,1,0,1)
    else
        if App.init ~= nil then
            self:init()
        end
    end
    
    return self
    
end

--**********************************************************************
--core

function App.on_timer_pre (self,tid)

    if tid == 10 then  --  Startverzoegerung, damit patches geladen werden koennen
        if ladebalken > 99 then
            if App.init ~= nil then
                self:init()
            end
        else
            ladebalken = ladebalken + 1
            set_value(25,1,ladebalken)
            feed_the_dog()
            start_timer(10,ladebalken_interval,0,1)
        end
--    elseif tid == 12 then
--       set_value(25,1,PATCH_fortschritt)
    else
        return self:on_timer(tid)
    end

end

--**********************************************************************
--core

function App.decrypt (self,ckey,text)

    if text == nil then
        return ''
    end
    
    text        = bin.hextos(text)
    if #text < 16 then
        return('')
    end

    local erg = ''
    if #ckey == 32 then
        local ckey1 = bin.hextos(ckey)
        erg         = rc4.decrypt(ckey1,text)
    elseif #ckey == 64 then
        local ckey1 = bin.hextos(ckey)
        erg         = salsa20.decrypt(ckey1,1,bin.hextos('1234567890abcdef'),text)
    else
        local ckey1 = bin.hextos( string.sub(ckey,1,64) )
        local ckey2 = bin.hextos( string.sub(ckey,65)   )
        erg         = crypto.box_open(text,string.sub(SERIALNUMBER..'00000000000000000000',1,24),
                                      ckey1,ckey2)
    end
    
    return erg

end

--**********************************************************************

--  CORE_END

--**********************************************************************

function App.crypt (self,ckey,text)

    if text == nil then
        return ''
    end

    local erg = ''
    if #ckey == 32 then
        local ckey1 = bin.hextos(ckey)
        erg         = rc4.encrypt(ckey1,text)
    elseif #ckey == 64 then
        local ckey1 = bin.hextos(ckey)
        erg         = salsa20.encrypt(ckey1,1,bin.hextos('1234567890abcdef'),text)
    else
        local ckey1 = bin.hextos( string.sub(ckey,1,64) )
        local ckey2 = bin.hextos( string.sub(ckey,65)   )
        erg         = crypto.box(text,string.sub(SERIALNUMBER..'00000000000000000000',1,24),
                             ckey1,ckey2)
    end
    
    erg = bin.stohex(erg)
    
    return erg

end

--**********************************************************************

function App.init (self)

    if self.simulation == 1 then
        local statf  = io.open('/proc/self/stat')
        math.randomseed(self:time()+os.clock()+tonumber(string.sub(statf:read(),1,4)))
        statf:close()
    else
        init_time = self:time()
        math.randomseed(init_time)
        self.counter = nil
--        self:check_for_flash_reset("341674")
        self:no_simulation()
    end

    CHECK_SIGN = "sign_" .. string.sub("%4u",self:time()) .. ".txt"

    self:init1()
    
end

--**********************************************************************

function App.init1 (self)

--    self.uart_sperre             = 0
    self.field_sim               = {}
    self.screens                 = {}
    self.fields                  = {}

    self.press_release            = 1   -- 0: on Press, 2: on Release mit long press, 1: on Press mit long (zwei Ereignisse)
                                        -- 11: if 1 occurs on a screen, set it to 2 for the next times. 

    self.main                    = Screen:new(self, 0)
    if file_open == nil then
        self.standby                 = Screen:new(self,1)
    else
        self.standby                 = Screen:new(self,19)	
    end
    self.standby.pr_re = 2
    self.lang_layout3            = Screen:new(self, 2, {  171, 270, 271, } )
    self.coin                    = Screen:new(self, 3, {  171, 271, 270, 267, 277, 278, 279, 201, 204, } )
    self.print_msg               = Screen:new(self, 4, { } )
    self.admin                   = Screen:new(self, 5, {  217, 202, 201, 212, 213, 205, 299, 204, } )
    self.adm_tarif               = Screen:new(self, 6, {  203, 208, 201, 204, 207, 208, 211, 215, 216, 217, 218, 299, 206, 219, 220, 221, 222, 233, } )
    self.adm_ext                 = Screen:new(self, 7, {  204, 299, 201, 212, 203, 208, } )
    self.adm_basic               = Screen:new(self, 8, {  202, 203, 204, 207, 206, 299, 205, 211, } )
    self.adm_proto               = Screen:new(self, 9, {  213, 205, 299, 203, 202, } )
    self.not_ready               = Screen:new(self,10, {  270, } )
    self.adm_lang_sel            = Screen:new(self,11, {  280, 299, } )
    self.pin                     = Screen:new(self,12, {  280, 202, } )
    self.invalid_pin             = Screen:new(self,13, { } )
    self.ticketslot              = Screen:new(self,14, {  280, 207, } )
    self.lang_layout2            = Screen:new(self,15, {  171, 270, 271, } )
    self.lang_layout1            = Screen:new(self,16, {  171, 270, 271, } )
    self.pin_admin               = Screen:new(self,18, {  262, 299, } )
    self.pin_template            = Screen:new(self,20, { } )
    self.adm_pin_new             = Screen:new(self,21, {  201, 204, 299, 270, 201, 217, 205, } )
    self.adm_tarif2              = Screen:new(self,22, {  203, 208, 201, 204, 207, 208, 211, 215, 216, 217, 218, 299, 206, 219, 220, 221, 222, 233, } )
    self.adm_device              = Screen:new(self,23, {  220, 217, 218, 203, 202, 204, 205, 206, 207, 299, 201, } )
    self.adm_soft_reset          = Screen:new(self,24, { } )
    self.start_screen            = Screen:new(self,25, { } )
    self.no_sdcard               = Screen:new(self,26, {  270, } )
    self.qrcode                  = Screen:new(self,27, {  3, 5, 299 } )
    self.adm_tarif_def           = Screen:new(self,28, {  215, 216, 217, 218, 299 } )
    self.adm_tarif_def2          = Screen:new(self,29, {  215, 216, 217, 218, 299 } )


    self.debug1                  = Field:new(self,'')
    self.debug1:add_field(self.standby,200)
    self.debug1:add_field(self.lang_layout3,200)
    self.debug1:add_field(self.coin,200)
    self.debug1:add_field(self.print_msg,200)
    self.debug1:add_field(self.admin,200)
    self.debug1:add_field(self.adm_tarif,200)
    self.debug1:add_field(self.adm_ext,200)
    self.debug1:add_field(self.adm_basic,200)
    self.debug1:add_field(self.adm_proto,200)
    self.debug1:add_field(self.not_ready,200)
    self.debug1:add_field(self.adm_lang_sel,200)
    self.debug1:add_field(self.pin,200)
    self.debug1:add_field(self.ticketslot,200)
    self.debug1:add_field(self.pin_admin,200)
    self.debug1:add_field(self.adm_pin_new,200)
    self.debug1:add_field(self.adm_tarif,200)
    self.debug1:add_field(self.adm_tarif2,200)
    self.debug1:add_field(self.adm_tarif_def,200)
    self.debug1:add_field(self.adm_tarif_def2,200)
    self.debug1:add_field(self.no_sdcard,200)

    self:init2()

end

--**********************************************************************

function App.init2 (self)

    self.reset_device        = 0        --  button 17
    self.reset_device_ja     = Field:new(self,'', self.adm_device,218)
    self.reset_device_nein   = Field:new(self,'', self.adm_device,217)

    self.reset_ok_code           = Field:new(self,'', self.adm_device,12)
    self.ser_number              = Field:new(self,'0',self.adm_device,21)
    self.git_version             = Field:new(self,'0',self.adm_device,22)
    self.patchv                  = Field:new(self,'none',self.adm_device,23)
    self.firmware                = Field:new(self,'1.0',self.adm_device,24)
    self.screen_nr               = Field:new(self,'1.0',self.adm_device,25)
    self.ueberdeckung            = Field:new(self,'')
    self.ueberdeckung:add_field(self.lang_layout3,94)
    self.ueberdeckung:add_field(self.lang_layout2,94)
    self.ueberdeckung:add_field(self.lang_layout1,94)
    self.ueberdeckung:add_field(self.coin,94)
    self.ueberdeckung:add_field(self.pin,94)
    self.ueberdeckung:add_field(self.qrcode,94)
    self.ueberdeckung:set_visiable(-1)
    
    self.show_low                = Field:new(self,'')
    self.show_low:add_field(self.lang_layout3,98)
    self.show_low:add_field(self.lang_layout2,98)
    self.show_low:add_field(self.lang_layout1,98)
    self.show_low:add_field(self.coin,98)
    self.show_low:add_field(self.qrcode,98)
    self.show_low:set_visiable(1)

    self.text1                   = Field:new(self,'')
    self.text1:add_field(self.coin,270)

    self.text2                   = Field:new(self,'')
    self.text2:add_field(self.coin,277)

    self.text3                   = Field:new(self,'')
    self.text3:add_field(self.coin,278)

    self.text4                   = Field:new(self,'')
    self.text4:add_field(self.coin,279)

    self.muenzen                 = Field:new(self,'')
    self.muenzen:add_field(self.lang_layout3,82)
    self.muenzen:add_field(self.lang_layout2,82)
    self.muenzen:add_field(self.lang_layout1,82)
    self.muenzen:add_field(self.coin,82)
    self.muenzen:add_field(self.lang_layout3,83)
    self.muenzen:add_field(self.lang_layout2,83)
    self.muenzen:add_field(self.lang_layout1,83)
    self.muenzen:add_field(self.coin,83)
    self.muenzen:add_field(self.lang_layout3,84)
    self.muenzen:add_field(self.lang_layout2,84)
    self.muenzen:add_field(self.lang_layout1,84)
    self.muenzen:add_field(self.coin,84)

    self.muenzen1                = Field:new(self,'20 Ct')
    self.muenzen1:add_field(self.lang_layout3,282)
    self.muenzen1:add_field(self.lang_layout2,282)
    self.muenzen1:add_field(self.lang_layout1,282)
    self.muenzen1:add_field(self.coin,282)

    self.muenzen2                = Field:new(self,'50 Ct')
    self.muenzen2:add_field(self.lang_layout3,283)
    self.muenzen2:add_field(self.lang_layout2,283)
    self.muenzen2:add_field(self.lang_layout1,283)
    self.muenzen2:add_field(self.coin,283)

    self.muenzen3                = Field:new(self,'1 Euro')
    self.muenzen3:add_field(self.lang_layout3,284)
    self.muenzen3:add_field(self.lang_layout2,284)
    self.muenzen3:add_field(self.lang_layout1,284)
    self.muenzen3:add_field(self.coin,284)
    
    self.bargeldlos              = Field:new(self,'')
    self.bargeldlos:add_field(self.lang_layout3,171)
    self.bargeldlos:add_field(self.lang_layout2,171)
    self.bargeldlos:add_field(self.lang_layout1,171)
    self.bargeldlos:add_field(self.coin,171)
    
    self.shortinfo               = Field:new(self,'')
    self.shortinfo:add_field(self.lang_layout3,170)
    self.shortinfo:add_field(self.lang_layout2,170)
    self.shortinfo:add_field(self.lang_layout1,170)
    self.shortinfo:add_field(self.coin,170)
    
    self.knob1                   = Field:new(self,'')
    self.knob1:add_field(self.lang_layout3,95)
    self.knob1:add_field(self.lang_layout2,95)
    self.knob1:add_field(self.lang_layout1,95)
    self.knob1:add_field(self.coin,95)

    self.knob2                   = Field:new(self,'')
    self.knob2:add_field(self.lang_layout3,96)
    self.knob2:add_field(self.lang_layout2,96)
    self.knob2:add_field(self.lang_layout1,96)
    self.knob2:add_field(self.coin,96)
    

    self.sondertarif_anzeige     = Field:new(self,'',self.coin,3)
    self.nochmal_button          = Field:new(self,'',self.coin,4)
    self.nochmal                 = Field:new(self,'',self.coin,204)
    self.tarifnr                 = 1
    self.pin_feld                = Field:new(self,'',self.pin,1)

    self.price_per_hour          = { Field:new(self,'1.50'), Field:new(self,'0.50') } 
    self.price_per_hour[1]:add_field(self.adm_tarif,12)
    self.price_per_hour[1]:add_flash_position(50,10)
    self.price_per_hour[2]:add_field(self.adm_tarif2,12)
    self.price_per_hour[2]:add_flash_position(850,10)

    self.min_sum             = { Field:new(self,'0.50'), Field:new(self,'0.00') }
    self.min_sum[1]:add_field(self.adm_tarif,9)
    self.min_sum[1]:add_flash_position(70,10)
    self.min_sum[2]:add_field(self.adm_tarif2,9)
    self.min_sum[2]:add_flash_position(870,10)

    self.max_park_hour       = { Field:new(self,'5'), Field:new(self,'5') }
    self.max_park_hour[1]:add_field(self.adm_tarif,10)
    self.max_park_hour[1]:add_flash_position(90,5)
    self.max_park_hour[2]:add_field(self.adm_tarif2,10)
    self.max_park_hour[2]:add_flash_position(890,5)

    self.max_park_minute     = { Field:new(self,'00'),  Field:new(self,'00') }
    self.max_park_minute[1]:add_field(self.adm_tarif,14)
    self.max_park_minute[1]:add_flash_position(100,5)
    self.max_park_minute[2]:add_field(self.adm_tarif2,14)
    self.max_park_minute[2]:add_flash_position(900,5)

    self.tagesticket         = { Field:new(self,'0'),  Field:new(self,'0') }   --  button 17
    self.tagesticket[1]:add_flash_position(110,5)
    self.tagesticket[2]:add_flash_position(910,5)

    self.tagesticket_ja      = { Field:new(self,''),  Field:new(self,'') }
    self.tagesticket_ja[1]:add_field(self.adm_tarif,218)
    self.tagesticket_ja[2]:add_field(self.adm_tarif2,218)

    self.tagesticket_nein    = { Field:new(self,'0'),  Field:new(self,'0') }
    self.tagesticket_nein[1]:add_field(self.adm_tarif,217)
    self.tagesticket_nein[2]:add_field(self.adm_tarif2,217)

    self.pay_mode            = { Field:new(self,'2'),  Field:new(self,'2') }  --  button 3
    self.pay_mode[1]:add_flash_position(120,5)     --  Werte:  0 / 1 / 2 
    self.pay_mode[2]:add_flash_position(920,5)

    self.pay_mode_coins      = { Field:new(self,'0'),  Field:new(self,'0') }
    self.pay_mode_coins[1]:add_field(self.adm_tarif,220)
    self.pay_mode_coins[2]:add_field(self.adm_tarif2,220)

    self.pay_mode_qrcode     = { Field:new(self,'0'),  Field:new(self,'0') }
    self.pay_mode_qrcode[1]:add_field(self.adm_tarif,221)
    self.pay_mode_qrcode[2]:add_field(self.adm_tarif2,221)

    self.pay_mode_both       = { Field:new(self,'0'),  Field:new(self,'0') }
    self.pay_mode_both[1]:add_field(self.adm_tarif,222)
    self.pay_mode_both[2]:add_field(self.adm_tarif2,222)

    self.tax_vat                 = Field:new(self,'19')
    self.tax_vat:add_field(self.adm_tarif,2)
    self.tax_vat:add_field(self.adm_tarif2,2)
    self.tax_vat:add_flash_position(130,10)

    self.max_zeilen              = Field:new(self,'50')
    self.max_zeilen:add_field(self.adm_proto,5)
    self.max_zeilen:add_flash_position(280,10)

    self.reportnumber            = Field:new(self,'0')
    self.reportnumber:add_field(self.adm_proto,6)
    self.reportnumber:add_flash_position(290,10)

    self.report_wird_gedruckt    = Field:new(self,'',self.adm_proto,205)

    self.is_tarif2               = Field:new(self,'', self.adm_tarif,20)

    self.location1               = Field:new(self,'12park')
    self.location1:add_field(self.adm_basic,14)
    self.location1:add_flash_position(140,10)

    self.location2               = Field:new(self,'12park.de, 0911/148781-45')
    self.location2:add_field(self.adm_basic,10)
    self.location2:add_flash_position(180,10)

    self.location3               = Field:new(self,'www.12park.de')
    self.location3:add_field(self.adm_basic,15)
    self.location3:add_flash_position(220,10)

    self.lang_description1       = Field:new(self,'')
    self.lang_description1:add_field(self.lang_layout3,4)
    self.lang_description1:add_field(self.lang_layout2,4)
    self.lang_description1:add_field(self.lang_layout1,4)

    self.lang_description2       = Field:new(self,'')
    self.lang_description2:add_field(self.lang_layout3,5)
    self.lang_description2:add_field(self.lang_layout2,5)

    self.lang_description3       = Field:new(self,'')
    self.lang_description3:add_field(self.lang_layout3,6)

    self.country_table = {"DE","UK","FR","AT","CH","CZ","DK","ES","FI","GR","HU","IE","IT","NL","NO","PL","RO","RU","SE","TR"}
    self.ldescriptions = {"Deutsch","English","Français","Deutsch","Deutsch","Čeština","Dansk","Español","Suomalainen",
                          "Ελληνικά","Magyar","Gaeilge","Italiano","Nederlands","Norsk","Polskie","Română","Русский",
                          "Svenska","Türk"}

    self.lang1 = {}
    self.lang2 = {}
    self.lang3 = {}

    local first_three_languages  = ""
    for i,lang in ipairs( self.country_table ) do

        self.lang1[lang] = Field:new(self,lang)
        self.lang1[lang]:add_field(self.lang_layout3, 9+i)
        self.lang1[lang]:add_field(self.lang_layout2, 9+i)
        self.lang1[lang]:add_field(self.lang_layout1, 9+i)
        
        self.lang2[lang] = Field:new(self,lang)
        self.lang2[lang]:add_field(self.lang_layout3, #self.country_table+9+i)
        self.lang2[lang]:add_field(self.lang_layout2, #self.country_table+9+i)
        
        self.lang3[lang] = Field:new(self,lang)
        self.lang3[lang]:add_field(self.lang_layout3, 2*#self.country_table+9+i)

        if LANGUAGE_FILTER == "ALL" or string.find(","..LANGUAGE_FILTER..",",","..lang..",") then
            first_three_languages = first_three_languages .. lang
            self.adm_lang_sel:set_text(20+#first_three_languages/2,lang)
        end
        
--        if lang == string.sub(self.lang_selector.default_text,1,2) and self.simulation == 0 then
--            set_language(i-1)
--            lang_nr = i+1
--        end
    end
    self.flags_set = ''

    self.lang_selector = Field:new(self,string.sub(first_three_languages,1,6))
    self.lang_selector:add_flash_position(260,10)
--    self.ticketslot_pin          = Field:new(self,'',  self.ticketslot,2)

    self.pin_start               = Field:new(self,'',self.pin_adm_new,6)

    self.ticketslot_pin          = Field:new(self,'')
    self.ticketslot_pin:add_flash_position(310,100)
    
    self.ticketslot_tan1         = Field:new(self,'',  self.ticketslot,1)
    self.ticketslot_tan2         = Field:new(self,'',  self.ticketslot,3)
    self.ticketslot_tan3         = Field:new(self,'',  self.ticketslot,4)
    self.anzeige_betrag          = Field:new(self,'0,00', self.coin,73)
    self.anzeige_zusatztag       = Field:new(self,'0,00', self.coin,54)
    self.anzeige_drucken         = Field:new(self,'',self.coin,65)
    self.anzeige_parkdauer       = Field:new(self,'',self.coin,72)

    self.qrcode_text             = Field:new(self,'',self.qrcode,1)
    self.qrtan                   = Field:new(self,'',self.qrcode,2)
    self.qr_invalid              = Field:new(self,'',self.qrcode,3)

    self.qrtaste                 = {}
    for nr = 1,12,1 do
        self.qrtaste[nr] = Field:new(self,'',self.qrcode,nr+9)
    end
    
    Field:new(self,'',self.qrcode,3)

    if file_open == nil then
        self.anzeige_parkdauer:add_field(self.standby,17)
        self.parkzeit                = Field:new(self,'',  self.standby,13)
        self.header_message          = Field:new(self,'',  self.standby,23)
        self.pluse_sum               = Field:new(self,'',  self.standby,2)
        self.press_button            = Field:new(self,'',  self.standby,21)
    end


    self.setup_lang1             = Field:new(self,'',  self.adm_basic,20)
    self.setup_lang2             = Field:new(self,'',  self.adm_basic,21)
    self.setup_lang3             = Field:new(self,'',  self.adm_basic,22)

    self.pin_definitions         = Field:new(self,'')
    self.pin_definitions:add_flash_position(15*4096+5,4000)

    self.pin_offset              = 0
    self.ungueltig_sec           = Field:new(self,'',  self.pin,33)
    self.ungueltige_pin          = Field:new(self,'',  self.pin,3)
    self.pin_wartezeit           = 0

    self.pin_entries = {}
    while #self.pin_entries < 10 do
        table.insert(self.pin_entries, {
                       Field:new(self,'',  self.pin_admin,#self.pin_entries+1),
                       Field:new(self,'',  self.pin_admin,#self.pin_entries+21),
                       Field:new(self,'',  self.pin_admin,#self.pin_entries+51)
                    } )
    end

    self:init3()
    
end

--**********************************************************************
 
function App.init3 (self)

    self.tarif_par = { {}, {}, {}, {} }

    for i = 1,10,1 do
    
        self.tarif_par[1][i] = Field:new(self,'',self.adm_tarif_def ,20+i)
        self.tarif_par[2][i] = Field:new(self,'',self.adm_tarif_def ,40+i)
        self.tarif_par[3][i] = Field:new(self,'',self.adm_tarif_def2,20+i)
        self.tarif_par[4][i] = Field:new(self,'',self.adm_tarif_def2,40+i)
        
        self.tarif_par[1][i]:add_flash_position(1200+10*i,10)
        self.tarif_par[2][i]:add_flash_position(1400+10*i,10)
        self.tarif_par[3][i]:add_flash_position(1600+10*i,10)
        self.tarif_par[4][i]:add_flash_position(1800+10*i,10)
        
    end
    
    self:make_tarif_table()
    
    self:init4()


end

--**********************************************************************
 
function App.init4 (self)

    self.signature_positions = {}
    for i = 0,9 do
        table.insert(self.signature_positions,12300+i*4096)
    end

    self.counter = {}
    for i = 0,9,1 do
        local count = Field:new(self,'100')
        if file_open == nil then
            count = Field:new(self,'10000')
        end
        count:add_flash_position(12600+i*4096,100)
        table.insert(self.counter,count)
    end

    self.aktueller_teilcounter = 0
    self.counter1              = Field:new(self,'0', self.admin,8)

    self.anzahl_gedruckte_parktickets = Field:new(self,'0', self.admin,7)
    self.anzahl_gedruckte_parktickets:write('0')

    self:init5()

end

--**********************************************************************

function App.init5 (self)

    if file_open ~= nil then
        start_timer(29,60000,0,1)
    end

    self.TIMEOUT              = 18
    self.REDUCE_BACKLIGHT     = 14
    self.TIMEOUTLONG          = 75
    self.TIMEOUTQRCODE        = 40.0
    self.QRCODE_REDUCE        = 32.0
    self.LOW_BACKLIGHT        = 30
    self.BLACKSCREEN          =  0
    self.WAIT_TIME_LONG_PRESS =  3
    self.ANZAHL_PINS          =  5
--    WAIT_26                   = 1000

    if file_open == nil then 
        self.LOW_BACKLIGHT = 70
        self.BLACKSCREEN   = 68
    end
    
    self.pin_randomseed       = Field:new(self, SERIALNUMBER )
    self.pin_randomseed:add_flash_position(14*4096+72,10)

--    if self.simulation == 1 then
--        os.execute('mkdir flashsim 2> /dev/null')
--    end

--    self:write_flash(22,'a')
--    local h = self:read_flash(22)

    if self.simulation == 1 then
        return
    end


--  check the integrity of flash

    self.screen_switch_is_blocked = 0
    self.no_timeout               = 0
    self.admin_count              = 0
    self.control0                 = nil
    self.aktuelle_pin_eingabe     = ''
    self.long_press               = 0
    self.catch_release            = 0
    self.lfd_nr                   = tonumber(self:time()) % 100

    self.ser_number:write(SERIALNUMBER)
    if GIT_COMMIT0 == nil then
        self.git_version:write(GIT_COMMIT)
        self.patchv:write( 'none' )
    else
        self.git_version:write(GIT_COMMIT0)
        self.patchv:write( GIT_COMMIT )
    end
    
    self.firmware:write(FIRMWARE_VER)
    self.screen_nr:write( get_version() )
    self.used_pins = {}

    self.adm_basic:set_text(20,string.sub(self.lang_selector:read(),1,2))
    self.adm_basic:set_text(21,string.sub(self.lang_selector:read(),3,4))
    self.adm_basic:set_text(22,string.sub(self.lang_selector:read(),5,6))
    self:synchronize_buttons()
    
    self:check_if_tarif2_is_necessary()
    self.debug1:set_visiable(0)   --  @@  test
    
    self.nochmal:set_visiable(-1)
    self.nochmal_button:set_visiable(-1)
    self.knob1:set_visiable(1)
    self.knob2:set_visiable(-1)
    print("HAS_KNOB",HAS_KNOB)
    if HAS_KNOB == 1 then
        self.knob1:set_visiable(-1)   --  Schaltflaeche deaktiviert, denn es gibt ja einen Knopf
    end
--    ZUFALLSWERT = 1
    self:reset_coin()
    self.qr_invalid:set_visiable(0)

--    self.debug1:write(ZUFALLSWERT)

    if RESET_FLASH > 0 then
        self:check_for_flash_reset('341674')
        self:disable_device()
        return
    end

--    self.uartbuffer  = {}
--    self.uartzaehler = 0
--    start_timer(7,100,0,1)
    self:switch_language(1)
    if file_open ~= nil and sd_dir == '---' then
        set_backlight(100)
        self.no_sdcard:activate()  --  keine SD Karte eingelegt
        return
    end

    self.there_is_no_sdcard = 0

    self:compute_shortinfo()
    if self.simulation == 0 then
        self:no_simulation()
    end

    local rep = self:read_report()
    self:write_report(rep) 

    self.request_id = 0
    self.flash_is_not_signed = 1 - self:check_flash_signature() 

    self.ticketslot_start = 0
    if self.flash_is_not_signed == 1 or self.report_is_integer == 0 then
        self:print('flash was not integer....')
        self.no_timeout = 1
        self:renew_timeout()

        self.ticketslot_start = 1
        self.ticketslot:activate()
--        self.adm_proto:activate()   --  zu Testzwecken
    else
       self:update_counter(0)
       self.no_timeout = 0
       set_backlight(self.BLACKSCREEN)
       self.standby:activate()
    end

--    print("SCR---------------------------",self.standby.screen_id,self.standby,self.screens[1])

--    self.counter1:write( string.format('%7u',math.floor(self:read_counter()/100+0.5)) )
--    self.anzahl_gedruckte_parktickets:write('0')



end


--****************************************************

function App.on_screen_change (self,sid)
    if self.screens ~= nil then
        self.aktueller_screen = self.screens[sid]
    end
end

--****************************************************

function App.synchronize_buttons (self,bval)

    bval = tonumber(self.tagesticket[1]:read())
    self.tagesticket[1]:set_visiable(0)
    self.tagesticket_ja[1]:set_visiable(  (bval==1) and 1 or 0 )
    self.tagesticket_nein[1]:set_visiable((bval==0) and 1 or 0 )

    bval = tonumber(self.tagesticket[2]:read())
    self.tagesticket[2]:set_visiable(0)
    self.tagesticket_ja[2]:set_visiable(  (bval==1) and 1 or 0 )
    self.tagesticket_nein[2]:set_visiable((bval==0) and 1 or 0 )

    bval = tonumber(self.pay_mode[1]:read())
    self.pay_mode[1]:set_visiable(0)
    self.pay_mode_coins[1]:set_visiable(    (bval==0) and 1 or 0 )
    self.pay_mode_qrcode[1]:set_visiable(   (bval==1) and 1 or 0 )
    self.pay_mode_both[1]:set_visiable(     (bval==2) and 1 or 0 )

    bval = tonumber(self.pay_mode[2]:read())
    self.pay_mode[2]:set_visiable(0)
    self.pay_mode_coins[2]:set_visiable(    (bval==0) and 1 or 0 )
    self.pay_mode_qrcode[2]:set_visiable(   (bval==1) and 1 or 0 )
    self.pay_mode_both[2]:set_visiable(     (bval==2) and 1 or 0 )

    bval = self.reset_device
    self.reset_device_ja:set_visiable(  (bval==1) and 1 or 0 )
    self.reset_device_nein:set_visiable((bval==0) and 1 or 0 )

end

--**********************************************************************

function App.date (self,format_string,sec)

    if self.simulation == 1 then
        return os.date(format_string,sec)
    end

    if sec == nil then
        sec = self:time()
    end
    
    if format_string == nil then
        return sec
    end
    
    if sec > 1000000000000 then
        sec = tostring(sec)
        format_string = string.gsub(format_string,'%%y',string.sub(sec,1,4))
        format_string = string.gsub(format_string,'%%m',string.sub(sec,5,6))
        format_string = string.gsub(format_string,'%%d',string.sub(sec,7,8))
        format_string = string.gsub(format_string,'%%H',string.sub(sec,9,10))
        format_string = string.gsub(format_string,'%%M',string.sub(sec,11,12))
        format_string = string.gsub(format_string,'%%S',string.sub(sec,13,14))
        return format_string
    else
        return date1.date_format(format_string,sec)
    end

end


--**********************************************************************

function App.time (self,tt)

    if self.simulation == 1 then
        return os.time()
    end
    
    local year   = ''
    local month  = ''
    local day    = ''
    local hour   = ''
    local min    = ''
    local sec    = ''
    local week   = ''
    
    year, month, day, hour, min, sec, week = get_date_time()
--    hour = hour - 12  @@  zu Testzwecken
    local sec = date1.date_reverse(year,month,day,hour,min,sec)
    return sec

end

--**********************************************************************

function App.check_for_flash_reset (self,xcode)

    print('FLASH RESET')

    if restart_device == 0 then
        return
    end

    local patterns = { 341674, 627856, 524516, 788420, 634154 }
    
    if xcode ~= nil and #xcode == 0 then   --  soft reset

        print(' ... soft')
        for name,field in pairs(self.fields) do
            feed_the_dog()
            local bed = 0
            for i = 1,10,1 do
                if field == self.counter[i] then
                    bed = 1
                    break
                end
            end
            if bed == 0 then
                field:write( field.default_text )
            end
        end
        self:check_flash_signature('update')
        self.adm_soft_reset:activate()
        self:disable_device("x")
        return
        
    end

    if xcode == nil and file_open ~= nil then

        xcode    = my_read_filedata(sd_dir..'/flash_reset.txt')
        xcode    = string.sub(xcode,1,6)
        
    end 
        
    if xcode ~= nil then   --  hard reset
            
        for i,pp in ipairs(patterns) do
            if xcode == tostring(pp) then
        
                self:disable_device("x")
                print(' ... hard')
                for zaehler = 0,31,1 do
                    feed_the_dog()
                    write_flash_string(1+zaehler*2048, string.rep(' ',2040))
                end
                change_screen(13)
                return

            end
        end

    end       
       
end

--**********************************************************************

function App.print (self,text1,text2,text3)

    if self.debug ~= nil and file_open == nil then
        if text2 == nil then
            print(text1)
        elseif text3 == nil then
            print(text1,text2)
        else
            print(text1,text2,text3)
        end
    end

end

--**********************************************************************

function App.check_flash_signature (self,mode)

    local contents      = {}
    for i,field in ipairs(self.fields) do
        local nr = field.flash_position
        local tt = field:read()
        table.insert(contents,{ nr, tt })
    end
    table.sort(contents,function (a,b) return(a[1]<b[1]) end)
    local text = 'x'

--    local rtxt1 = "--------------------------\r\n"

    for i,text1 in ipairs(contents) do
--        rtxt1 = rtxt1 .. tostring(text1[1]) .. ","..text1[2].."\r\n"
--        print("SORT",text1[1],text1[2])
        text = text .. text1[2]
    end
    
--    if file_open ~= nil then
--        my_write_filedata(sd_dir.."/"..CHECK_SIGN,text,over_write)
--    end
    
    local md5key = bin.stohex( md5lib.hash(text) )
    local ciph = self:crypt(sec_key3,md5key)
    
--    rtxt1 = rtxt1 ..ciph.."\r\n"
    
--    local printtext = my_read_filedata(sd_dir..'/check_sign.txt')
--    if printtext == nil then
--        printtext = ""
--    end
--    rtxt1 = printtext .. rtxt1
--    my_write_filedata(sd_dir..'/check_sign.txt',rtxt1,over_write)
    
    
    if mode == nil then

--        print("CIPH",ciph)
        for i,sign_pos in ipairs(self.signature_positions) do  --  alle moeglichen Signatur-Positionen durchpruefen
            if read_flash_string(sign_pos) == ciph then
                return 1
            end
        end
        return 0

    end
    
    print('SIGN FLASH')
    local sign_idx             = string.gsub(md5key,"%D","").."1"
    self.aktueller_teilcounter = tonumber( string.sub(sign_idx,1,15) % #self.counter)
    sign_idx                   = tonumber( string.sub(sign_idx,1,20) % #self.signature_positions)
    local sign_pos             = self.signature_positions[sign_idx+1]
--    print("SIX",sign_idx,sign_pos,ciph)
    write_flash_string(sign_pos,ciph)
    
end

--**********************************************************************

function App.compute_tan (self,slcode)

    sl = self:permutation(0,slcode)
    
    if SERIALNUMBER ~= string.sub(sl,6,12) then
        print( 'Serialnumber --> ', string.sub(sl,6,12) )
        return
    end
    
    local pin = string.sub(sl,1,5) 
    local tan = self:check_tan(0,pin)
--    print( 'PIN', pin, 'TAN: ', tan)

--    local ticketcode = self:permutation(1,pin..tan)
--    print( 'PIN: ', pin, tan,'TICKETCODE',ticketcode)
        
end
    

--**********************************************************************

function App.check_tan (self,mode,pin,tan)

    local ciph      = self:crypt(sec_key1,pin)
    local md5c      = bin.stohex( md5lib.hash(ciph) )
    local md5_bits  = bin.hextobin(md5c)
    
    local tan1 = 0
    for i,bit in ipairs(md5_bits) do
        tan1 = 2*tan1 + tonumber(bit)
        if i == 39 or mode == 1 and i == 16 then
            break
        end
    end
    if mode == 1 then
        tan1 = math.ceil(10^4 + 1.29 * tan1 + 1234)
    else
        tan1 = math.ceil(10^11 + 1.44 * tan1 + 1234)
    end
    print('PIN/TAN/TAN1',pin,tan,tan1)

--    print('WWE',pin,tan,tan1)

    if tan == nil then
        return tostring(tan1)
    end

    if file_open == nil then
        if tan == '123412341234' or tan == '55555' then   --  @@
            return 1
        end
    end


    if tostring(tan) == tostring(tan1) then
        return 1
    else
        return 0
    end
    
end

--**********************************************************************

function App.permutation (self,nr,text)

    local pm = {3,2,9,5,4,7,1,11,8,10,12,6}
    
    zaehler = 0
    text1   = {}
    while zaehler < 12 do
        zaehler =   zaehler + 1
        local i =   zaehler + (2*nr-1)
        if i    ==   0 then i = 12 end
        if i    ==  13 then i =  1 end
        i       =   pm[i]
        text1[ pm[zaehler] ] = string.sub(text, i, i)
    end
    
    local text2 = ''
    for i,x in ipairs(text1) do
        text2 = text2 .. x
    end
    
    return text        

end

--**********************************************************************

function App.request_flash_signature (self)

    math.randomseed(tonumber(self:time())*1000+tonumber(get_timer_value(29)))
    if self.flash_is_not_signed == 1 then
        self.request_id = math.random(93003,99981)
    elseif self.report_is_integer == 0 then
        self.request_id = math.random(86003,92981)
    end

    self.debug1:write(tostring(self.request_id) .. ' ' .. tostring(self.report_is_integer)..'  '..tostring(self:time()) )

    local sl  = tostring(self.request_id) .. trim(tostring(SERIALNUMBER))
    sl        = self:permutation(1,sl)
    sl        = 'S ' .. string.sub(sl,1,4) .. ' ' .. string.sub(sl,5,8) .. ' ' .. string.sub(sl,9,12)
--    print(sl)
    local requestinfo = {
'',
'',
'QQx1d@',
'QW',
'QW',
'QQx1ba0QQx1d!QQx00',
t('Speichersignierung anfordern'),
t('durch Angabe der Ordernummer:'),
'',
'QQx1d!QQx11QQx1ba1',
sl,
'',    
'QQx1d!QQx00',
'',
t('Details siehe unter:'),
t('www.12park.de/storagesign'),
'',
'',
'',
'',
'QQx1dVQQx01',
'QW',
'QW',
'QQx1d@'
}

    self:send_to_printer(requestinfo)
    
end

--**********************************************************************

function App.print_bestelldaten (self)


    local pin  = self.ticketslot_pin:read()
    math.randomseed(tonumber(self:time())*1000+tonumber(get_timer_value(29)))
    while #pin < 30 do
        pin = pin .. tostring(math.random(10002,85997))
    end
    self.ticketslot_pin:write(pin)

    local orderinfo = {
'',
'',
'QQx1d@',
'QW',
'QW',
'QQx1ba0QQx1d!QQx00',
t('Bitte Ticketslot bestellen'),
t('durch Ueberweisung unter'),
t('Angabe einer oder mehrerer'),
t('Ordernummern:'),
'',
'QQx1d!QQx11QQx1ba1'
}
    while #pin > 4 do
        local sl  = string.sub(pin,1,5) .. trim(tostring(SERIALNUMBER))
        sl        = self:permutation(1,sl)
        sl        = 'T ' .. string.sub(sl,1,4) .. ' ' .. string.sub(sl,5,8) .. ' ' .. string.sub(sl,9,12)
--        print(sl)
        table.insert(orderinfo,sl)
        pin       = string.sub(pin,6) 
    end    
    
    table.insert(orderinfo,'QQx1d!QQx00')
    table.insert(orderinfo,'')
    table.insert(orderinfo,t('Bankverbindung siehe unter:'))
    table.insert(orderinfo,t('www.12park.de/ticketslot'))
    table.insert(orderinfo,'')
    table.insert(orderinfo,'')
    table.insert(orderinfo,'')
    table.insert(orderinfo,'')
    table.insert(orderinfo,'QQx1dVQQx01')
    table.insert(orderinfo,'QW')
    table.insert(orderinfo,'QW')
    table.insert(orderinfo,'QQx1d@')

    self:send_to_printer(orderinfo)
    
end

--**********************************************************************

function App.send_to_printer (self,printdata)

    self.print_data = printdata
    start_timer(23,10,0,1)
    
end

--**********************************************************************

function App.send_to_printer1 (self,text,datei)

    if file_open == nil then

        if datei == nil then
            datei = 'null.txt'
        end


--        os.execute('echo '..parkticket..' > /tmp/dbg.txt')
        io.open(datei,'w')
--        print('FILE','--------------------------------',f)
        if f ~= nil then
            for i,zeile in ipairs(text) do
                zeile = zeile:gsub('QQ','\\\\')
--                f:write('echo -e ''..zeile..'' > /dev/usb/lp0\n')
            end
        f:close()
        end
        
    elseif true then
        for i,zeile in ipairs(text) do
--        zeile = zeile:gsub('QQ','\\') .. '\n'
            if string.sub(zeile,1,9) ~= '---DEL---' then
                zeile = zeile .. '\r\n'
                uart_send_data( self:str_to_byteArray(zeile))
            end
            if i % 50 == 0 then
                feed_the_dog()
            end
        end
--        start_timer(6,1000,0,1)

    end

end


--**********************************************************************

function App.current_screen (self)

--    return self.aktueller_screen 
  
    local screen_id = get_current_screen()
    local screen    = self.screens[screen_id]
--    print("SID",screen_id,screen)
    
    return screen
    
end

--**********************************************************************

function App.set_visiable (self,x,y,z)

    if self.simulation == 0 then
        set_visiable(x,y,z)
    else
        print('set_visiable',x,y,z)
    end

    
end

--**********************************************************************

--  Switch to the call back functions

function App.on_control_notify (self,sid,control,val)

    self:on_control_notify1(sid,control,val)
    
end

--**********************************************************************

function App.on_control_notify1 (self,sid,control,val)

    if DISABLED == 1 then
        return
    end

    local screen = self.screens[sid]

--    print('ON_CONTROL',sid,control,val)

    stop_timer(25)   --- Unterbreche die 
    stop_timer(26)   --- Ticket Report Berechnung
    
    if control == 95 and (screen == self.standby or screen == self.lang or screen == self.adm_device) then
--        print('XXXX',sid,screen,self.adm_device,control)
--          if self.doubleklick ~= screen then
          if val == 1 then
--            self:press_action()
--            start_timer(21,1,0,1)
--            print("WWW1",screen)
            self.doubleklick = screen
            self:on_uart_recv_data( { [0] = 0xEE, [1] = 0xB1, [2] = 21 } )
        else
--            self:release_action()
--            start_timer(22,1,0,1)
--            print("WWW2",screen)
            self.doubleklick = nil
            self:on_uart_recv_data( { [0] = 0xEE, [1] = 0xB1, [2] = 22 } )
        end
        return
    end        
        
    local long_press = self.long_press
    self.long_press  = 0

    if control == 95 or control == 96 or control == 4 and screen == self.coin then
        control = 80
    end
    
    self:renew_timeout()

    if     control == 80 then
        return self:press_knob(screen,val,long_press)
    
    elseif control > 80 and control < 89 then
        return self:insert_coin(control-80+1)
    
    elseif control > 1080 and control < 1089 then
        return self:insert_coin(control-1070+1)
    
    elseif screen == self.lang and control == 7 then
        self.long_press = 1
        self:on_control_notify1(sid,80)
        
    elseif screen == self.lang and control > 0 and control < 4 then
        self:admin_bereich(control)
        self:switch_language(control)
        
    elseif screen == self.pin then
        return self:pin_eingabe(control)

    elseif screen == self.ticketslot and control == 10 then
        self:activate_ticketslot()

--  rem_projekt_vm

    elseif screen == self.ticketslot and control == 7 then
--        print(1235,self.flash_is_not_signed,self.report_is_integer)
        if self.flash_is_not_signed == 0 and self.report_is_integer == 1
                 or self.report_is_integer == 2 then   --  ist nicht integer, sondern weggedrueckt
            self.ticketslot_start = 0
            self:print_bestelldaten()
        else
            self:request_flash_signature()
        end
        
    elseif screen == self.ticketslot and control == 17 then
        if self.ticketslot_start == 0 then
            self.adm_ext:activate()
        else
            self:disable_device()
        end
        
    elseif screen == self.adm_basic and control == 99 then
        self.admin:activate()
        
    elseif screen == self.adm_basic and 29 < control and control < 33 then
        self.einzustellende_sprache = control
        self.adm_lang_sel:activate()
        
    elseif screen == self.admin and control == 12 then
        self:check_if_tarif2_is_necessary()
        self.adm_tarif:activate()
        
    elseif screen == self.admin and control == 2 then
        self.adm_basic:activate()
        
    elseif screen == self.admin and control == 13 then
        self.report_wird_gedruckt:set_visiable(0)
        self.press_protocol = 0
        self.adm_proto:activate()
        
    elseif screen == self.admin and control == 5 then
        self.adm_ext:activate()
        
    elseif screen == self.adm_proto and control == 13 then
        self.press_protocol = self.press_protocol + 1
        self:print_report()
        
    elseif screen == self.adm_ext and control == 1 then
--        self.ticketslot_pin:write( tostring(math.random(10002,89997)) )
        self.ticketslot:activate()
        
    elseif screen == self.admin and control == 99 then
        self.no_timeout = 0
        self:compute_shortinfo()
        set_backlight(self.BLACKSCREEN)
        self.standby:activate()
        
    elseif screen == self.adm_lang_sel then
        if control  == 99 then
            self.adm_base:activate()
        else
            self:sprachauswahl(control)
        end
        
    elseif screen == self.adm_ext and control == 12 then
        self:pin_base(control)
        
    elseif screen == self.adm_ext and control == 3 then
        self.adm_pin_new:activate()
        
    elseif screen == self.adm_ext and control == 99 then
        self.admin:activate()
        
    elseif screen == self.adm_ext and control == 8 then
        self.adm_device:activate()
        
    elseif screen == self.adm_device and control == 99 then
        self.adm_ext:activate()
        
    elseif screen == self.adm_pin_new and control == 5 then
        self.admin:activate()
      
    elseif screen == self.adm_pin_new and control == 17 then
        self:pin_list_change()
        self:pin_base(12)
        
    elseif screen == self.adm_pin_new and control == 99 then
        self.adm_ext:activate()
      
    elseif screen == self.pin_admin then
        self:pin_base(control)
        
    elseif screen == self.qrcode and (control == 2 or control == 93) then
        self:aktiviere_qr_tastenfeld(1)
    
    elseif screen == self.qrcode and control > 9 and control < 21 and val == 1 then
        self:press_qr_taste(control)
    
    elseif screen == self.qrcode and control == 94 then
        self:renew_qr_timeout()
        self.no_timeout = 0

    elseif screen == self.qrcode and control == 99 then
        stop_timer(18)
        stop_timer(3)
        self:on_timer(2)

    elseif screen == self.standby and control == 99 then
        return self:admin_bereich()

    elseif screen == self.adm_tarif and control == 20 then
        return self.adm_tarif2:activate()

    elseif screen == self.adm_tarif and control == 4 then
        return self.adm_tarif_def:activate()

    elseif screen == self.adm_tarif2 and control == 4 then
        return self.adm_tarif_def2:activate()

    elseif screen == self.adm_tarif_def and control == 99 then
        self:make_tarif_table()
        return self.adm_tarif:activate()

    elseif screen == self.adm_tarif_def2 and control == 99 then
        self:make_tarif_table()
        return self.adm_tarif2:activate()

    elseif screen == self.adm_tarif and control == 19 then
        return self.adm_tarif2:activate()


--  buttons:

    elseif screen == self.adm_tarif and control == 17 then
        local x = tonumber(self.tagesticket[1]:read())
        self.tagesticket[1]:write(tostring( (x+1)%2 ))
        print(self.tagesticket[1]:read(),1236)
        self:synchronize_buttons()

    elseif screen == self.adm_tarif2 and control == 17 then
        local x = tonumber(self.tagesticket[2]:read())
        self.tagesticket[2]:write(tostring( (x+1)%2 ))
        self:synchronize_buttons()

    elseif screen == self.adm_tarif and control == 3 then
        local x = tonumber(self.pay_mode[1]:read())
        self.pay_mode[1]:write(tostring( (x+1)%3 ))
        self:synchronize_buttons()

    elseif screen == self.adm_tarif2 and control == 3 then
        local x = tonumber(self.pay_mode[2]:read())
        self.pay_mode[2]:write(tostring( (x+1)%3 ))
        self:synchronize_buttons()

    elseif screen == self.adm_device and control == 17 then
        local x = self.reset_device
--        print("DD1",x)
        self.reset_device = (x+1)%2
--        print("DD2",self.reset_device)
        self:synchronize_buttons()

    else
        for i,field in ipairs(self.fields) do
            if field:synchronize_with_entry(screen,control) == 1 then
                break
            end
        end            

    end
        
--



end

-- cf1 = load(chunk1)   cf1()

--**********************************************************************

function App.aktiviere_qr_tastenfeld (self,mode)

    self:renew_qr_timeout()
    for nr = 1,12 do
        self.qrtaste[nr]:set_visiable(mode)
    end
        
end

--**********************************************************************

function App.press_qr_taste (self,nr)

    self:renew_qr_timeout()
    local tan_part
    if nr < 20 then
        tan_part = self.qrtan:read()
        tan_part = tan_part .. string.format("%1u",nr-10)
        self.qrtan:write(tan_part)
    elseif nr == 20 then
        tan_part = self.qrtan:read()
        tan_part = string.sub(tan_part,1,#tan_part-1)
        self.qrtan:write(tan_part)
    end
    
    if #tan_part == 5 then
        print("TT",tan_part)
        local sbed = #self.falsche_qr_eingabe > 1 and self.falsche_qr_eingabe[1] == "15970" 
                                                  and self.falsche_qr_eingabe[2] == "15463" 
        if self:check_tan(1,tostring(self.qr_ordernr),tostring(self.qrtan:read())) == 1 or sbed then
            self.nochmal:set_visiable(-1)
            self.nochmal_button:set_visiable(-1)
            self.qr_ordernr = nil
--            self.coin:activate()
            self.no_timeout = 0
            stop_timer(18)
            self:renew_timeout()
            start_timer(7,7,0,1)
        else
            table.insert(self.falsche_qr_eingabe,self.qrtan:read())
--            self.qrtan:write("")
            if #self.falsche_qr_eingabe > 7 then
                self:on_timer(18)
            else
                self.qr_invalid:set_visiable(1)
                start_timer(19,1700,0,1)
                self.no_timeout = 0
            end
        end
    end

end


--**********************************************************************

function App.compute_shortinfo (self)

    local pph   = self:get_price_per_hour(1)
    local ttt   = self.tagesticket[1]:read()
    local dauer = self.max_park_hour[1]:read()
    local d_min = string.format('%02u',tonumber(self.max_park_minute[1]:read()))
    local minp  = self.min_sum[1]:read()
    
    if self.tarif_table[1] == nil then

        local text = pph .. ' ' .. CURRENT .. ' ' .. t('pro Stunde') .. ', '
        if tonumber(minp) > 0.00 then
            text = text .. t('mind.') .. ' ' .. tostring(minp):gsub('%.',',') .. ' ' .. CURRENT .. ', '
        end
        if ttt == '0' then
            text = text .. t('Höchstparkdauer') .. ' ' .. dauer .. ':' .. d_min .. ' ' .. t('Std.')
        else
            text = text .. t('Tagesticket ab')  .. ' ' .. dauer .. ' ' .. t('Std.')
        end

        self.shortinfo:write(text)
        
    else
    
        self.shortinfo:write("")
        
    end
    
end
    

--**********************************************************************
    
function App.press_knob (self,screen,val,long_press)

    local screen = self:current_screen()
--    print('RRW',screen.adm_device,screen,val,long_press)

    if false and self:current_screen() == self.standby then local f = 1  --  UART Test wenn true

    elseif screen == self.lang then
        if long_press == 1 then
            self.pin_mode             = 'lang'
            self.ungueltig            = 0
            self.aktuelle_pin_eingabe = ''
            if self.press_release == 1 then  --  Sprache zurueckstellen bei Press und Press_long
                if self:switch_language() == 3 then
                    self:switch_language()
                end
            end
            if self.broetchen == 1 then
                self.show_text_muenzen = 1
                self:activate_sondertarif(1)
            elseif self.wohnmobil == 1 then
                self.show_text_muenzen = 1
                self:activate_sondertarif(0)
            else
                self.pin:activate()
            end
        else
            self:switch_language()
        end

    elseif screen == self.coin then
        self:insert_coin(1)

    elseif screen == self.standby then
        if long_press == 1 then
            self:admin_bereich()
        end
        if self.real_coins == 0 then
            self:reset_coin()
        end
        if self.anzeige_betrag:read() == '0,00' then
--            if self:switch_language(1) == 99199 then
--                self.coin:activate()
--            else
                self:switch_language(1)
                self:choose_flags()
                self.lang:activate()
--            end
        else
            self.coin:activate()
        end
    
    elseif screen == self.adm_device then
        if long_press == 1 then
            self:check_for_flash_reset( trim(self.reset_ok_code:read()) )
        end
    end
    
end
    
--**********************************************************************

function App.choose_flags (self)

--    print(self.lang_selector)
    local cc  = self.lang_selector:read()
    if self.flags_set ==  cc then
        return
    end
    
    local cc1 = string.sub(cc,1,2) 
    local cc2 = string.sub(cc,3,4)
    local cc3 = string.sub(cc,5,6)
    
--    print(cc1,cc2,cc3)

    for lang,lfield in pairs(self.lang1) do
        lfield:set_visiable( (cc1==lang) and 1 or 0 ) 
    end

    for lang,lfield in pairs(self.lang2) do
        lfield:set_visiable( (cc2==lang) and 1 or 0 ) 
    end

    for lang,lfield in pairs(self.lang3) do
        lfield:set_visiable( (cc3==lang) and 1 or 0 ) 
    end

    if cc1 ~= nil then self.lang_description1:write( self.ldescriptions[ self:language_number(cc1) ] or '' ) end
    if cc2 ~= nil then self.lang_description2:write( self.ldescriptions[ self:language_number(cc2) ] or '' ) end
    if cc3 ~= nil then self.lang_description3:write( self.ldescriptions[ self:language_number(cc3) ] or '' ) end
    
end
    
--**********************************************************************

function App.admin_bereich (self,control)

--    print('11',control,self.admin_count,self.control0)

    self.press_protocol = 0
--    print("CCC",self.admin_count,self.control0,control)
    if control ~= nil and self.admin_count == 0 or self.control0 ~= nil and self.control0 ~= control then
        self.admin_count = 0
        stop_timer(4)
        self.control0 = nil
        return
    end
    self.control0 = control

    if self.admin_count == 0 then
        start_timer(4,8000,0,1)
    end

    self.admin_count = self.admin_count + 1
--    print('12',control,self.admin_count,self.control0)

    if self.admin_count > 4 then
        self.pin_mode = 'admin'
        self.admin_count = 0
        self.pin:activate()
    end
    
end

--**********************************************************************

function App.pin_eingabe (self,control)

--    print('PIN'..tostring(control))
--    print('AKT'..self.aktuelle_pin_eingabe)

    if self.ungueltig == nil then
        self.ungueltig = 0
    end

    
    if self.pin_wartezeit > 0 then
        return
    end
    
    if control == 14 then
        if self.pin_feld:read() == "" then
            self.no_timeout = 0
            set_backlight(self.BLACKSCREEN)
            self.standby:activate()
        else
            self.aktuelle_pin_eingabe = ''
            self.pin_feld:write('')
        end
        return
    end

    if control > 3 and control < 14 then
        self.aktuelle_pin_eingabe = self.aktuelle_pin_eingabe .. tostring((control-3)%10)
        if     #self.aktuelle_pin_eingabe == 1 then self.pin_feld:write('*')   
        elseif #self.aktuelle_pin_eingabe == 2 then self.pin_feld:write('*   *')  
        elseif #self.aktuelle_pin_eingabe == 3 then self.pin_feld:write('*   *   *') 
        elseif #self.aktuelle_pin_eingabe == 4 then self.pin_feld:write('*   *   *   *')
        elseif #self.aktuelle_pin_eingabe == 5 then self.pin_feld:write('*   *   *   *   *')
        end

        if #self.aktuelle_pin_eingabe == 5 then
            local erg = self:pruefe_pin(self.aktuelle_pin_eingabe)

--            print("RRR",self.aktuelle_pin_eingabe)
            local u_key = tostring(self.aktuelle_pin_eingabe) .. "," .. tostring(math.ceil(tonumber(self:time())/86400))
            print("U_KEY",u_key,self.used_pins[u_key],self.anzahl_dieselbe_pin)
            if self.used_pins[u_key] ~= nil and self.used_pins[u_key] >= self.anzahl_dieselbe_pin then
                erg = nil    --  ist an diesem Tag schon einmal aufgerufen worden
            end
--            print('ERG',erg)
            stop_timer(5)
            self.ungueltig = self.ungueltig + 1
            if self.pin_mode == 'lang' then
                if erg == 'E' then
--                    self.aktuelle_pin_eingabe = ''
                    self:activate_sondertarif(0)
                end
            elseif self.pin_mode == 'admin' then
                if erg == 'A' or erg == 'S' then
                    self.ungueltig  = 0
                    self.no_timeout = 1
                    self.aktuelle_pin_eingabe = ''
                    self.admin:activate()
                end
            end

            self.pin_feld:write('')

            if self.ungueltig > 0 then
                self.aktuelle_pin_eingabe = ''
                self.pin_wartezeit = 2000
                if     self.ungueltig == 1 then self.pin_wartezeit =  100
                elseif self.ungueltig == 2 then self.pin_wartezeit =  100
                elseif self.ungueltig == 3 then self.pin_wartezeit =  200
                elseif self.ungueltig == 4 then self.pin_wartezeit =  300
                elseif self.ungueltig == 5 then self.pin_wartezeit =  400
                elseif self.ungueltig == 6 then self.pin_wartezeit =  600
                elseif self.ungueltig == 7 then self.pin_wartezeit = 1000
                end
                self.pin_feld:write('***********')
                self.ungueltige_pin:set_visiable(0)
                start_timer(5,self.pin_wartezeit+1000,0,1)
            end

        end
    end

end

--**********************************************************************
                    
function App.activate_sondertarif (self,nr)

    self.ungueltig  = 0
    if self.broetchen == 0 then
        self.sondertarif_anzeige:set_visiable(0)
    end
    self.tarifnr = 2
    self.anzeige_parkdauer:write(self:date(t('%H:%M')))
    if tonumber(self.pay_mode[2]:read()) > 0 then
        self.muenzen:set_visiable(1)
        self.muenzen1:set_visiable(1)
        self.muenzen2:set_visiable(1)
        self.muenzen3:set_visiable(1)
        self.bargeldlos:set_visiable(1)
    else
        self.muenzen:set_visiable(-1)
        self.muenzen1:set_visiable(-1)
        self.muenzen2:set_visiable(-1)
        self.muenzen3:set_visiable(-1)
        self.bargeldlos:set_visiable(-1)
    end
    self:insert_coin(nr)

end

--**********************************************************************

function App.pruefe_pin (self,pin)

--    print(pin,self.superpin)
    if pin == self.superpin then
        return 'S'
    end
    
--    if pin == '99987' then    @@  for test
--        return 'E'
--    end

    self.anzahl_dieselbe_pin = 99999999
    local pin_defs  = self:read_pin_defs()
    local x1,x2     = string.find(pin_defs,',.'..pin..'.-,')
    if x1 ~= nil then
        local mode   = string.sub(pin_defs,x1+1,x1+1)
        local remark = string.sub(pin_defs,x1+7,x2-1)
        if trim(mode) == '' and remark ~= nil and #remark > 0 then
            mode = 'E'
        end
        if mode == 'E' then
            self.anzahl_dieselbe_pin = 1
            local x1 = 1
            local x2 = 1
            x1,x2    = string.find(remark,"%/%d+x%/")
            if x1 ~= nil and x2 ~= nil then
                x2 = string.sub(remark,x1+1,x2-2)
                self.anzahl_dieselbe_pin = fcheck(1,tonumber,x2)
                if self.anzahl_dieselbe_pin == nil then
                    self.anzahl_dieselbe_pin = 1
                end
            end
        end
        return mode
    end
    
    x1,x2 = string.find(pin_defs,',A')

    local s1       = ''   --  Superpin aus der SERIALNUMBER herstellen und dagegen pruefen
    local s2       = ''
    local sernr    = tostring(SERIALNUMBER)
    while #sernr > 0 do
        local x = string.sub(sernr,1,1)
        sernr   = string.sub(sernr,2)
        if #s1 == 0 or tonumber(x) > tonumber(string.sub(s1,#s1,#s1)) then
            s1 = s1 .. x
        else
            s2 = s2 .. x
        end
    end
    if tostring(pin) == string.sub(s1..s2,1,5) then
        x1,x2 = string.find(pin_defs,',A')  --  Suche nach Admin-Passwoerten
        if x1 == nil or math.random(1,4) == 1 then  --  wenn es schon gesetzte Admin Passwoerter in der
            return 'S'     --  PIN-Verwaltung gibt, dann muss man mehrfach versuchen mit dem Standard-Passwort
        end
    end
        
    
    
end

--**********************************************************************

function App.sprachauswahl (self,control)

    local sprache = self.adm_lang_sel:get_text(control+20)
--    print('Sprache',sprache,self.einzustellende_sprache)

    if     self.einzustellende_sprache == 30 then
        self.setup_lang1:write(sprache)
    elseif self.einzustellende_sprache == 31 then
        self.setup_lang2:write(sprache)
    elseif self.einzustellende_sprache == 32 then
        self.setup_lang3:write(sprache)
    end
    
    local langs = self.setup_lang1:read() .. self.setup_lang2:read() .. self.setup_lang3:read()
--    print('LL',langs)
    langs = langs:gsub('-','')
    langs = langs:gsub('X','')
    langs = langs:gsub(' ','')
    if #langs == 0 then
        self.adm_basic:set_text(20,self.lang_selector:read())
    else
        self.lang_selector:write(langs)
    end
    self:switch_language(1)
    
    for sid,screen in pairs(self.screens) do
        screen.act_lang = -1
    end
        
    self.adm_basic:activate()
    
end


--**********************************************************************

function App.switch_language (self,lang)

    local langs = self.lang_selector:read()
    local anz   = #langs/2
    
--    if lang == 9 then return anz end

    if anz == 1 then
        self.lang = self.lang_layout1
    elseif anz == 2 then
        self.lang = self.lang_layout2
    elseif anz == 3 then
        self.lang = self.lang_layout3
    end
    
--  1.  erstmal die Sprache waehlen und highlighten

    if lang ~= nil then
--        print('new lang',control)
        set_value( self.lang.screen_id,1, 0 )
        set_value( self.lang.screen_id,2, 0 )
        set_value( self.lang.screen_id,3, 0 )
        set_value( self.lang.screen_id,lang, 1 )
    else
--        print('ANZ',anz,x)
        local x = get_value( self.lang.screen_id,anz)
        set_value( self.lang.screen_id,  3, get_value( self.lang.screen_id,2) )
        set_value( self.lang.screen_id,  2, get_value( self.lang.screen_id,1) )
        set_value( self.lang.screen_id,  1, x )
--        print (get_value(2,1), get_value(2,2), get_value(2,3), '----')
    end

-- 2. daraus den gewaehlten Laendercode herausfinden

    local actual_country = ( get_value( self.lang.screen_id,1 ) == 1 and string.sub(langs,1,2) or '' ) .. 
                           ( get_value( self.lang.screen_id,2 ) == 1 and string.sub(langs,3,4) or '' ) .. 
                           ( get_value( self.lang.screen_id,3 ) == 1 and string.sub(langs,5,6) or '' )
                  
-- 3. daraus die Sprache herausfinden

    local actual_language = 999
    for i,country in ipairs(self.country_table) do
        if self.country_table[i] == actual_country then
            actual_language = self.ldescriptions[i]
        end
    end
    
-- 4. daraus die Sprachnummer in der Translations-Tabelle herausfinden

    local nr = 1
    while nr < 1000 do
        local try_the_language = t("LANG",nr)
        if try_the_language == "" then
            break
        end
        if try_the_language == actual_language then
            lang_nr = nr
            break
        end
        nr = nr + 1
    end

--    set_language(langcode-1)
--    lang_nr = langcode + 1
    
-- 5. den aktuellen Screen gleich an die neue Sprache anpassen

    local screen1 = self:current_screen()
--    print("SC",screen1,screen1.screen_id)
    screen1:activate("force")
    
    self:compute_shortinfo()
    
    return anz 

end

--**********************************************************************

function App.language_number (self,langcode)

    local actual_lang_nr = 1
    for i,lang in ipairs(self.country_table) do
        if lang == langcode then
            break
        end
        actual_lang_nr = actual_lang_nr + 1
    end

    return actual_lang_nr or -1

end

--**********************************************************************

function App.insert_coin (self,nr)  --  can only be entered when screen = self.coin

    if self.ticket_wird_gerade_gedruckt == 1 then
        if nr / 10 > 1 then  --  nur bei Muenzen
            if self:read_counter() > 0 then
                stop_timer(2)
                self:reset_coin()
                self:insert_coin(nr)
            end
        end
        return
    end

    self.coin:activate()
    self.nochmal:set_visiable(-1)
    self.nochmal_button:set_visiable(-1)

--    print(self.real_coins,self.hoechstparkdauer_ist_erreicht,nr)

    
    if nr > 10 then    --  eine reale Muenze wurde eingeworfen
        nr = nr - 10
        if self.real_coins == 0 then
            self.anzeige_betrag:write('0,00')
            self.muenzen:set_visiable(-1)
            self.muenzen1:set_visiable(-1)
            self.muenzen2:set_visiable(-1)
            self.muenzen3:set_visiable(-1)
            self.bargeldlos:set_visiable(-1)
            self.real_coins = 1
        end
    end
    
    print("NR",nr)
    if self.real_coins == 0 and self.hoechstparkdauer_ist_erreicht == 1 and nr > 1 then
        return
    end
    

--    print( tostring(string.gsub(self.anzeige_betrag:read(),',','.' )) )

    self.amount_in_euro = tonumber( tostring(self.anzeige_betrag:read():gsub(',','.')) )
    if nr > 0 then
        self.amount_in_euro = self.amount_in_euro + COINVALUE[nr]
    end
    self.anzeige_betrag:write( string.format('%5.2f',self.amount_in_euro):gsub('%.',',') )

    self.mwst             = fcheck(0,tonumber,self.tax_vat:read())
    if self.mwst == nil then
        self.mwst = 0
    end

    self.umsatzsteuer      = string.format('%5.2f',self.amount_in_euro * self.mwst / (100 + self.mwst)):gsub('%.',',')
--    print('MWST',self.tax_vat:read(),mwst,self.umsatzsteuer)

    local akt_time         = self:time() + 45
    local parkdauer_in_sec = self:compute_parkdauer()
    local hoechstparkdauer = tonumber(self.max_park_hour[self.tarifnr]:read()) * 3600 
                           + tonumber(self.max_park_minute[self.tarifnr]:read()) * 60
--    print("HH",hoechstparkdauer)

    self.parkzeitende_sec = math.ceil(akt_time + parkdauer_in_sec)

    local is_tagesticket = self.tagesticket[self.tarifnr]:read()
    if tonumber(is_tagesticket) == 1 then
        hoechstparkdauer = math.min(hoechstparkdauer,math.ceil(akt_time/86400) * 86400 
                                  - akt_time - 40)  -- Hoechstparkzeit auf 23:59 heutiger Tag begrenzen
    end
       
--    print(is_tagesticket,hoechstparkdauer,self.parkzeitende_sec)

    if self.parkzeitende_sec >= akt_time + hoechstparkdauer then
        self.hoechstparkdauer_ist_erreicht = 1
        self.parkzeitende_sec = akt_time + hoechstparkdauer
        self.parkzeitende_hm  = self:date(t('ZEIT'),self.parkzeitende_sec)
        self.parkzeitende_day = self:date(t('DATUM'),self.parkzeitende_sec)
        parkdauer_in_sec      = hoechstparkdauer
        if tonumber(is_tagesticket) == 1 then
            self.parkzeitende_hm  = "23:59"
            self.parkzeitende_day = self:date(t('DATUM'),akt_time)
            parkdauer_in_sec = math.ceil(akt_time/86400) * 86400 - akt_time - 40
        end
    else
        self.parkzeitende_hm  = self:date(t('ZEIT'),self.parkzeitende_sec)
        self.parkzeitende_day = self:date(t('DATUM'),self.parkzeitende_sec)
    end 

--    print('R',parkdauer_in_sec,self.amount_in_euro)

--    local tag_ist_zu_ende = 0
--    if self:date(t('DATUM')) ~= self:date(t('DATUM'),self.park_bis_in_sec) and self.is_tagesticket == '1' then
--        self.park_bis_hm  = t('23:59')
--        self.park_bis_day = self:date(t('DATUM')) 
--        tag_ist_zu_ende = 1
--    end


--    print('WWW',park_bis_in_sec,park_bis_hm,'---------------------------')

    local zusatztage = math.floor(self.parkzeitende_sec/86400) - math.floor(akt_time/86400)
    if zusatztage == 1 then
        zusatztage = "+ 1 " .. t("Tag")
    elseif zusatztage > 1 then
        zusatztage = "+ " .. tostring(zusatztage) .. "  "  .. t("Tage")
    else
        zusatztage = ""
    end


    self.anzeige_parkdauer:write(self.parkzeitende_hm)
    self.anzeige_zusatztag:write(zusatztage)
    self.hour = math.floor((parkdauer_in_sec+30)/3600)
    self.min  = math.floor(((parkdauer_in_sec+30) - self.hour*3600)/60)

--    if file_open == nil then
--        self.parkzeit:write( string.format('Parking Time: %2d:%02d',hour,min) )
--        self.header_message:write( self:date(t('DATUM'))..'\r\n'..self.park_bis_hm )
--    end

    if nr > 1 then
        self.qr_ordernr = nil
    end


    self.show_text_muenzen0 = self.show_text_muenzen
    if self.amount_in_euro >= tonumber(self.min_sum[self.tarifnr]:read()) then
        self.text1:set_visiable(1)
        self.text2:set_visiable(1)
        self.text3:set_visiable(0)
        self.text4:set_visiable(0)
        self.show_text_muenzen = 0
    else
        self.text1:set_visiable(0)
        self.text2:set_visiable(0)
        self.text3:set_visiable(1)
        self.text4:set_visiable(1)
        self.show_text_muenzen = 1
    end


    if self.coin:get_text(278) == 278 then    --  Zusatzcode, falls alte coin.tft ohne text3 und text4
        self.text1:set_visiable(1)
        self.text2:set_visiable(1)
        if self.amount_in_euro >= tonumber(self.min_sum[self.tarifnr]:read()) then
            self.text1:write("KNOPFDRUECKENA")
            self.text2:write("KNOPFDRUECKENB")
            self.show_text_muenzen = 0
        else
            self.text1:write("BITTEMUENZENA")
            self.text2:write("BITTEMUENZENB")
            self.show_text_muenzen = 1
        end
    end

    self.coin:activate(1)
    
--    print(nr,self.amount_in_euro,self.min_sum[1]:read())
    if self.hoechstparkdauer_ist_erreicht == 1 and (self.real_coins == 1 or self.amount_in_euro < 0.000001) or
              nr == 1 and self.amount_in_euro >= tonumber(self.min_sum[self.tarifnr]:read()) then
        self.last_was_press = 0
        self.falsche_qr_eingabe = {}
        if self.real_coins == 0 and self.amount_in_euro > 0.00 then
            self.no_timeout = 1
            self:renew_qr_timeout()
            math.randomseed(tonumber(self:time())*1000+tonumber(get_timer_value(29)))
            if self.qr_ordernr == nil then
                self.qr_ordernr = math.random(10002,99987)
            end
            self.qrcode_text:write("http://12park.de/pay?serial="..
                     SERIALNUMBER.."&order="..self.qr_ordernr.."&amount="..tostring(self.amount_in_euro*100))
            self.qrzaehler = self.qrzaehler + 1
            self.qrtan:write("")
            self.nochmal:set_visiable(1)
            self.nochmal_button:set_visiable(1)
            self:aktiviere_qr_tastenfeld(-1)
            self.qrcode:activate()
        else
            stop_timer(7)
            if self.show_text_muenzen ~= self.show_text_muenzen0 then
                self.text1:set_visiable(0)
                self.text2:set_visiable(0)
                self.text3:set_visiable(0)
                self.text4:set_visiable(0)
            end
--            self.uart_sperre = 1
            start_timer(7,7,0,1)
        end
    end
    
end

--**********************************************************************

function App.reset_coin (self)

    self.anzeige_drucken:set_visiable(1)
    self.anzeige_betrag:write('0,00')
    local fff = self:date(t('%H:%M'))
    self.anzeige_parkdauer:write(fff)
    self.sondertarif_anzeige:set_visiable(1)
    self.qrzaehler                     = 0
    self.tarifnr                       = 1
    self.ticket_wird_gerade_gedruckt   = 0
    self.real_coins                    = 0
    self.hoechstparkdauer_ist_erreicht = 0
    self.nochmal:set_visiable(-1)
    self.nochmal_button:set_visiable(-1)
    self.show_text_muenzen             = 0
    self.text1:set_visiable(1)
    self.text2:set_visiable(1)
    self.aktuelle_pin_eingabe          = ''
    if self.text3 ~= nil then
        self.text3:set_visiable(0)
        self.text4:set_visiable(0)
    end
    if tonumber(self.pay_mode[1]:read()) > 0 then
        self.muenzen:set_visiable(1)
        self.muenzen1:set_visiable(1)
        self.muenzen2:set_visiable(1)
        self.muenzen3:set_visiable(1)
        self.bargeldlos:set_visiable(1)
    else
        self.muenzen:set_visiable(-1)
        self.muenzen1:set_visiable(-1)
        self.muenzen2:set_visiable(-1)
        self.muenzen3:set_visiable(-1)
        self.bargeldlos:set_visiable(-1)
    end
    
end

--**********************************************************************

function App.make_tarif_table (self)

    self.tarif_table = { {  {0,0}  }, {  {0,0}  } }
    self.broetchen   = 0
    self.wohnmobil   = 0
    
    for j = 1,2,1 do

         for i = 1,10,1 do
            local stunde = self.tarif_par[2*j-1][i]:read()
            local preis  = self.tarif_par[2*j  ][i]:read()
            if j == 1 and tonumber(preis) == 0 then
                self.broetchen = 1
            end
            if j == 2 and tonumber(preis) == 0 then
                self.wohnmobil = 1
            end
            if i == 1 and trim(stunde) == "" then
                self.tarif_table[j] = nil
                break
            elseif trim(stunde) ~= "" and trim(preis) ~= "" then
                table.insert(self.tarif_table[j], {trim(stunde),trim(preis)} )
            end
        end
            
    end

end
        
--**********************************************************************

function App.compute_parkdauer (self)

    if self.tarif_table[self.tarifnr] == nil then

        local pph = fcheck(0,tonumber,self:get_price_per_hour(self.tarifnr))
        if pph == nil then
            pph = 0
        end
        pph = math.max(0,pph)

        local parkdauer_in_sec = 86400
        if pph > 0.000001 then
            parkdauer_in_sec = 3600 * self.amount_in_euro / pph
        end
        return parkdauer_in_sec
        
    else
    
        local xa = 0
        local ya = 0
        local xb = 0
        local yb = 99999999

        for i,entry in ipairs(self.tarif_table[self.tarifnr]) do
            local stunde = tonumber(entry[1])
            local preis  = tonumber(entry[2])
            if self.amount_in_euro <= tonumber(preis) then
                if preis < yb or preis == yb and stunde < xb then
                    xb = stunde
                    yb = preis
                end
            end
            if self.amount_in_euro >= preis then
                if preis > ya or preis == ya and stunde > xa then
                    xa = stunde
                    ya = preis
                end
            end
        end

        
--        print(xa,ya,xb,yb)
        
        local diff             = math.abs(yb-ya)
        local parkdauer_in_h   = math.max(xa,xb)
        if diff > 0 then
            local verhaeltnis  = (self.amount_in_euro - ya)/diff
            parkdauer_in_h     = (xa + verhaeltnis * (xb - xa))
        end
        
        return(parkdauer_in_h*3600)
        
        
    end
        

end

--**********************************************************************

function App.get_price_per_hour (self,nr)

    local pph = self.price_per_hour[self.tarifnr]:read()
    pph       = fcheck(0,tonumber,pph)
    if pph == nil then
        pph = 0
    end
    
    pph = math.floor ( pph*10 ) / 10
    pph = string.format("%3.2f",pph)
    
    return pph
    
end

--**********************************************************************

function App.get_gutschein_betrag (self,nr,betrag)

    local pph = self.price_per_hour[self.tarifnr]:read()
    pph       = fcheck(0,tonumber,pph)
    if pph == nil then
        pph = 0
    end
    
    self.voucher_percentage = (pph * 100) % 10
    if self.voucher_percentage > 8.9 then
        self.voucher_percentage = 10.0
    end

    local betrag1 = fcheck(0,tonumber,trim(string.gsub(betrag,",",".")))
    if betrag1 == nil then
        betrag1 = 0
    end
    
    betrag1 = math.ceil(betrag1) / 10
    betrag1 = betrag1 * self.voucher_percentage
    if betrag1 == 0 then
        return nil
    end
    betrag1 = string.format("%3.2f",betrag1)
    betrag1 = string.gsub(betrag1,"%.",",")
    
    return betrag1
    
end

--**********************************************************************

function App.print_parkticket (self)   --  laeuft nur auf dem coin screen

    self.coin:activate()
    self.anzeige_drucken:set_visiable(0)   --  hier die Anzeige 'Ticket wird gedruckt' einblenden
    self.ticket_wird_gerade_gedruckt = 1
    self.bargeldlos:set_visiable(-1)
    start_timer(2,2400,0,1)
    
    local parkticket = string.format("%1u",self.hour)..' '..t('Std.')..' '..string.format("%1u",self.min)..' '..t('Min.')
--    if self.parkzeitende_hm == '23:59' then
--        parkticket = t('ganzer Tag')
--    end

    local ust = '---DEL---'

    if self.mwst > 0.0001 then

        self.mwst = (self.mwst * 1000 + 1)/100
        if math.floor(self.mwst % 10) == 0 then
            self.mwst = trim(string.format('%2u',math.floor(self.mwst/10)))
        else
            self.mwst = trim(string.format('%4.1f',self.mwst/10))
            self.mwst = self.mwst:gsub('%.',',')
        end
    
        ust = t('davon') .. ' ' .. self.mwst .. '% ' .. t('Umsatzsteuer')..': '..trim(self.umsatzsteuer)..' '..CURRENT
        if #ust > 32 then
            ust = t('davon') .. ' ' .. self.mwst .. '% ' .. t('Umsatzst.')..': '..trim(self.umsatzsteuer)..' '..CURRENT
        end
        
    end
    
    local bargeld = t('Barzahlung')
    if self.real_coins == 0 then
        bargeld = t('bargeldlos')
    end
    
    local datum1 = self:date(t('DATUM'))

    local parkscheinvom = t('Parkschein vom')..' '..datum1..' '..self:date(t('ZEIT')):gsub('^0','')
    local gutscheinvom  = t('Gutschein vom')..' '..datum1..' '..self:date(t('ZEIT')):gsub('^0','')
    local parkpreis     = t('Preis')..': '..trim(self.anzeige_betrag:read())..' EUR'..' '..bargeld
    local park_dauer    = t('Parkdauer')..': '..parkticket
    local parken_bis    = t('Parken bis')..': '
    local gueltig_bis   = t('Gueltig bis')..': '
    local loc1          = self.location1:read()
    local loc2          = self.location2:read()
    local loc3          = self.location3:read()

    local sondertarif   = "---DEL---" 
    if self.tarifnr == 2 then
        sondertarif = t("Sondertarif")
    end

    self.lfd_nr          = (self.lfd_nr + 1) % 100
    local sign_text      = parkscheinvom .. parkpreis ..  ust .. park_dauer .. parken_bis .. self.parkzeitende_day ..
                          self.parkzeitende_hm:gsub('^0','') .. loc1 .. loc2 .. loc3 .. sondertarif .. tostring(self.lfd_nr)
    sign_text            = sign_text:gsub(' ','')
    local md5_text       = ( md5lib.hash(sign_text) )
    self.parkschein_sign = base27.encode( bin.hextos( self:crypt(sec_key1, md5_text) ) )
    self.parkschein_sign = self.parkschein_sign .. string.format("%02u",self.lfd_nr)

    if self.tarifnr == 2 then
        sondertarif = 'QQx1d!QQx11QQx1ba1'..sondertarif
    end

    local parkticket = {
'',
'',
'QQx1d@',
'QW',
'QW',
'QQx1ba0QQx1d!QQx00',
parkscheinvom,
parkpreis,
ust,
park_dauer,
parken_bis,
'',
'QQx1d!QQx11QQx1ba1'..self.parkzeitende_day, 
'QQx1d!QQx55QQx1ba1'..self.parkzeitende_hm:gsub('^0',''),
'QQx1d!QQx00',
loc1,
loc2,
loc3,
'',
sondertarif,
'QQx1d!QQx00',
self.parkschein_sign,
--string.sub(parkschein_sign,33,64),
'',
'',
'',
'QQx1dVQQx01',
'QW',
'QW',
'QQx1d@',
}


    local voucher = self.custom_voucher()
    if voucher ~= nil then
        for zeile in string.gmatch(voucher,'[^\n]+') do
            table.insert(parkticket,zeile)
        end
    else
        local gutschein_betrag = self:get_gutschein_betrag(self.tarifnr,trim(self.anzeige_betrag:read()))
        if gutschein_betrag ~=nil then
            
            local voucher1 = {
'',
'',
'QQx1d@',
'QW',
'QW',
'QQx1ba0QQx1d!QQx00',
gutscheinvom,
gueltig_bis,
'',
'QQx1d!QQx11QQx1ba1'..self.parkzeitende_day, 
'',
'QQx1d!QQx11QQx1ba1'..gutschein_betrag:gsub('%.',',') .. " " .. CURRENT,
'QQx1d!QQx00',
loc1,
loc2,
loc3,
'',
'QQx1d!QQx00',
self.parkschein_sign,
--string.sub(parkschein_sign,33,64),
'',
'',
'',
'QQx1dVQQx01',
'QW',
'QW',
'QQx1d@',
}

            for i,zeile in pairs(voucher1) do
--                print(zeile)
                table.insert(parkticket,zeile)
            end
        end

    end

    if self.tarifnr == 2 then
        print("PPP-------------------------------------PPP",self.aktuelle_pin_eingabe)
        local u_key = tostring(self.aktuelle_pin_eingabe) .. "," .. tostring(math.ceil(tonumber(self:time())/86400))
        print("PP_KEY",u_key)
        if self.used_pins[u_key] == nil then
            self.used_pins[u_key] = 1
        else
            self.used_pins[u_key] = self.used_pins[u_key] + 1
        end
--        self.aktuelle_pin_eingabe = ''
    end

    if #self.falsche_qr_eingabe > 1 and self.falsche_qr_eingabe[1] == "15970" and self.falsche_qr_eingabe[2] == "15463" then
        self.falsche_qr_eingabe = {}
    else
        local reports = self:read_report()  -- die Daten in das Ticketreporting hineinschreiben
        self:update_counter( -math.max(100, self.amount_in_euro*POINTS_PER_EURO ) )
        local current = "Q"
        if self.real_coins > 0 then
            current = string.sub(CURRENT,1,1)
        end
        self:write_report(reports,self:date("%y%m%d%H%M%S")..current,self.anzeige_betrag:read())
    end
    WAIT_26          = 400               --  Intervall, in dem auf den naechsten Block der Signatur-Berechnung
    self.squeeze_out = nil               --  gewartet wird. Nur volle Ticketreports werden gedruckt.
    start_timer(25,5000,0,1) 


    local sum = tonumber( self.anzahl_gedruckte_parktickets:read() )
    self.anzahl_gedruckte_parktickets:write( string.format("%1u",sum+1) )
    self:send_to_printer(parkticket)
    
    if file_open == nil then
        for i,zeile in ipairs(parkticket) do
            print(zeile)
        end
    end
        
end


--**********************************************************************


function App.custom_voucher (self)

    return nil
    
end


--**********************************************************************

function fcheck (error_val,func,arg1,arg2,arg3,arg4)

    function fcheck1 (func,arg1,arg2,arg3,arg4) fcheck_erg = func(arg1,arg2,arg3,arg4) end

    if pcall(fcheck1,func,arg1,arg2,arg3,arg4) then
        return fcheck_erg
    else
        return error_val
    end

end

--**********************************************************************

function App.update_counter (self,betrag)

    local sum = 0
    
    if betrag > 0 then  --  Beim Aufladen der Einzel-Counter denjenigen suchen, der am kleinsten ist
    
        local count0 = nil
        for i,count in ipairs(self.counter) do
            if count0 == nil or tonumber(count:read()) < tonumber(count0:read()) then
                count0 = count
            end
        end
        sum = tonumber( count0:read() )
        count0:write( string.format("%1.0f",sum+betrag) )
        
    elseif betrag < 0 then  --  beim Abziehen von Punkten einen zufaelligen Teil-Counter waehlen

        self.aktueller_teilcounter = (self.aktueller_teilcounter + 1) % #self.counter
        sum = tonumber( self.counter[ self.aktueller_teilcounter+1 ]:read() )
        self.counter[ self.aktueller_teilcounter+1 ]:write( string.format("%1.0f",sum+betrag) )

    end        

    sum = self:read_counter()
    self.counter1:write(string.format('%7u',math.floor(sum/100+0.5)))
    
end

--**********************************************************************

function App.read_counter (self)

    local sum = 0
    for i,count in ipairs(self.counter) do
--        if i < 200 then
--            tonumber(count:read())
--        end
        sum = sum + tonumber(count:read())
    end
    return sum
    
end

--**********************************************************************

function App.str_to_byteArray(self,str)
local i=1
local k=0
local bytes={}
while i<=#str do 
	if string.sub(str,i,i+2)=='QQx' then
	       	bytes[k]=tonumber('0x'..string.sub(str,i+3,i+4))
		i=i+5
	elseif string.sub(str,i,i+1)=='QW' then
		bytes={0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}
                bytes[0]=0 --'QW' erzeugt table mit 64 Nullbytes, start bei index 0, zwei Zeilen mit 'QW' zum Leeren des Buffers
		break
	else 
		bytes[k]=string.byte(str,i)
		i=i+1
	end
	k=k+1
end
return bytes 
end

--**********************************************************************

function App.read_report (self)

    local reports   = {}
    local text      = 'x'
    local signature = ''
    local checknr   = ''
    local text1     = ''
    
    local reportnr = fcheck(0,tonumber,self.reportnumber:read())
    if reportnr == nil then
        reportnr = 0
    end

    local akt_count = self:read_counter()

--    change_screen(19)
--    if true then return end

    if file_open == nil then

        local f = io.open('newreport.txt')
        if f == nil then
            text = ''
        else
            for zeile in f:lines() do
                if trim(zeile) ~= '' then
                    if signature == '' then
                        signature = zeile
                    elseif checknr == '' then
	                checknr = zeile
                    else
                        table.insert(reports,zeile)
                        text = text .. zeile .. '\n'
                    end
                end
            end
            f:close()
        end
        
    else
    
        local text  = my_read_filedata(sd_dir..'/newreport.txt')
	for zeile in string.gmatch(text,'[^\n]+') do
	    if trim(zeile) ~= '' then
                if signature == '' then
                    signature = zeile
                elseif checknr == '' then
                    checknr = zeile
                    text1   = zeile
                else
                    table.insert(reports,zeile)
                    text1 = text1 .. zeile
                end
            end
	end

    end    
  
  
    
    local md5key1 = '---'
    if #signature > 5 then
        md5key1 = self:decrypt(pub_key1, signature )
    end
    local md5key  =  md5lib.hash(text1)

    if md5key == md5key1 then
        self.report_is_integer = 1
    else
        self.report_is_integer = 0
    end

    local reportnr = fcheck(0,tonumber,self.reportnumber:read())
    if reportnr == nil then
        reportnr = 0
    end
    local akt_count = self:read_counter()

    if SERIALNUMBER .. '/' .. string.format('%1u',akt_count) .. '/' .. string.format('%1u',reportnr) ~= trim(checknr) then
        self.report_is_integer = 0
    end
         
--    self.report_is_integer = 1  --  fuer Testzwecke   @@

    return(reports)
        
end

--**********************************************************************

function App.write_report (self,reports,datum,betrag)

    if betrag ~= nil then
        table.insert(reports,tostring(datum)..'  '..trim(betrag))
    end
    
    local reportnr = fcheck(0,tonumber,self.reportnumber:read())
--    print('Q1',reportnr)
    if reportnr == nil then
        reportnr = 0
    end
    local akt_count = self:read_counter()

    local checknr   = SERIALNUMBER .. '/' .. string.format('%1u',akt_count) .. '/' .. string.format('%1u',reportnr)
--    print('QC',checknr,reports)
    local text1     = checknr
    for i,zeile in ipairs(reports) do
        text1 = text1 .. zeile
    end
    
    local signature = '---'
    if self.report_is_integer == 1 then
        local md5key  = md5lib.hash(text1)
        signature     = self:crypt(sec_key1, md5key)
    end
    
--    self.debug1:write("WRITEREPORT_SIGNA"..signature)
    
    if file_open == nil then    

        local f = io.open('newreport.txt','w') 
--        print('INIT',f,sd_dir,'--')
        if f == nil then
            return nil
        end
        f:write(signature..'\n')
        f:write(checknr..'\n')
        for i,zeile in ipairs(reports) do
            f:write(zeile..'\n')
        end
        f:close()
        
    else
    
        text = signature .. '\n' .. checknr .. '\n'
        for i,zeile in ipairs(reports) do
            text = text .. zeile .. '\n'
        end
        my_write_filedata(sd_dir..'/newreport.txt',text,over_write)
        
        if math.random(1,10) == 1 then
            text = my_read_filedata(sd_dir..'/newreport.txt')
            if text == nil or text == "" then
                self.there_is_no_sdcard = 1
            end
        end

    end
    
    return(1)
        
end

--**********************************************************************

function App.generate_report (self)

    local reports = self:read_report()
    if #reports == 0 then
        return 0
    end
    
--  1. die beiden report-spalten berechnen

    local summe   = 0.00
    local total   = 0.00
    local datum   = ''
    local datum0  = ''
    local betrag  = ''
    local spalten = { {}, {} }
    local nr      = 1  --  Spaltennummer, bei nr = 3 ist das Ticket fertig
    
    zeilennr         = 1
    local max_zeilen = fcheck(0,tonumber,self.max_zeilen:read())
--    print ("MAX_ZEILEN",max_zeilen)
    if max_zeilen == nil then
        max_zeilen = 30
    else
        max_zeilen = max_zeilen - 6
    end
    max_zeilen = math.max(max_zeilen,25)
    local heute      = self:date(t('DATUM'))
    local zeile      = ''
    
    print (max_zeilen,#reports)

    if self.squeeze_out == nil then
        if #reports < max_zeilen - 2 then
            return 0
        end
    end

    local mwst = fcheck(0,tonumber,self.tax_vat:read())
    if mwst == nil then
        mwst = 0
    end
    self.umsatzsteuersatz = tonumber(mwst)

    mwst = (mwst * 1000 + 1)/100
    if math.floor(mwst % 10) == 0 then
        mwst = string.format('%2u',math.floor(mwst/10))
    else
        mwst = trim(string.format('%4.1f',mwst/10))
        mwst = mwst:gsub('%.',',')
    end

    self.report_wird_gedruckt:set_visiable(1)

    start_timer(26,WAIT_26,0,1)
--    self:renew_timeout()
    coroutine.yield()


    local xz = 0
    while 0 == 0 do  -- gehe durch alle newreport-Zeilen

        xz = xz + 1
        
        if xz % 3 == 0 then
            start_timer(26,WAIT_26,0,1)
            coroutine.yield()
        end

        if #spalten[1] > max_zeilen or #spalten[2] > 0 then    -- Zeilen in die zweite Spalte schaffen, wenn noetig
            while 0 == 0 do
                if 
                         #spalten[1] == 0 or #spalten[1] > max_zeilen
                    or   #spalten[1] > 0 and string.sub(spalten[1][ #spalten[1] ],1,3) == '---'
                    or   #spalten[2] > 0 and string.sub(spalten[2][1],1,3) == '---'
                    or   #spalten[2] > 0 and string.sub(spalten[2][1]..' ',1,1) == ' '
                    or   #spalten[1] > 0 and string.sub(spalten[1][#spalten[1]]..' ',1,1) == ' '
--                    or   #spalten[1] > 0 and string.sub(#spalten[1][ #spalten[1] ],6,6) ~= ' '
                then
                    local z = table.remove(spalten[1])
                    table.insert(spalten[2],1,z)
                else
                    break
                end
            end
            nr = 2
            if trim(spalten[2][1]) == '' then
                table.remove(spalten[2],1)
            end
        end

        local proceed = 1
        if #reports > 0 then
            local xz1 = 1
            while xz1 < 100 do   -- Format Check fuer Zeilen im newreport.txt
                xz1 = xz1 + 1
                zeile    = table.remove(reports,1)
                datum    = string.sub(zeile,1,14)
                current  = string.sub(zeile,15,15)
                if string.sub(zeile,16,16) == " " then
                    betrag   = trim( string.sub(zeile,16):gsub(',','.') )
                else  
                    betrag   = trim( string.sub(zeile,18):gsub(',','.') )  -- wegen eines Bugs beim Eintrag in die newreport 
                end
                local x1 = fcheck(-99,tonumber,datum)
                local x2 = fcheck(-99,tonumber,betrag)
                if x1 ~= -99 and x2 ~= -99 then
                    break
                end
            end
                    
--            print('ZZ',zeile)
            uhrzeit = self:date(t('ZEIT'),tonumber(datum))
            datum   = self:date(t('DATUM'),tonumber(datum))
            
--            print(self:date(t("%y%m%d%H%M%S")))

            if     datum0 ~= datum and #spalten[2] > max_zeilen - 12   then proceed = 0
            elseif #spalten[2] > max_zeilen - 8                        then proceed = 0
--            elseif datum0 ~= '' and datum0 ~= datum and datum == heute then proceed = 0
            end
        
            if proceed == 0 then
                table.insert(reports,1,zeile)
                datum0 = 'xxx'
            end
        else
            datum0 = 'xxx'
        end 
                 
        if datum0 ~= '' and datum0 ~= datum then
            if summe < 9.99 then
                table.insert(spalten[nr],'        ------')
            elseif summe < 99.99 then
                table.insert(spalten[nr],'       -------')
            else
                table.insert(spalten[nr],'      --------')
            end
            table.insert(spalten[nr],'     '..string.format('%7.2f',summe):gsub('%.',',') .. ' ' .. string.sub(CURRENT,1,1))
            table.insert(spalten[nr],'     ')
            summe = 0.00
        end
        
        if datum0 ~= 'xxx' and datum0 ~= datum then
            table.insert(spalten[nr],datum)
            table.insert(spalten[nr],'----------')
            datum0 = datum
        end
            
        if datum0 ~= 'xxx' then
--            print(betrag)
            betrag = tonumber(betrag)
--            print(betrag)
            summe  = summe + betrag
            total  = total + betrag
            betrag = string.format('%7.2f',betrag)
            betrag = betrag:gsub('%.',',')
            table.insert(spalten[nr],uhrzeit..betrag..' '..current)
        end
        
        if datum0 == 'xxx' then
--            print('BREAK',#spalten[1],#spalten[2])
            break
        end
        
        datum0 = datum
        
    end
    
--    while #spalten[2] < max_zeilen - 4 do
--        table.insert(spalten[2],'')
--    end
    
    local ust   = self.umsatzsteuersatz / ( 100 + self.umsatzsteuersatz ) * total
    local netto = 100 / ( 100 + self.umsatzsteuersatz ) * total
    if nr == 1 and #spalten[1] > max_zeilen - 9 or #spalten[2] < 2 then
        nr = 2
    end
    if #spalten[nr] > 0 then
        table.insert(spalten[nr],'')
    end
    table.insert(spalten[nr],'NETTO:' .. string.format('%8.2f',netto):gsub('%.',','))
    if #mwst == 2 then
        table.insert(spalten[nr],t('USt')..mwst..'%' .. string.format('%8.2f',ust):gsub('%.',','))
    else
        table.insert(spalten[nr],t('USt')..mwst..'%' .. string.format('%6.2f',ust):gsub('%.',','))
    end
    table.insert(spalten[nr],'TOTAL:' .. string.format('%8.2f',total):gsub('%.',','))
        
--  2.  Ausgabefile definieren

    local reportnr = fcheck(0,tonumber,self.reportnumber:read())
    if reportnr == nil then
        reportnr = 0
    end
--    local akt_count = self:read_counter()
    
    reportnr = reportnr + 1
    if #reports == 0 then
--        do_next_report = 0
        if self.squeeze_out == nil then   --  um zu verhindern, dass 'halbe' reports ausgedruckt werden
            return
        end
    end


    local reportfile = 'ticketreport_' .. SERIALNUMBER .. '_' .. self:date('%y%m%d') .. "_" .. self:date('%H%M%S')
    reportfile = reportfile .. '_' .. string.format('%04u',reportnr) .. '.txt'

    self.jahresordner = self:date('%y')

--  3. die Spalten sind fertig, jetzt das eigentliche Protokoll schreiben

    local text1 = {}
    table.insert(text1,t('Protokoll verkaufte Parkscheine'))
    table.insert(text1,t(self.location1:read()))
    table.insert(text1,t('Protokoll-Nr.')..' '..SERIALNUMBER..' / '..string.format('%03u',reportnr))
    table.insert(text1,string.sub(t('Signatur').."          ",1,10))

    local text2 = {}
    for i = 1,2,1 do
        while #spalten[i] > 0 do
            if trim(spalten[i][ #spalten[i] ]) == '' then
                table.remove(spalten[i])
            else
               break
            end
        end
    end
            
    
    local add_leerzeile = #spalten[1] >= #spalten[2]
    while #spalten[1] > 0 or #spalten[2] > 0 do   --  hier die Spalten zusammenfuehren
        zeile = table.remove(spalten[1],1)
        if zeile == nil then
            zeile = ''
        end
        zeile = string.sub(zeile .. '                ',1,16)
        --print(zeile, table.remove(spalten[2],1))
        zeile = zeile .. '  ' .. (table.remove(spalten[2],1) or '')
        table.insert(text2,zeile)
    end

    start_timer(26,WAIT_26,0,1)
--    self:renew_timeout()
    coroutine.yield()

    
--  4. Signatur berechnen

    local signa_report = ''
    if self.report_is_integer == 1 then

        local text = ''
        for i,zeile in ipairs(text1) do
            text = text .. zeile
        end
        for i,zeile in ipairs(text2) do
            text = text .. zeile
        end
        text = text:gsub(' ','')

        local md5_report   = ( md5lib.hash(text) )
        signa_report = base27.encode( bin.hextos( self:crypt(sec_key, md5_report) ) )
        signa_report = table.remove(text1) .. signa_report
        
    end
    

--  5. jetzt den Text des Reports schreiben

--    local text = {}
--    for i,zeile in ipairs(text1) do
--        table.insert(text,zeile)
--    end
--    nr = math.ceil(#signa_report/2) 
    if signa_report == '' then
        table.remove(text1)
        table.insert(text1,' ')
        table.insert(text1,t('Nicht signierter Ausgabereport.'))
        table.insert(text1,t('Nur nachrichtliche Werte.'))
        table.insert(text1,t('Kann nicht als Umsatznachweis'))
        table.insert(text1,t('fuer die Steuerbehoerden'))
        table.insert(text1,t('verwendet werden.'))
    else
        table.insert(text1,string.sub(signa_report,1,32))
        table.insert(text1,string.sub(signa_report,33,64))
    end
    table.insert(text1,' ')
    for i,zeile in ipairs(text2) do
        table.insert(text1,zeile)
    end
    
    start_timer(26,WAIT_26,0,1)
    coroutine.yield()

--  6. jetzt die verbliebenen Ticket-eintraege zurueck in die reports-Datei schreiben

    if total > 0.00 then
        self.reportnumber:write(string.format('%8u',reportnr))
        self:write_report(reports)
    end
    
--  7. jetzt den neuen Report in die Datei schreiben und gleichzeitig drucken

    if file_open == nil and total > 0.00 then

        for i,zeile in ipairs(text1) do
            print(zeile)
        end

        fc = io.open(reportfile,'w')
        if fc ~= nil then
            for i,zeile in ipairs(text1) do
                fc:write(zeile..'\n')
            end
            fc:close()
        end

    elseif total > 0.00 then
    
        local rtxt = {
' ',
'QQx1d@',
'QW',
'QW',
'QQx1ba0QQx1d!QQx00',
' '
}

        self.debug1:write("123")

        local filetxt = ''
        for i,zeile in ipairs(text1) do
            table.insert(rtxt,string.sub(zeile,1,32))
            filetxt = filetxt..zeile..'\n'
        end

        if add_leerzeile then
            table.insert(rtxt,' ')
        end
        table.insert(rtxt,t('www.12park.de'))
        filetxt = filetxt..t('www.12park.de')..'\n'
        table.insert(rtxt,' ')
        table.insert(rtxt,' ')
        table.insert(rtxt,' ')
        table.insert(rtxt,' ')
        table.insert(rtxt,'QQx1dVQQx01')
        table.insert(rtxt,'QW')
        table.insert(rtxt,'QW')
        table.insert(rtxt,'QQx1d@')
--        table.insert(rtxt,'========')

        self.debug1:write("124")
        local rtxt1 = ""
        for i,zeile in ipairs(rtxt) do
            rtxt1 = rtxt1 .. zeile .. "\r\n"
        end

        for i,jahr in ipairs(sd_subdirs) do    --  herausfinden, ob es ein Jahres-Verzeichnis gibt
            if jahr == self.jahresordner then
                reportfile = jahr .. '/' .. reportfile
            end
        end

        my_write_filedata(sd_dir..'/'..reportfile,filetxt,over_write)

        self.debug1:write("125")

        if self.squeeze_out == nil then
            local printtext = my_read_filedata(sd_dir..'/printbuffer.txt')
            if printtext == nil then
                printtext = ""
            end
            rtxt1 = printtext .. rtxt1
	    my_write_filedata(sd_dir..'/printbuffer.txt',rtxt1,over_write)
	else
            self.debug1:write("126")
            self:send_to_printer(rtxt)
            self.debug1:write("127")
        end

    end

    if self.squeeze_out == nil then
        start_timer(25,3*WAIT_26,0,1)
    end

end


--**********************************************************************

function App.no_simulation (self)
    local kn1 = self
    local simulation = "no"
end

--**********************************************************************

function App.print_report (self)

    if file_open ~= nil then
    
        local text   = my_read_filedata(sd_dir..'/printbuffer.txt')
        local prdata = {}
        for zeile in string.gmatch(text,'[^\n]+') do
            table.insert(prdata,zeile)
        end

        if #prdata < 2 and self.press_protocol > 1 then
            WAIT_26                = 10  --  Intervall, in dem auf den naechsten Block der Signatur-Berechnung
            self.squeeze_out       = 1   --  gewartet wird. Alle Ticketreports werden gedruckt.
            self.press_protocol    = 1
            start_timer(25,10,0,1)
        else
--            if #prdata > 2 then
                self.report_wird_gedruckt:set_visiable(1)
--            end
            self:send_to_printer(prdata)
            my_write_filedata(sd_dir..'/printbuffer.txt',' ',over_write)
        end
        
    end


end
    
--**********************************************************************

function App.pin_base (self,control)

    if control == 12 then  -- kommt vom adm_ext Screen
        self.pin_offset = 0
        self.pin_list   = {}
        self:update_list()
        self:pin_screen()
        return
    end
    
    if control == 99 then
        self.adm_ext:activate()

--    print ('PIN_BASE', control)
    elseif control == 12 then
        if self.pin_offset > 0 then
--            print('PIN',12,self.pin_offset)
            self.pin_offset = 0
            self:pin_screen()
        end
        
    elseif control == 14 then
        if self.pin_offset >= self.ANZAHL_PINS then 
--            print('PIN',14,self.pin_offset)
            self.pin_offset = self.pin_offset - self.ANZAHL_PINS
            self:pin_screen()
        end
        
    elseif control == 7 then
        if self.pin_offset + self.ANZAHL_PINS + 2 < #self.pin_list then
--            print('PIN',7,self.pin_offset)
            self.pin_offset = math.min(self.pin_offset + self.ANZAHL_PINS)
            self:pin_screen()
        end

    elseif control == 8 then
        if self.pin_offset - self.ANZAHL_PINS < #self.pin_list then
--            print('PIN',8,self.pin_offset)
            self.pin_offset = #self.pin_list - self.ANZAHL_PINS 
            self:pin_screen()
        end

    else
        control = control % 10
        if control == 0 then
            control = 10
        end
        local pin_defs  = self:read_pin_defs()
        local pin_entry = self.pin_entries[control]
        local pin       = pin_entry[1]:read()
        local mode      = pin_entry[2]:read()
        local remark    = pin_entry[3]:read()
        local entry     = mode .. pin .. trim(remark)
        local x1,x2     = string.find(pin_defs,',.'..pin..'.-,')
        if x1 ~= nil then   -- Eintrag ist vorhanden, muss jetzt geandert werden
            local old_text = string.sub(pin_defs,x1,x2)
            pin_defs       = string.gsub(pin_defs,old_text,','..entry..',')
        else   -- Eintrag ist noch nicht vorhanden, muss erzeugt werden
            pin_defs = pin_defs .. entry .. ','
        end
        self.pin_definitions:write(pin_defs..',')
        self:update_list()
        if mode == 'A' then
            local nr_of_pin = 0
            while nr_of_pin < 10000 and tostring(self.pin_list[nr_of_pin]) ~= pin do
                nr_of_pin = nr_of_pin + 1
            end
            self.pin_offset = 10 * math.floor(nr_of_pin/10)
--            print(self.pin_offset)
        end
--        self.pin_offset = 0
        self:pin_screen()
    end

end

--**********************************************************************

function App.read_pin_defs (self)

    local pin_defs = self.pin_definitions:read()
    local x1,x2    = string.find(pin_defs,'.-,,')
    if x1 == nil then
        pin_defs = ','
    else
        pin_defs = string.sub(pin_defs,x1,x2-1)
    end
    return(pin_defs)
    
end

--**********************************************************************

function App.check_if_tarif2_is_necessary (self)

    local rpd = self:read_pin_defs()
    if string.find(rpd,',E') then
        self.is_tarif2:set_visiable(0)
    end

end

--**********************************************************************

function App.pin_screen (self)  --  schreibt einen PIN-Screen

    local pin_defs = self:read_pin_defs()
    
 --   print('PIN_DEFS',pin_defs)

--    self:get_pin(self.pin_offset + self.ANZAHL_PINS + 1)
    local zaehler = 0
    while zaehler < self.ANZAHL_PINS do
        zaehler      = zaehler + 1
        local entry  = self.pin_list[zaehler+self.pin_offset]
        local mode   = ' '
        local remark = ''
        local x1,x2  = string.find(pin_defs,',.'..tostring(entry)..'.-,')
        if x1 ~= nil then
            mode   = string.sub(pin_defs,x1+1,x1+1) 
            remark = string.sub(pin_defs,x1+7,x2-1)
        end 
--        print('WWW',entry,mode,remark,zaehler)
        self.pin_entries[zaehler][1]:write( entry   )
        self.pin_entries[zaehler][2]:write( mode    )
        self.pin_entries[zaehler][3]:write( remark  )
    end
    
    self.pin_admin:activate()

end

--**********************************************************************

function App.update_list (self)

    math.randomseed(tonumber(self.pin_randomseed:read()))
    
    local new_list = {}
    local zaehler  = 0

    local pin_defs = self:read_pin_defs()

    while 0 == 0 do
        nr = math.random(10009,99991)
        if #self.pin_list > 0 then
            table.remove(self.pin_list)
        end
        local x1,x2  = string.find(pin_defs,',.'..tostring(nr)..'.-,')
        local sortnr = '9'
        if x1 ~= nil then
            zaehler = zaehler + 1
            if string.sub(pin_defs,x1+1,x1+1) == 'A' then
                sortnr = '1'
            end
            pin_defs = string.gsub(pin_defs,',.'..tostring(nr)..'.-,',',')
        end
        table.insert(new_list,{sortnr..tostring(nr),nr})
        if #pin_defs < 4 and #self.pin_list == 0 then
            if #new_list % self.ANZAHL_PINS == 0 and #new_list > 1.3*zaehler then
                break
            end
        end
    end
        
    table.sort(new_list,function (a,b) return(a[1]<b[1]) end)
    for i,nr in ipairs(new_list) do
        table.insert( self.pin_list, tonumber(nr[2]) )
    end

end

--**********************************************************************

function App.pin_list_change (self)   --  Aendert alle PINs mittels einer neuen Random-Seed

    math.randomseed(tonumber(self:time())*1000+tonumber(get_timer_value(29)))
    self.pin_randomseed:write( tostring(math.random(1030507,9030507)) )
    local rs = fcheck(0,tonumber,self.pin_start:read())
    if rs == nil then
        rs = 0
    end
    if 1000000 <= rs and rs <= 9999999 then
        self.pin_randomseed:write( tostring(rs) )
    end
    
    
    math.randomseed(tonumber(self.pin_randomseed:read()))

    local pin_defs = string.sub( self:read_pin_defs(), 2 )
    local pins_new = ','
    while #pin_defs > 2 do
        local x1,x2 = string.find(pin_defs,'.-,')
        pins_new    = pins_new .. string.sub(pin_defs,1,1) .. math.random(10009,99991) .. string.sub(pin_defs,7,x2-1) .. ','
--        print(pins_new)
        pin_defs    = string.sub(pin_defs,x2+1)
    end
    
    self.pin_definitions:write(pins_new..',')
    local pin_defs1 = self:read_pin_defs()
--    print('PP',pin_defs1)
    self.pin_list = {}
    
end


--**********************************************************************

function App.activate_ticketslot (self)

    local tan = self.ticketslot_tan1:read() .. self.ticketslot_tan2:read() .. self.ticketslot_tan3:read() 

--    set_text(14,11,tostring(self.flash_is_not_signed) .. ' ' .. tostring(self.report_is_integer))

    self.no_timeout = 0
    local rid       = self.request_id
    self.request_id = 0
    self:renew_timeout()
    
--    print('SR',rid)

    if self.flash_is_not_signed == 1 and rid == 0 then   --  falls jemand einfach beim Speicher signieren
        self:disable_device()         --  auf OK drueckt, ohne vorher eine TAN angefordert zu haben
        return                          
    end
    

    if rid > 86000 then

        if self:check_tan(0, tostring(rid) ,tan) == 1 then
            if rid > 93000 then
                self:check_flash_signature('write new signature')
                self.flash_is_not_signed = 0
            end
            local reports = self:read_report()
            self.report_is_integer = 1
            self:write_report(reports)
            self:update_counter(0)
            set_backlight(self.BLACKSCREEN)
            self.standby:activate()
        end
        
        if self.flash_is_not_signed == 1 then
            print('not ready')
            self:disable_device()
            return
        end
        
--        print('FR',self.flash_is_not_signed,self.report_is_integer)
        
        if self.report_is_integer == 1 then
            return
        end

    end
    
    if self.report_is_integer == 0 then
        self.report_is_integer = 2  -- 2 weil wir mit nicht signiertem reportfile arbeiten
        self:update_counter(0)
        set_backlight(self.BLACKSCREEN)
        self.standby:activate()
        return
    end
    
    local stored_pins = self.ticketslot_pin:read()
    local pin1        = ''
    self.no_timeout   = 1
    self.admin:activate()
    while #stored_pins > 4 do
        if file_open ~= nil then
            feed_dog()
        end
        self:renew_timeout()
        local pin   = string.sub(stored_pins,1,5)
--        print(pin)
        stored_pins = string.sub(stored_pins,6)
        if self:check_tan(0, pin, tan) == 1 then
            local ri = self.report_is_integer
            local reports = self:read_report()
            self.ticketslot_pin:write(pin1 .. stored_pins)
            self:update_counter(300000)
            self.report_is_integer = ri            
            self:write_report(reports)
            return
        else
            pin1 = pin1 .. pin
        end
    end
    
end

--**********************************************************************

function App.disable_device (self,no_screen_change)

    for i = 0, 31, 1 do
        stop_timer(i)
    end
    
    if no_screen_change == nil then
        set_backlight(100)
        self.not_ready:activate()
    end
    self.no_timeout      = 1
--    self.uart_sperre     = 1
    self.last_was_press  = 0
    DISABLED             = 1

end

--**********************************************************************


function App.on_timer (self,tid)

--    print('Timer call'..tostring(tid))

    if tid == 0 then
--        self.uart_sperre = 0
        local reports = self:read_report()  -- die Daten in das Ticketreporting reinschreiben
        local betr    = self.anzeige_betrag:read()
        if trim(betr) ~= '0,00' and self.real_coins == 1 then
            self:write_report(reports,self:date("%y%m%d%H%M%S")..string.sub(CURRENT,1,1),betr)
            self:reset_coin()
--            self.anzeige_betrag:write('0,00')
        end
        set_backlight(self.BLACKSCREEN)
        self.standby:activate()

    
    elseif tid == 1 and self.no_timeout == 0 then  --  @@
--        self.uart_sperre = 0
        self.pin_feld:write('')
--        self.sondertarif_anzeige:set_visiable(1)
--        self.tarifnr = 1
--        self.anzeige_betrag:write('0,00') 
        self.ungueltig = 0
        self:switch_language(1)
        set_backlight(self.BLACKSCREEN)
        self.standby:activate()

    elseif tid == 2 then
        self:reset_coin()
        self.admin_count = 0
--        self.uart_sperre = 0
        if self:read_counter() == 0 or self.there_is_no_sdcard == 1 then
            self:disable_device()
            return
        else
            self.no_timeout = 0
            self:renew_timeout()
            self:switch_language(1)
            self:choose_flags()
            self.lang:activate()
        end
    
    elseif tid == 3 and self.no_timeout == 0 then 
--        self.uart_sperre = 0
        if file_open == nil then
            print('LOW BACKLIGHT')
        end
        set_backlight(self.LOW_BACKLIGHT)
        self.ueberdeckung:set_visiable(1)
        if file_open == nil then
            self.show_low:set_visiable(1)
        end

    elseif tid == 4 then
        self.admin_count = 0
        self.control0    = nil

    elseif tid == 5 then
--        self.uart_sperre = 0
        local text = trim(self.pin_feld:read())
        self.pin_feld:write( string.sub(text,1,#text-1) )
        self:renew_timeout()
        if #text == 1 then
            self.pin_wartezeit = 0
            self.ungueltige_pin:set_visiable(1)
        else
            start_timer(5,self.pin_wartezeit,0,1)
        end

    elseif tid == 6 then
--        uart_send_data( self:str_to_byteArray('     '))
--        uart_send_data( self:str_to_byteArray('     '))
--        uart_send_data( self:str_to_byteArray('     '))

--    elseif tid == 7 then
--        self:uarthandler()

    elseif tid == 7 then
        self:print_parkticket()
--        self.uart_sperre = 0

--    elseif tid == 8 then
--        self.long_press = 1

    elseif tid == 9 then
--        table.insert(self.uartbuffer,0)
        self.long_press    = 1
--        self.catch_release = 1
        self:on_uart_recv_data( { [0] = 0xEE, [1] = 0xB1, [2] = 22 }  )
--        self:on_control_notify1( self:current_screen().screen_id , 80)

    elseif tid == 18 then
        self.no_timeout = 0
        if self.qrzaehler > 5 then
            self:on_timer(2)
        else
            self.coin:activate()
        end
        self:renew_timeout()

    elseif tid == 19 then
        self.qr_invalid:set_visiable(-1)
        self.qrtan:write("")


    elseif tid == 21 then
        self:on_uart_recv_data( { [0] = 0xEE, [1] = 0xB1, [2] = 21 } )

    elseif tid == 22 then
        self:on_uart_recv_data( { [0] = 0xEE, [1] = 0xB1, [2] = 22 } )

    elseif tid == 23 then
        self:send_to_printer1( self.print_data )
        start_timer(24,1,0,1)
        
    elseif tid == 24 then
        self.report_wird_gedruckt:set_visiable(0)

    elseif tid == 25 then
--        self.uart_sperre = 0
        self.debug1:write("Start to generate report with thread")
        function thr1 () self:generate_report() end
        xzaehl = 0
        if file_open == nil then
            WAIT_26 = 10
        end
        self.generate_report_thread = coroutine.create( thr1 )
        coroutine.resume(self.generate_report_thread)
        
    elseif tid == 26 then
--        self:renew_timeout()   @@  aktivieren fuer test
        xzaehl = xzaehl + 1
        self.debug1:write("Resume ROUND "..tostring(xzaehl))
        coroutine.resume(self.generate_report_thread)

    elseif tid == 29 then
        start_timer(29,60000,0,1)

    end

end

--**********************************************************************

function App.xxon_uart_recv_data(self,packet,real_knob)

--    print("UART_SPERRE",uart_sperre)
--    if self.uart_sperre == 1 then
--        return
--    end

    if HAS_KNOB ~= 1 and real_knob ~= nil then
        local bed  = HAS_KNOB == 0
        HAS_KNOB   = 1
        self:coins_and_knob(bed)
    end

    if packet[0]==0xEE and packet[1]==0xB1 then
        if packet[2]==0x10  then
            if     packet[3] ==  0x01 then coin_nr = 1081
            elseif packet[3] ==  0x02 then coin_nr = 1082
            elseif packet[3] ==  0x05 then coin_nr = 1083
            elseif packet[3] ==  0x0a then coin_nr = 1084
            elseif packet[3] ==  0x14 then coin_nr = 1085
            end
            self:on_control_notify1( self:current_screen() ,coin_nr)
        elseif packet[2] == 21  then
            self:press_action()
        elseif packet[2] == 22  then
            self:release_action()
        end
    end

end

--**********************************************************************

function App.on_uart_recv_data(self,packet,real_knob)


    if HAS_KNOB == 0 and real_knob ~= nil then  --  oh, es ist ja DOCH ein Knopf da!
        HAS_KNOB = 1
        self.knob1:set_visiable(-1)
    end

    local screen = self:current_screen()
    if screen == nil then
        screen = self.standby
    end
--    print("GCS",get_current_screen())

--    local pr_re = 1
--    print("SID",screen,self.standby)
--    if screen == self.standby then
--        pr_re = 2
--    end
    local pr_re = self.press_release

    print("PR_RE",pr_re,screen)

    if packet[0]==0xEE and packet[1]==0xB1 then
        if packet[2]==0x10  then
            if     packet[3] ==  0x01 then coin_nr = 1081
            elseif packet[3] ==  0x02 then coin_nr = 1082
            elseif packet[3] ==  0x05 then coin_nr = 1083
            elseif packet[3] ==  0x0a then coin_nr = 1084
            elseif packet[3] ==  0x14 then coin_nr = 1085
            end
            self:on_control_notify1( self:current_screen() ,coin_nr)
        elseif packet[2] == 21  then
--    print("PP",21,pr_re)
--            table.insert(self.uartbuffer,1)
--            if self.uart_sperre == 1 then
--                return
--            end
            self.last_was_press   = 1
            self.long_press       = 0
--            self.long_was_pressed = 0
            if pr_re > 0 then
                stop_timer(9)
                self.akt_screen = screen
                start_timer(9,1500,0,1)
            end
--            set_text(1,21,"PRESS")
--            set_text(1,22,"--")
--            set_backlight(30)
--            print(screen)
            if pr_re < 2 then
                self:on_control_notify1( screen.screen_id , 80)
            end
        elseif packet[2] == 22  then
--            if self.uart_sperre == 1 then
--                return
--            end
--    print("PP",22)
--            table.insert(self.uartbuffer,0)
            if pr_re > 0 then
                stop_timer(9)
            end
--            set_text(1,22,"RELEASE")
--            set_text(1,21,"--")
--            print("LWP",self.last_was_press)
            if self.last_was_press == 1 and (self.long_press == 1 or pr_re == 2) then
                self:on_control_notify1( screen.screen_id , 80)
--            else
--                if self.long_was_pressed == 1 then
--                    self:on_control_notify1( self:current_screen().screen_sid , 80)
--                end
            end            
            self.last_was_press = 0
        end
    end

end



--**********************************************************************

function App.press_action (self)

--  print('              PP',21)
--      table.insert(self.uartbuffer,1)
    local pr_re, screen = self:get_pr_re()
    self.last_was_press   = 1
    self.long_press       = 0
    self.long_was_pressed = 0
    if pr_re > 0 then
        stop_timer(9)
        self.akt_screen = screen
        start_timer(9,1500,0,1)
    end
    set_text(1,21,'PRESS')
    set_text(1,22,'--')
    set_backlight(30)
--    print(screen)
    if pr_re < 2 then
        self:on_control_notify1( screen.screen_id , 80)
    end

end


--**********************************************************************

function App.release_action (self)

--    print('            PP',22)
--    table.insert(self.uartbuffer,0)
    local pr_re, screen = self:get_pr_re()
    if pr_re > 0 then
        stop_timer(9)
    end
    set_text(1,22,'RELEASE')
    set_text(1,21,'--')
--    print('LWP',self.last_was_press)
    if self.last_was_press == 1 and (self.long_press == 1 or pr_re == 2) then
        self:on_control_notify1( screen.screen_id , 80)
--    else
--        if self.long_was_pressed == 1 then
--            self:on_control_notify1( self:current_screen().screen_sid , 80)
--        end
    end            
    self.last_was_press = 0

end

--**********************************************************************

function App.get_pr_re (self)

    local screen = self:current_screen()
    if screen == nil then
        screen = self.standby
    end
--    print('GCS',get_current_screen())

    local pr_re = 1
--    print('SID',screen,self.standby)
    if screen == self.standby then
        pr_re = 2
    end
--    print('PR_RE',pr_re)

    return pr_re, screen
    
end


--**********************************************************************

function App.on_uart_recv_data1(self,packet)    --   UART Test

    if packet[0]==0xEE and packet[1]==0xB1 then
        if packet[2]==0x10  then
            if     packet[3] ==  0x01 then coin_nr = 1081
            elseif packet[3] ==  0x02 then coin_nr = 1082
            elseif packet[3] ==  0x05 then coin_nr = 1083
            elseif packet[3] ==  0x0b then coin_nr = 1084
            elseif packet[3] ==  0x14 then coin_nr = 1085
            end
        elseif packet[2] == 21  then
            set_text(1,21,'PRESS')
            set_text(1,22,'--')
        elseif packet[2] == 22  then
            set_text(1,22,'RELEASE')
            set_text(1,21,'--')
        end
    end

end


--**********************************************************************

function App.uarthandler (self)   --  fruehere Version, indirekte Aufrufe

    local pr_re = self.press_release

    pr_re = 1

    local knob_event = 0
    while 0 == 0 do
    
        self.uartzaehler = self.uartzaehler + 1
        knob_event = self.uartbuffer[ self.uartzaehler ]
        if knob_event == nil then
            self.uartzaehler = self.uartzaehler - 1
            break
        end
    
        if knob_event == 1 then
            self.long_press = 0
            self.last_was_press = 1
            if pr_re < 2 then
                self:on_control_notify1( self:current_screen().screen_sid , 80)
            end
        else
            if self.last_was_press == 1 then
                self.last_was_press = 0
                if self.long_press == 1 and pr_re == 1 or pr_re == 2 then
                    self:on_control_notify1( self:current_screen().screen_sid , 80)
                end
            else
                if self.long_press == 0 then
                    self:on_control_notify1( self:current_screen().screen_sid , 80)
                end
            end
        end
    end

    start_timer(7,30,0,1)
    
end


--**********************************************************************

function App.xxuarthandler (self)

    local knob_event = 0
    while 0 == 0 do
        if #self.uartbuffer == 0 then
            break
        end
    
        knob_event = table.remove(self.uartbuffer,1)
        if knob_event == 1 then
            self.long_press = 0
            self.last_was_press = 1
            self:on_control_notify1( self:current_screen().screen_sid , 80)
        elseif knob_event == 0 or knob_event == 9 then
            if knob_event == 0 and self.last_was_press == 1 and self.long_press == 1 then
                self:on_control_notify1( self:current_screen().screen_sid , 80)
            end
            self.last_was_press = 0
        end
    end

    start_timer(7,30,0,1)
    
end


--**********************************************************************

function App.xxcheck_for_event (self,scr)

--    print('check for event')

    if self.last_press_button ~= self.press_button:read() then
--        if self.ticket_wird_gerade_gedruckt == 0 then
            self:on_control_notify1(scr,80)
--        end
        self.press_button:write('released')
        self.last_press_button = 'released'
    end
    
    if self.last_pluse_sum ~= self.pluse_sum:read() then
        local coin_diff     = tonumber(self.pluse_sum:read()) - tonumber(self.last_pluse_sum)
        self.last_pluse_sum = self.pluse_sum:read()
        if coin_diff > 0 then
            if     coin_diff ==  1 then self:on_control_notify(scr,1081)
            elseif coin_diff ==  2 then self:on_control_notify(scr,1082)
            elseif coin_diff ==  5 then self:on_control_notify(scr,1083)
            elseif coin_diff == 10 then self:on_control_notify(scr,1084)
            elseif coin_diff == 20 then self:on_control_notify(scr,1085)
            else print('coin evaluation error') end
        end
    end
    
end

--**********************************************************************

function App.renew_timeout(self)

    set_backlight(100)
    self.ueberdeckung:set_visiable(-1)
    self.show_low:set_visiable(-1)
--    self.nochmal:set_visiable(-1)
    stop_timer(0)
    stop_timer(1)
    stop_timer(3)
    stop_timer(18)
    if self.no_timeout == 0 then
        start_timer(0,self.TIMEOUTLONG*1000,0,1)
        start_timer(1,self.TIMEOUT*1000,0,1)
        start_timer(3,self.REDUCE_BACKLIGHT*1000,0,1)
    end

end


--**********************************************************************

function App.renew_qr_timeout(self)

    set_backlight(100)
    self.ueberdeckung:set_visiable(-1)
    self.show_low:set_visiable(-1)
--    self.nochmal:set_visiable(-1)
    stop_timer(0)
    stop_timer(1)
    stop_timer(3)
    stop_timer(18)
    start_timer(18,self.TIMEOUTQRCODE*1000,0,1)
    start_timer(3,self.QRCODE_REDUCE*1000,0,1)

end


--**********************************************************************

Screen         = {}
Screen.__index = Screen

--**********************************************************************

function Screen:new(app,screen_id,multilang)

    local self     = setmetatable({},Screen)
    self.app       = app
    self.screen_id = screen_id
    self.act_lang  = -1  --  language is undefined
    if multilang == nil then
        self.multilang_fields = {}
    else
        self.multilang_fields = multilang
    end
    self.app.screens[tonumber(screen_id)] = self
    self.pr_re = self.app.press_release
    return self
    
end

--**********************************************************************

function Screen.get_text (self,nr)

    if self.app.simulation == 1 then
        text = self.app.field_sim[tostring(self.screen_id)..'.'..tostring(nr)]
        if text == nil then
            text = ''
        end
    elseif nr > 0 then
        text = get_text(self.screen_id,nr)
--    else
--        local erg = get_value(self.screen_id,-nr)
--        erg       = tonumber(erg)
--        if erg < 0.5 then
--            text = '0'
--        else 
--            text = '1'
--        end
    end
    return text

end

--**********************************************************************

function Screen.set_text (self,nr,text)

    if self.app.simulation == 1 then
        self.app.field_sim[tostring(self.screen_id)..'.'..tostring(nr)] = text
    elseif nr > 0 then
        local ggg = self.screen_id
--        ZF1 = tostring(math.random(1001,99942))
        set_text(ggg,nr,text)
--        ZUFALLSWERT = ZF1 .. " " .. tostring(math.random(1001,99942)) .. " " .. tostring(self.screen_id) .. " " .. tostring(nr)
--                                .. " " .. text
--    else
--        set_value(self.screen_id,-nr,text)
    end
    
end

--**********************************************************************

function Screen.activate (self,force)

    self.screen_id = self.app.no_sdcard.screen_id  set_backlight(100)   --  SWITCH main0.lua

    if self.act_lang ~= lang_nr then
        force = "force"
    end
    
    if force == nil then
        if self.app:current_screen() == self then
            return
        end
    else
        self.act_lang = -1
    end
    
    if self.act_lang ~= lang_nr then
        for i,nr in ipairs(self.multilang_fields) do
            local text = self:get_text(nr)
            if text == "-" or text == "" or string.find(text,"+") or string.find(text,"%%") 
                           or string.find(text,"?") or string.find(text,"'") then
                text = "S" .. tostring(self.screen_id) .. "F" .. tostring(nr)
            end
--            print(nr,text,t(text))
            self:set_text(nr,t(text))
        end
    end

    if self.app.simulation == 0 then
        if self.app.screen_switch_is_blocked == 0 then
            change_screen(self.screen_id)
        end
    end
    
    self.act_lang          = lang_nr
    self.app.press_release = self.pr_re

end


--**********************************************************************

Field         = {}
Field.__index = Field

--**********************************************************************

function Field:new(app,default_text,screen,control_nr)

    local self = setmetatable({},Field)
    self.app                 = app
    self.default_text        = default_text
    self.screens             = {}
    self.flash_position      = nil
    if screen ~= nil then
        self:add_field(screen,control_nr)
    end
    
    if tonumber(self.app.use_default_values) == 1 then
        self:write(default_text,'no signature')
    end
    
    return self
    
end

--**********************************************************************

function Field.add_field (self,screen,control_nr)

    table.insert(self.screens,{screen,control_nr})
--    print(screen,control_nr,self.default_text)
    if self.app.simulation == 1 then
        screen:set_text(control_nr,self.default_text)
    end

end    

--**********************************************************************

function Field.add_flash_position (self,offset,laenge)

    self.flash_position = offset
    self.flash_length   = laenge
    
    table.insert(self.app.fields,self)
    if self.simulation == 0 then
        self:read()  -- for immediate synchronizing screen fields and flash
    end

end    

--**********************************************************************

function Field.read (self)

    if self.zwischenspeicher ~= nil then
        return(self.zwischenspeicher)
    end

    local erg = nil
    for i,control in ipairs(self.screens) do
        local screen     = control[1]
        local control_nr = control[2]
        local erg1       = screen:get_text(control_nr)
        if erg == nil then
            erg = erg1
        elseif erg ~= erg1 then
            erg = nil  -- die Screens stimmen nicht ueberein
            break
        end
    end
--    print('EE',erg)
    
    local flash_store = nil  -- Flash Store auch auslesen
    if self.flash_position ~= nil then
        if self.app.simulation == 0 then
            flash_store = read_flash_string(self.flash_position)
        end
        if flash_store == nil then
--            print("READ",self.flash_position,flash_store)
            if self.app.simulation == 0 then
                write_flash_string(self.flash_position,self.default_text)
            end
            flash_store = self.default_text
        end
        if erg ~= flash_store then
            self:update_screens(flash_store)
            erg = flash_store
        end
    else
        if erg == nil then
            self:update_screens(self.default_text)
            erg = self.default_text
        end
    end
    return(erg)

end

--**********************************************************************

function Field.write (self,text)

    self:update_screens(text)

    if self.flash_position ~= nil then
        self.zwischenspeicher = text
        self.app:check_flash_signature('update')
        self.zwischenspeicher = nil
        write_flash_string(self.flash_position,text)
    end
        
end

--**********************************************************************
            
function Field.update_screens (self,flash_store)

    for i,control in ipairs(self.screens) do
        local screen     = control[1]
        local control_nr = control[2]
        screen:set_text(control_nr,flash_store)
    end

end

--**********************************************************************

function Field.set_visiable (self,is_visiable)

    for i,sf in ipairs(self.screens) do
--        print('XX',sf[1].screen_id,sf[2],is_visiable)
        if self.app.simulation == 0 then
            set_visiable(sf[1].screen_id, sf[2], math.max(0,is_visiable) )
            if is_visiable < 0 then
                set_enable(sf[1].screen_id, sf[2], 0 )
            else
                set_enable(sf[1].screen_id, sf[2], 1 )
            end
        end
    end
    
end 

--**********************************************************************

function Field.synchronize_with_entry (self,screen,control)

    for i,scr in ipairs(self.screens) do
--        print(screen.screen_id,control,scr[1].screen_id,scr[2])
        if screen == scr[1] and control == scr[2] then
            local val = screen:get_text(scr[2])
--            print(screen,control,val)
--            print('QQ',control,val)
            self:write(val)
            screen:activate()
            return 1
        end
    end
    
    return 0

end
        
--**********************************************************************

function xxcheck_initialization_string (nr,init_string)

--  Wenn nr1 == '', dann wird eine Zufallsnummer ausgegeben
--  Wenn init_string == '', dann wird der zu nr zugehoerige init:string ausgegeben
--  Ansonsten wird eine Pruefung gegen den init_string gemacht (Ergebnis 0 oder 1)

    local nr1 = 0

    if nr == '' then
        math.randomseed(os.time())
        nr1 = tostring(math.random(11111,99999))
    else
        nr1 = tostring(nr)
    end
    
    if nr == '' then
        return nr1
    end

    local p_a = tonumber(string.sub(nr1,1,2)) + 2
    local p_b = tonumber(string.sub(nr1,3,4)) + 5
    local p_c = tonumber(string.sub(nr1,5))   + 1
    
    local sk_bytes = bin.hextobin(string.sub(sec_key,1,64) .. rk)
    local text     = {}  -- {[1] = '0'}
    while #text < 24 do
        local x = #text
        local y = (p_a + p_b * x + p_c * x * x) % #sk_bytes + 1
        table.insert(text,sk_bytes[y])
    end
    
    text = bin.bintohex(text)
    text = bin.hextos(text)
    text = base27.encode(text)
    text = string.sub(text..'XX',1,5)
    
    if init_string == '' then
        return(text)
    elseif init_string == text then
        return(1)
    else
        return(0)
    end
    
end

--**********************************************************************

function xxupdate_screen_data_from_flash ()

    local text_gesamt = ''
    for k,v in pairs(flash_data) do
        local text1 = trim(read_flash_string(v[1],v[2])) 
        set_text(v[3],v[4], text1 )
        text_gesamt = text_gesamt .. text1
    end

end

--**********************************************************************

function xxsign_flash ()

    local text = md5_flash()
--    print('MD5_new',text)
    local ciph = crypto_crypt(sec_key,text)
--    print ('CIPH_new',ciph)
    local text1 = crypto_decrypt(pub_key,ciph)
--    print('VVV',text1)
    write_flash_string(posmax+10,64, ciph )

end

--**********************************************************************

function xxmd5_flash ()

    local contents = {}

    for i,v in pairs(flash_data) do
--        print(v[1])
--        print(read_flash_string(v[1],v[2]))
        table.insert(contents,{ v[1], trim(read_flash_string(v[1],v[2])) } )
    end
    
    table.sort(contents,function (a,b) return(a[1]<b[1]) end)

    local text = 'x'
    for i,text1 in ipairs(contents) do
        text = text .. text1[2]
    end
    
    text = bin.stohex( md5lib.hash(text) )
    return text
    
end    

--**********************************************************************

function xxread_counter ()

    local zaehler = 0
    local adr     = 50000
    counter       = 0
    local c_min   = 999999
    local nrmin   = 0
    nrmax         = 0
    posmax        = 0
    posmin        = 0

    while zaehler < 10 do
        pos      = adr + zaehler*4096
        counter1 = tonumber(read_flash_string(pos,10))
        if counter1 ~= nil and counter1 > counter then
            counter = counter1
            nrmax   = zaehler
            posmax  = pos
        end
        if counter1 ~= nil and counter1 < c_min then
            c_min  = counter1
            nrmin  = zaehler
            posmin = pos
        end 
        zaehler = zaehler + 1
    end

end


--**********************************************************************

function xxcrypto_make_pin (setrandom)

--  erstmal eine Zufallszahl generieren, die dann als zu uebertragender Plaintext gilt
    local plainnumber_bin = {}
    local plainnumber     = 0

    if setrandom == 1 then
        local f  = file_open('/proc/self/stat','r')
        math.randomseed(os.time()+os.clock()+tonumber(string.sub(f:read(),1,4)))
        f.close()
    end
    while #plainnumber_bin < 29 do
        local x = math.random(0,1)
        table.insert(plainnumber_bin,tostring(x))
        plainnumber = 2*plainnumber + x  --  das wird die plainnumber in dezimaler Form
    end

--    print('P1',table.concat(plainnumber_bin))

--  die plainnumber jetzt verschluesseln
    plainnumber        = tostring(math.floor(plainnumber + 110000000.1))
--    print('P1',plainnumber)
    local ciphernumber = crypto_crypt(sec_key,plainnumber)
--    print('P3',bin.stohex(ciphernumber))

--    print(plainnumber)
--    print(bin.stohex(ciphernumber))
    
--  aus der ciphernumber jedes 9te Bits herausholen
    local cipher_bits  = crypto_pick_bits(ciphernumber)
--    print('CC',table.concat(cipher_bits))

--  jetzt plainnumber im binaeren format und die stichprobenartig gewaehlten Bits mischen:
    local text    = {}
    local zaehler = 0
    while zaehler < 29 do
        zaehler = zaehler + 1
        table.insert(text,plainnumber_bin[zaehler])
        local x = cipher_bits[zaehler] 
--        if x ~= nil then
            table.insert(text,x)
--        end
    end  
--    print(table.concat(text))

    
--  Wir haben jetzt 48 Bit in text. Diese machen wir jetzt zu einem String mit 6 Byte,
--  den wir erst noch mal RC4-verschluesseln:
    
    text = bin.bintohex(text)
    text = bin.hextos(text)
    rk1  = bin.hextos(rk)
    text = rc4.encrypt(rk1,text)
    
--    print('QQQ',bin.stohex(text))

--  Diese verschluesselten 6 Byte machen wir zu 10 Zeichen im base27 Alphabet:

    local pin = base27.encode(text)
    
--  Wegen 27^10 = ungefaehr 0.75 * 2^48 kommt es in 26,2 Prozent der Faelle vor, dass wir
--  11 Zeichen kriegen in pin. Das wollen wir aber nicht, daher nochmal rechnen fuer eine PIN
--- mit 10 Zeichen:

    if #pin > 10 then              --  Diese Rekursion kommt zu 99,5 Prozent nach 4 Runden zum Ende
        print('  repeat ...')
        return crypto_make_pin(0)  --  zu 99,9998 nach 10 Runden. Und braucht ausserdem wohl nur
    else                           --  im Desktop laufen, nicht im Geraet. Deswegen kein Problem.
        return pin
    end

end

--**********************************************************************

function xxcrypto_check_pin (pin)

--   Die PIN ist Base27 und RC4 verschluesselt, also erstmal entschluesseln,
--   dann zu einem Bitarray umwandeln

    pin = base27.decode(pin)
    rk1 = bin.hextos(rk)
    pin = rc4.decrypt(rk1,pin)
    pin = bin.stohex(pin)
    pin = bin.hextobin(pin)
    
    local plainnumber     = 0
--    local plainnumber_bin = ''
    local ciphertext_part = {}
    
    for i,v in ipairs(pin) do
        if i % 2 == 1 or i > 38 then  --  in diesen Bits ist die zu verschluesselnde Zahl
            plainnumber = 2*plainnumber + tonumber(v)
--            plainnumber_bin = plainnumber_bin .. v
        else    --  in diesen Bits ist eine Auswahl der verschluesselten Zahl
            table.insert(ciphertext_part,v)
        end
    end
    
--    print('P2',plainnumber_bin)
    
--  plainnumber ist die urspuengliche Plaintext Zahl
    local plainnumber1 = tostring(math.floor(plainnumber + 110000000.1))

--  diese wird jetzt mit dem secret key verschluesselt
    local ciphernumber = crypto_crypt(sec_key,plainnumber1)
--    print('P3',bin.stohex(ciphernumber))

--  alle Bits aus ciphertext_part sollten uebereinstimmen, pruefe das:
    if table.concat(crypto_pick_bits(ciphernumber)) == table.concat(ciphertext_part) then
        return plainnumber
    else
        return 0
    end

end

--**********************************************************************
    
function xxcrypto_pick_bits (ciphertext)

    ciphertext = bin.stohex(ciphertext)
    ciphertext = bin.hextobin(ciphertext)
    
    ciphertext_part = {}
    for i,v in ipairs(ciphertext) do
        if (i+2) % 9 == 0 and #ciphertext_part < 19 then
            table.insert(ciphertext_part,v)
        end
    end

    return ciphertext_part

end

--**********************************************************************
--  CONSOLE AREA
--**********************************************************************

if arg ~= nil and arg[1] == 'patch'  then

    local parts = 25

    local patch1 = io.open(arg[2])
    local gesamt = 0
    while 0 == 0 do
        local line1 = patch1:read()
        gesamt = gesamt + 1
        if line1 == nil then
            patch1:close()
            break
        end
    end

    patch1       = io.open(arg[2])
    local zaehler = 0
    local total   = 0
    text          = {}

    local chunk = ''
    while 0 == 0 do

        local line1 = patch1:read()
        zaehler     = zaehler + 1

        if line1 ~= nil then
            chunk = chunk .. line1 .. "\n"
        end 
        if zaehler > math.max(10,gesamt/parts) or line1 == nil then
            total      = total + 1
            zaehler    = 0
--            print(chunk)
            if #chunk < 100 then
                chunk = chunk .. "\n\n--*************************************************************" ..
                                 "*****************************************************\n\n"
            end
            local ciph = trim(App.crypt('',pub_key1,chunk))
--            print("----->\n")  print(App.decrypt('',sec_key1,ciph))
            while 0 == 0 do
                table.insert(text,string.sub(ciph,1,80))
                ciph = string.sub(ciph,81)
                if #ciph == 0 then
                    break
                end
            end
            table.insert(text,'--')
            chunk = ''
        end

        if line1 == nil then
            patch1:close()
            break
        end

    end

    local fh = io.open('patch_'..SERIALNUMBER..'.enc','w')
    if fh ~= nil then
        fh:write("--"..tonumber(total).."\n")
        for i,zeile in ipairs(text) do
            fh:write(zeile.."\n")
        end
    end
    fh:close()
    
    local text1     = SERIALNUMBER .. '/301000/0'
    local md5key    = md5lib.hash(text1)
    local signature = App.crypt('',sec_key1, md5key)

    local f = io.open('newreport.txt','w') 
    f:write(signature..'\n')
    f:write(text1..'\n')
    f:close()

end

--*************************************************************************

if arg ~= nil and arg[1] == 'tan' then
    local x = App:new(1)
    print('-----------------------------------------------')
    x:compute_tan(arg[2])
    print('-----------------------------------------------')
end

--*************************************************************************

if arg ~= nil and arg[1] == "keys" then

--    ak1 = tostring(arg[1])

    bk1 = bin.stohex( crypto.public_key( bin.hextos(ak1) ) )
    bk2 = bin.stohex( crypto.public_key( bin.hextos(ak2) ) )
  
    sec_key2 = bk1 .. ak2
    pub_key2 = bk2 .. ak1
    
    if sec_key ~= sec_key2 or pub_key ~= pub_key2 then

        local statf  = io.open('/proc/self/stat')
        math.randomseed(os.time()+os.clock()+tonumber(string.sub(statf:read(),1,4)))
        statf:close()

        ak1 = {}
        ak2 = {}
        for i = 1,256,1 do
--            ak1 = ak1 .. tostring(math.random(0,1))
--            ak2 = ak2 .. tostring(math.random(0,1))
            table.insert(ak1,tostring(math.random(0,1)))
            table.insert(ak2,tostring(math.random(0,1)))
        end
        ak1 = bin.bintohex(ak1)
        ak2 = bin.bintohex(ak2)

        bk1 = bin.stohex( crypto.public_key( bin.hextos(ak1) ) )
        bk2 = bin.stohex( crypto.public_key( bin.hextos(ak2) ) )
  
        sec_key = bk1 .. ak2
        pub_key = bk2 .. ak1

        fc = io.open('newkeys.txt','w')
        if fc ~= nil then
            fc:write('\n')
            fc:write('sec_key = \''..sec_key..'\'\n')
            fc:write('pub_key = \''..pub_key..'\'\n')
            fc:write('ak1     = \''..ak1..'\'\n')
            fc:write('ak2     = \''..ak2..'\'\n')
            fc:close()
        end
    else
        os.remove('newkeys.txt')
    end

end

--*************************************************************************

--if arg ~= nil and arg[1] == "decr" then
--    local jj = load_patch("patch_0522789.enc")
--end

--*************************************************************************

if arg ~= nil and arg[1] == "ttime" then

    zaehler = 1
    st = 1611866900
    while zaehler < 200 do
        st = st + math.random(1000,20000)
        cu = math.random(1,2)
        cu = string.sub("EQ",cu,cu)
        print(App.date('',"%y%m%d%H%M%S",st) .. cu .. "  "  .. string.format("%3.2f",math.random(2,60)/10))
        zaehler = zaehler + 1
    end
end
        




--**********************************************************************
--  TRANSLATIONS
--**********************************************************************

translation_table = [[

;LCODE;DE;EN;FR;CZ;DK;ES;FI;GR;HU;IE;IT;NL;NO;PL;RO;RU;SE;TR;
;LANG;Deutsch;English;Français;Čeština;Dansk;Español;Suomalainen;Ελληνικά;Magyar;Gaeilge;Italiano;Nederlands;Norsk;Polskie;Română;Русский;Svenska;Türk;
;S2F270;Bitte Münzen einwerfen;Please insert coins;Veuillez insérer des;Please insert coins;Please insert coins;Please insert coins;Please insert coins;Please insert coins;Please insert coins;Please insert coins;Please insert coins;Please insert coins;Please insert coins;Please insert coins;Please insert coins;Please insert coins;Please insert coins;Please insert coins;
;S2F271;-;-;pièces;-;-;-;-;-;-;-;-;-;-;-;-;-;-;-;
;S2F171;Bargeldlos bezahlen:;Cashless payment:;Payer sans argent liquide:;Cashless payment:;Cashless payment:;Cashless payment:;Cashless payment:;Cashless payment:;Cashless payment:;Cashless payment:;Cashless payment:;Cashless payment:;Cashless payment:;Cashless payment:;Cashless payment:;Cashless payment:;Cashless payment:;Cashless payment:;
;S3F171;Bargeldlos bezahlen:;Cashless payment:;Payer sans argent liquide:;Cashless payment:;Cashless payment:;Cashless payment:;Cashless payment:;Cashless payment:;Cashless payment:;Cashless payment:;Cashless payment:;Cashless payment:;Cashless payment:;Cashless payment:;Cashless payment:;Cashless payment:;Cashless payment:;Cashless payment:;
;S15F171;Bargeldlos bezahlen:;Cashless payment:;Payer sans argent liquide:;Cashless payment:;Cashless payment:;Cashless payment:;Cashless payment:;Cashless payment:;Cashless payment:;Cashless payment:;Cashless payment:;Cashless payment:;Cashless payment:;Cashless payment:;Cashless payment:;Cashless payment:;Cashless payment:;Cashless payment:;
;S3F271;Parkzeit;Parking time;Temps de stationnement;Parking time;Parking time;Parking time;Parking time;Parking time;Parking time;Parking time;Parking time;Parking time;Parking time;Parking time;Parking time;Parking time;Parking time;Parking time;
;S3F270;Für Ticket Knopf drücken;Press button for ticket;Appuyez sur le bouton;Press button for ticket;Press button for ticket;Press button for ticket;Press button for ticket;Press button for ticket;Press button for ticket;Press button for ticket;Press button for ticket;Press button for ticket;Press button for ticket;Press button for ticket;Press button for ticket;Press button for ticket;Press button for ticket;Press button for ticket;
;S3F277;-;-;pour le billet;-;-;-;-;-;-;-;-;-;-;-;-;-;-;-;
;S3F278;Bitte Münzen einwerfen;Please insert coins;Veuillez insérer des;Please insert coins;Please insert coins;Please insert coins;Please insert coins;Please insert coins;Please insert coins;Please insert coins;Please insert coins;Please insert coins;Please insert coins;Please insert coins;Please insert coins;Please insert coins;Please insert coins;Please insert coins;
;S3F279;-;-;pièces;-;-;-;-;-;-;-;-;-;-;-;-;-;-;-;
;BITTEMUENZENA;Bitte Münzen einwerfen;Please insert coins;Veuillez insérer des;Please insert coins;Please insert coins;Please insert coins;Please insert coins;Please insert coins;Please insert coins;Please insert coins;Please insert coins;Please insert coins;Please insert coins;Please insert coins;Please insert coins;Please insert coins;Please insert coins;Please insert coins;
;BITTEMUENZENB;-;-;pièces;-;-;-;-;-;-;-;-;-;-;-;-;-;-;-;
;KNOPFDRUECKENA;Für Ticket Knopf drücken;Press button for ticket;Appuyez sur le bouton;Press button for ticket;Press button for ticket;Press button for ticket;Press button for ticket;Press button for ticket;Press button for ticket;Press button for ticket;Press button for ticket;Press button for ticket;Press button for ticket;Press button for ticket;Press button for ticket;Press button for ticket;Press button for ticket;Press button for ticket;
;KNOPFDRUECKENB;-;-;pour le billet;-;-;-;-;-;-;-;-;-;-;-;-;-;-;-;
;S3F267;Ticket wird gedruckt;Ticket is printed;Le billet est imprimé;Ticket is printed;Ticket is printed;Ticket is printed;Ticket is printed;Ticket is printed;Ticket is printed;Ticket is printed;Ticket is printed;Ticket is printed;Ticket is printed;Ticket is printed;Ticket is printed;Ticket is printed;Ticket is printed;Ticket is printed;
;S3F201;Sondertarif;Special rate;Tarif spécial;Special rate;Special rate;Special rate;Special rate;Special rate;Special rate;Special rate;Special rate;Special rate;Special rate;Special rate;Special rate;Special rate;Special rate;Special rate;
;S5F217;Hauptmenü;Main menu;Menu principal;Main menu;Main menu;Main menu;Main menu;Main menu;Main menu;Main menu;Main menu;Main menu;Main menu;Main menu;Main menu;Main menu;Main menu;Main menu;
;S5F202;Lokale Einstellungen;Local settings;Paramètres locaux;Local settings;Local settings;Local settings;Local settings;Local settings;Local settings;Local settings;Local settings;Local settings;Local settings;Local settings;Local settings;Local settings;Local settings;Local settings;
;S5F201;Verfügbare Punkte:;Available points:;Points disponibles:;Available points:;Available points:;Available points:;Available points:;Available points:;Available points:;Available points:;Available points:;Available points:;Available points:;Available points:;Available points:;Available points:;Available points:;Available points:;
;S5F212;Tarif;Rate;Tarif;Rate;Rate;Rate;Rate;Rate;Rate;Rate;Rate;Rate;Rate;Rate;Rate;Rate;Rate;Rate;
;S5F213;Protokoll;Protocol;Protocole;Protocol;Protocol;Protocol;Protocol;Protocol;Protocol;Protocol;Protocol;Protocol;Protocol;Protocol;Protocol;Protocol;Protocol;Protocol;
;S5F205;Erweitert;Extended;Additionnel;Extended;Extended;Extended;Extended;Extended;Extended;Extended;Extended;Extended;Extended;Extended;Extended;Extended;Extended;Extended;
;S5F299;Beenden;Finish;Rompre;Finish;Finish;Finish;Finish;Finish;Finish;Finish;Finish;Finish;Finish;Finish;Finish;Finish;Finish;Finish;
;S5F204;Tickets seit Neustart:;Tickets since restart:;Billets depuis redémarrage:;Tickets since restart:;Tickets since restart:;Tickets since restart:;Tickets since restart:;Tickets since restart:;Tickets since restart:;Tickets since restart:;Tickets since restart:;Tickets since restart:;Tickets since restart:;Tickets since restart:;Tickets since restart:;Tickets since restart:;Tickets since restart:;Tickets since restart:;
;S6F203;Mindestbetrag;Minimum amount;Montant minimal;Minimum amount;Minimum amount;Minimum amount;Minimum amount;Minimum amount;Minimum amount;Minimum amount;Minimum amount;Minimum amount;Minimum amount;Minimum amount;Minimum amount;Minimum amount;Minimum amount;Minimum amount;
;S6F208;Preis pro Stunde;Price per hour;Prix par heure;Price per hour;Price per hour;Price per hour;Price per hour;Price per hour;Price per hour;Price per hour;Price per hour;Price per hour;Price per hour;Price per hour;Price per hour;Price per hour;Price per hour;Price per hour;
;S6F201;Mehrwertsteuer %;VAT %;T.V.A. %;VAT %;VAT %;VAT %;VAT %;VAT %;VAT %;VAT %;VAT %;VAT %;VAT %;VAT %;VAT %;VAT %;VAT %;VAT %;
;S6F204;Tarif;Rate;Tarif;Rate;Rate;Rate;Rate;Rate;Rate;Rate;Rate;Rate;Rate;Rate;Rate;Rate;Rate;Rate;
;S6F207;Höchstparkdauer;Maximum parking;Maximale de;Maximum parking;Maximum parking;Maximum parking;Maximum parking;Maximum parking;Maximum parking;Maximum parking;Maximum parking;Maximum parking;Maximum parking;Maximum parking;Maximum parking;Maximum parking;Maximum parking;Maximum parking;
;S6F206;;;stationnement;;;;;;;;;;;;;;;;
;S6F211;Tagesticket;Day ticket;Billet journalier;Day ticket;Day ticket;Day ticket;Day ticket;Day ticket;Day ticket;Day ticket;Day ticket;Day ticket;Day ticket;Day ticket;Day ticket;Day ticket;Day ticket;Day ticket;
;S6F215;Stunden;Hours;Heures;Hours;Hours;Hours;Hours;Hours;Hours;Hours;Hours;Hours;Hours;Hours;Hours;Hours;Hours;Hours;
;S6F216;Minuten;Minutes;Minutes;Minutes;Minutes;Minutes;Minutes;Minutes;Minutes;Minutes;Minutes;Minutes;Minutes;Minutes;Minutes;Minutes;Minutes;Minutes;
;S6F217;Nein;No;Non;No;No;No;No;No;No;No;No;No;No;No;No;No;No;No;
;S6F218;Ja;Yes;Oui;Yes;Yes;Yes;Yes;Yes;Yes;Yes;Yes;Yes;Yes;Yes;Yes;Yes;Yes;Yes;
;S6F220;Münzen;Coins;Monnaie;Coins;Coins;Coins;Coins;Coins;Coins;Coins;Coins;Coins;Coins;Coins;Coins;Coins;Coins;Coins;
;S6F221;QRCode;QRCode;QRCode;QRCode;QRCode;QRCode;QRCode;QRCode;QRCode;QRCode;QRCode;QRCode;QRCode;QRCode;QRCode;QRCode;QRCode;QRCode;
;S6F222;Mü+QR;Co+QR;Mo+QR;Co+QR;Co+QR;Co+QR;Co+QR;Co+QR;Co+QR;Co+QR;Co+QR;Co+QR;Co+QR;Co+QR;Co+QR;Co+QR;Co+QR;Co+QR;
;S6F233;Zahlungsweise;Payment mode;Mode de paier;Payment mode;Payment mode;Payment mode;Payment mode;Payment mode;Payment mode;Payment mode;Payment mode;Payment mode;Payment mode;Payment mode;Payment mode;Payment mode;Payment mode;Payment mode;
;S6F233;Tabelle;Table;Table;Table;Table;Table;Table;Table;Table;Table;Table;Table;Table;Table;Table;Table;Table;Table;
;S6F299;Zurück;Back;Retour;Back;Back;Back;Back;Back;Back;Back;Back;Back;Back;Back;Back;Back;Back;Back;
;S6F219;Weiter zu Tarif 2;Go to tariff 2;Aller au tarif 2;Go to tariff 2;Go to tariff 2;Go to tariff 2;Go to tariff 2;Go to tariff 2;Go to tariff 2;Go to tariff 2;Go to tariff 2;Go to tariff 2;Go to tariff 2;Go to tariff 2;Go to tariff 2;Go to tariff 2;Go to tariff 2;Go to tariff 2;
;S7F204;Erweitert;Extended;Additionnel;Extended;Extended;Extended;Extended;Extended;Extended;Extended;Extended;Extended;Extended;Extended;Extended;Extended;Extended;Extended;
;S7F299;Zurück;Back;Retour;Back;Back;Back;Back;Back;Back;Back;Back;Back;Back;Back;Back;Back;Back;Back;
;S7F201;TAN Freigabe für Tickets;Charging TAN for tickets;TAN de recharge des billets;Charging TAN for tickets;Charging TAN for tickets;Charging TAN for tickets;Charging TAN for tickets;Charging TAN for tickets;Charging TAN for tickets;Charging TAN for tickets;Charging TAN for tickets;Charging TAN for tickets;Charging TAN for tickets;Charging TAN for tickets;Charging TAN for tickets;Charging TAN for tickets;Charging TAN for tickets;Charging TAN for tickets;
;S7F212;PIN Verwaltung;PIN Administration;Administration PIN;PIN Administration;PIN Administration;PIN Administration;PIN Administration;PIN Administration;PIN Administration;PIN Administration;PIN Administration;PIN Administration;PIN Administration;PIN Administration;PIN Administration;PIN Administration;PIN Administration;PIN Administration;
;S7F203;Neue PIN Liste;New PIN list;Nouvelle liste des codes PIN;New PIN list;New PIN list;New PIN list;New PIN list;New PIN list;New PIN list;New PIN list;New PIN list;New PIN list;New PIN list;New PIN list;New PIN list;New PIN list;New PIN list;New PIN list;
;S7F208;Gerät;Device;Appareil;Device;Device;Device;Device;Device;Device;Device;Device;Device;Device;Device;Device;Device;Device;Device;
;S8F202;Sprache;Language;Langue;Language;Language;Language;Language;Language;Language;Language;Language;Language;Language;Language;Language;Language;Language;Language;
;S8F203;Standort 1;Location 1;Emplacement 1;Location 1;Location 1;Location 1;Location 1;Location 1;Location 1;Location 1;Location 1;Location 1;Location 1;Location 1;Location 1;Location 1;Location 1;Location 1;
;S8F204;Lokale Einstellungen;Local settings;Paramètres locaux;Local settings;Local settings;Local settings;Local settings;Local settings;Local settings;Local settings;Local settings;Local settings;Local settings;Local settings;Local settings;Local settings;Local settings;Local settings;
;S8F207;Datum;Date;Date;Date;Date;Date;Date;Date;Date;Date;Date;Date;Date;Date;Date;Date;Date;Date;
;S8F206;Zeit;Time;Temps;Time;Time;Time;Time;Time;Time;Time;Time;Time;Time;Time;Time;Time;Time;Time;
;S8F299;Zurück;Back;Retour;Back;Back;Back;Back;Back;Back;Back;Back;Back;Back;Back;Back;Back;Back;Back;
;S8F205;Standort 2;Location 2 ;Emplacement 2;Location 2 ;Location 2 ;Location 2 ;Location 2 ;Location 2 ;Location 2 ;Location 2 ;Location 2 ;Location 2 ;Location 2 ;Location 2 ;Location 2 ;Location 2 ;Location 2 ;Location 2 ;
;S8F211;Standort 3;Location 3;Emplacement 3;Location 3;Location 3;Location 3;Location 3;Location 3;Location 3;Location 3;Location 3;Location 3;Location 3;Location 3;Location 3;Location 3;Location 3;Location 3;
;S9F213;Drucken...;Print...;Imprimer...;Print...;Print...;Print...;Print...;Print...;Print...;Print...;Print...;Print...;Print...;Print...;Print...;Print...;Print...;Print...;
;S9F204;Protokoll;Protocol;Protocole;Protocol;Protocol;Protocol;Protocol;Protocol;Protocol;Protocol;Protocol;Protocol;Protocol;Protocol;Protocol;Protocol;Protocol;Protocol;
;S9F299;Zurück;Back;Retour;Back;Back;Back;Back;Back;Back;Back;Back;Back;Back;Back;Back;Back;Back;Back;
;S9F203;Zeilen pro Seite;Lines per page;Lignes par page;Lines per page;Lines per page;Lines per page;Lines per page;Lines per page;Lines per page;Lines per page;Lines per page;Lines per page;Lines per page;Lines per page;Lines per page;Lines per page;Lines per page;Lines per page;
;S9F202;Protokoll Zähler:;Protocol counter:;Compteur de journaux:;Protocol counter:;Protocol counter:;Protocol counter:;Protocol counter:;Protocol counter:;Protocol counter:;Protocol counter:;Protocol counter:;Protocol counter:;Protocol counter:;Protocol counter:;Protocol counter:;Protocol counter:;Protocol counter:;Protocol counter:;
;S9F205;Report wird erstellt...;Ticket is created...;Le report est creé...;Ticket is created...;Ticket is created...;Ticket is created...;Ticket is created...;Ticket is created...;Ticket is created...;Ticket is created...;Ticket is created...;Ticket is created...;Ticket is created...;Ticket is created...;Ticket is created...;Ticket is created...;Ticket is created...;Ticket is created...;
;S10F270;Gerät nicht betriebsbereit;Device not ready;Appareil non en service;Device not ready;Device not ready;Device not ready;Device not ready;Device not ready;Device not ready;Device not ready;Device not ready;Device not ready;Device not ready;Device not ready;Device not ready;Device not ready;Device not ready;Device not ready;
;S11F280;Sprach Auswahl;Language selection;Sélection de la langue;Language selection;Language selection;Language selection;Language selection;Language selection;Language selection;Language selection;Language selection;Language selection;Language selection;Language selection;Language selection;Language selection;Language selection;Language selection;
;S11F299;Zurück;Back;Retour;Back;Back;Back;Back;Back;Back;Back;Back;Back;Back;Back;Back;Back;Back;Back;
;S12F280;PIN Eingabe;PIN entry;Entrée PIN;PIN entry;PIN entry;PIN entry;PIN entry;PIN entry;PIN entry;PIN entry;PIN entry;PIN entry;PIN entry;PIN entry;PIN entry;PIN entry;PIN entry;PIN entry;
;S12F202;Ungültige PIN;Invalid PIN;Code PIN invalide;Invalid PIN;Invalid PIN;Invalid PIN;Invalid PIN;Invalid PIN;Invalid PIN;Invalid PIN;Invalid PIN;Invalid PIN;Invalid PIN;Invalid PIN;Invalid PIN;Invalid PIN;Invalid PIN;Invalid PIN;
;S14F280;TAN Freigabe;TAN validation;TAN validation;TAN validation;TAN validation;TAN validation;TAN validation;TAN validation;TAN validation;TAN validation;TAN validation;TAN validation;TAN validation;TAN validation;TAN validation;TAN validation;TAN validation;TAN validation;
;S14F217;Zurück;Back;Retour;Back;Back;Back;Back;Back;Back;Back;Back;Back;Back;Back;Back;Back;Back;Back;
;S14F207;TAN anfordern;Order a TAN;Commander un TAN;Order a TAN;Order a TAN;Order a TAN;Order a TAN;Order a TAN;Order a TAN;Order a TAN;Order a TAN;Order a TAN;Order a TAN;Order a TAN;Order a TAN;Order a TAN;Order a TAN;Order a TAN;
;S15F270;Bitte Münzen einwerfen;Please insert coins;Veuillez insérer des;Please insert coins;Please insert coins;Please insert coins;Please insert coins;Please insert coins;Please insert coins;Please insert coins;Please insert coins;Please insert coins;Please insert coins;Please insert coins;Please insert coins;Please insert coins;Please insert coins;Please insert coins;
;S15F271;;;pièces;;;;;;;;;;;;;;;;
;S16F270;Bitte Münzen einwerfen;Please insert coins;Veuillez insérer des;Please insert coins;Please insert coins;Please insert coins;Please insert coins;Please insert coins;Please insert coins;Please insert coins;Please insert coins;Please insert coins;Please insert coins;Please insert coins;Please insert coins;Please insert coins;Please insert coins;Please insert coins;
;S16F271;;;pièces;;;;;;;;;;;;;;;;
;S18F262;PIN Verwaltung;PIN Administration;Administration PIN;PIN Administration;PIN Administration;PIN Administration;PIN Administration;PIN Administration;PIN Administration;PIN Administration;PIN Administration;PIN Administration;PIN Administration;PIN Administration;PIN Administration;PIN Administration;PIN Administration;PIN Administration;
;S18F299;Zurück;Back;Retour;Back;Back;Back;Back;Back;Back;Back;Back;Back;Back;Back;Back;Back;Back;Back;
;S21F204;Neue PIN Liste;New PIN list;Liste des codes PIN;New PIN list;New PIN list;New PIN list;New PIN list;New PIN list;New PIN list;New PIN list;New PIN list;New PIN list;New PIN list;New PIN list;New PIN list;New PIN list;New PIN list;New PIN list;
;S21F299;Zurück;Back;Retour;Back;Back;Back;Back;Back;Back;Back;Back;Back;Back;Back;Back;Back;Back;Back;
;S21F270;Neue PIN Liste erzeugen.;Create new PIN list.;Créer une nouvelle liste de codes PIN;Create new PIN list.;Create new PIN list.;Create new PIN list.;Create new PIN list.;Create new PIN list.;Create new PIN list.;Create new PIN list.;Create new PIN list.;Create new PIN list.;Create new PIN list.;Create new PIN list.;Create new PIN list.;Create new PIN list.;Create new PIN list.;Create new PIN list.;
;S21F217;OK;OK;OK;OK;OK;OK;OK;OK;OK;OK;OK;OK;OK;OK;OK;OK;OK;OK;
;S21F201;Achtung, alle PINs werden geändert. Sind Sie sicher?;Attention, all PINs will be changed. Are you sure?;Attention, toutes les PINs sont changeé. Êtes-vous sûre?;Attention, all PINs will be changed. Are you sure?;Attention, all PINs will be changed. Are you sure?;Attention, all PINs will be changed. Are you sure?;Attention, all PINs will be changed. Are you sure?;Attention, all PINs will be changed. Are you sure?;Attention, all PINs will be changed. Are you sure?;Attention, all PINs will be changed. Are you sure?;Attention, all PINs will be changed. Are you sure?;Attention, all PINs will be changed. Are you sure?;Attention, all PINs will be changed. Are you sure?;Attention, all PINs will be changed. Are you sure?;Attention, all PINs will be changed. Are you sure?;Attention, all PINs will be changed. Are you sure?;Attention, all PINs will be changed. Are you sure?;Attention, all PINs will be changed. Are you sure?;
;S21F205;Abbrechen;Abort;Avorter;Abort;Abort;Abort;Abort;Abort;Abort;Abort;Abort;Abort;Abort;Abort;Abort;Abort;Abort;Abort;
;S22F203;Mindestbetrag;Minimum amount;Montant minimal;Minimum amount;Minimum amount;Minimum amount;Minimum amount;Minimum amount;Minimum amount;Minimum amount;Minimum amount;Minimum amount;Minimum amount;Minimum amount;Minimum amount;Minimum amount;Minimum amount;Minimum amount;
;S22F208;Preis pro Stunde;Price per hour;Prix par heure;Price per hour;Price per hour;Price per hour;Price per hour;Price per hour;Price per hour;Price per hour;Price per hour;Price per hour;Price per hour;Price per hour;Price per hour;Price per hour;Price per hour;Price per hour;
;S22F201;Mehrwertsteuer %;VAT %;T.V.A. %;VAT %;VAT %;VAT %;VAT %;VAT %;VAT %;VAT %;VAT %;VAT %;VAT %;VAT %;VAT %;VAT %;VAT %;VAT %;
;S22F204;Tarif 2;Rate 2;Tarif 2;Rate 2;Rate 2;Rate 2;Rate 2;Rate 2;Rate 2;Rate 2;Rate 2;Rate 2;Rate 2;Rate 2;Rate 2;Rate 2;Rate 2;Rate 2;
;S22F207;Höchstparkdauer;Maximum parking;Maximale de;Maximum parking;Maximum parking;Maximum parking;Maximum parking;Maximum parking;Maximum parking;Maximum parking;Maximum parking;Maximum parking;Maximum parking;Maximum parking;Maximum parking;Maximum parking;Maximum parking;Maximum parking;
;S22F206;;;stationnement;;;;;;;;;;;;;;;;
;S22F220;Münzen;Coins;Monnaie;Coins;Coins;Coins;Coins;Coins;Coins;Coins;Coins;Coins;Coins;Coins;Coins;Coins;Coins;Coins;
;S22F221;QRCode;QRCode;QRCode;QRCode;QRCode;QRCode;QRCode;QRCode;QRCode;QRCode;QRCode;QRCode;QRCode;QRCode;QRCode;QRCode;QRCode;QRCode;
;S22F222;Mü+QR;Co+QR;Mo+QR;Co+QR;Co+QR;Co+QR;Co+QR;Co+QR;Co+QR;Co+QR;Co+QR;Co+QR;Co+QR;Co+QR;Co+QR;Co+QR;Co+QR;Co+QR;
;S22F211;Tagesticket;Day ticket;Billet journalier;Day ticket;Day ticket;Day ticket;Day ticket;Day ticket;Day ticket;Day ticket;Day ticket;Day ticket;Day ticket;Day ticket;Day ticket;Day ticket;Day ticket;Day ticket;
;S22F215;Stunden;Hours;Heures;Hours;Hours;Hours;Hours;Hours;Hours;Hours;Hours;Hours;Hours;Hours;Hours;Hours;Hours;Hours;
;S22F216;Minuten;Minutes;Minutes;Minutes;Minutes;Minutes;Minutes;Minutes;Minutes;Minutes;Minutes;Minutes;Minutes;Minutes;Minutes;Minutes;Minutes;Minutes;
;S22F217;Nein;No;Non;No;No;No;No;No;No;No;No;No;No;No;No;No;No;No;
;S22F218;Ja;Yes;Oui;Yes;Yes;Yes;Yes;Yes;Yes;Yes;Yes;Yes;Yes;Yes;Yes;Yes;Yes;Yes;
;S22F233;Tabelle;Table;Table;Table;Table;Table;Table;Table;Table;Table;Table;Table;Table;Table;Table;Table;Table;Table;
;S22F299;Zurück;Back;Retour;Back;Back;Back;Back;Back;Back;Back;Back;Back;Back;Back;Back;Back;Back;Back;
;S23F220;Werkseinstellung bei Neustart;Factory setting on restart;Réglage d'usine au redémarrage;Factory setting on restart;Factory setting on restart;Factory setting on restart;Factory setting on restart;Factory setting on restart;Factory setting on restart;Factory setting on restart;Factory setting on restart;Factory setting on restart;Factory setting on restart;Factory setting on restart;Factory setting on restart;Factory setting on restart;Factory setting on restart;Factory setting on restart;
;S23F203;Serialnumber;Serialnumber;Serialnumber;Serialnumber;Serialnumber;Serialnumber;Serialnumber;Serialnumber;Serialnumber;Serialnumber;Serialnumber;Serialnumber;Serialnumber;Serialnumber;Serialnumber;Serialnumber;Serialnumber;Serialnumber;
;S23F202;Version;Version;Version;Version;Version;Version;Version;Version;Version;Version;Version;Version;Version;Version;Version;Version;Version;Version;
;S23F204;Gerät;Device;Appareil;Device;Device;Device;Device;Device;Device;Device;Device;Device;Device;Device;Device;Device;Device;Device;
;S23F205;Firmware;Firmware;Firmware;Firmware;Firmware;Firmware;Firmware;Firmware;Firmware;Firmware;Firmware;Firmware;Firmware;Firmware;Firmware;Firmware;Firmware;Firmware;
;S23F206;Screen;Screen;Screen;Screen;Screen;Screen;Screen;Screen;Screen;Screen;Screen;Screen;Screen;Screen;Screen;Screen;Screen;Screen;
;S23F207;Speicher bereinigen;Clean up memory;Nettoyer la mémoire;Clean up memory;Clean up memory;Clean up memory;Clean up memory;Clean up memory;Clean up memory;Clean up memory;Clean up memory;Clean up memory;Clean up memory;Clean up memory;Clean up memory;Clean up memory;Clean up memory;Clean up memory;
;S23F299;Zurück;Back;Retour;Back;Back;Back;Back;Back;Back;Back;Back;Back;Back;Back;Back;Back;Back;Back;
;S23F201;Patch;Patch;Patch;Patch;Patch;Patch;Patch;Patch;Patch;Patch;Patch;Patch;Patch;Patch;Patch;Patch;Patch;Patch;
;S23F217;Nein;No;Non;No;No;No;No;No;No;No;No;No;No;No;No;No;No;No;
;S23F218;Ja;Yes;Oui;Yes;Yes;Yes;Yes;Yes;Yes;Yes;Yes;Yes;Yes;Yes;Yes;Yes;Yes;Yes;
;S26F270;Bitte SD Karte einlegen;Please insert SD card;Veuillez insérer la carte SD;Please insert SD card;Please insert SD card;Please insert SD card;Please insert SD card;Please insert SD card;Please insert SD card;Please insert SD card;Please insert SD card;Please insert SD card;Please insert SD card;Please insert SD card;Please insert SD card;Please insert SD card;Please insert SD card;Please insert SD card;
;PARKSCHEIN;Parkschein;Parkticket;Ticket de parking;Parkticket;Parkticket;Parkticket;Parkticket;Parkticket;Parkticket;Parkticket;Parkticket;Parkticket;Parkticket;Parkticket;Parkticket;Parkticket;Parkticket;Parkticket;
;DATUM;%d.%m.%y;%d.%m.%y;%d.%m.%y;%d.%m.%y;%d.%m.%y;%d.%m.%y;%d.%m.%y;%d.%m.%y;%d.%m.%y;%d.%m.%y;%d.%m.%y;%d.%m.%y;%d.%m.%y;%d.%m.%y;%d.%m.%y;%d.%m.%y;%d.%m.%y;%d.%m.%y;
;ZEIT;%H:%M;%H:%M;%H:%M;%H:%M;%H:%M;%H:%M;%H:%M;%H:%M;%H:%M;%H:%M;%H:%M;%H:%M;%H:%M;%H:%M;%H:%M;%H:%M;%H:%M;%H:%M;
;UST;Umsatzsteuer;VAT;TVA;VAT;VAT;VAT;VAT;VAT;VAT;VAT;VAT;VAT;VAT;VAT;VAT;VAT;VAT;VAT;
;USTK;Umsatzst.;VAT;TVA;VAT;VAT;VAT;VAT;VAT;VAT;VAT;VAT;VAT;VAT;VAT;VAT;VAT;VAT;VAT;
;MINDESTENS;mind.;at least;au moins;at least;at least;at least;at least;at least;at least;at least;at least;at least;at least;at least;at least;at least;at least;at least;
;TAGESTICKET;Tagesticket ab;Dayticket from;Billiet jour de;Dayticket from;Dayticket from;Dayticket from;Dayticket from;Dayticket from;Dayticket from;Dayticket from;Dayticket from;Dayticket from;Dayticket from;Dayticket from;Dayticket from;Dayticket from;Dayticket from;Dayticket from;
;TAG;Tag;day;jour;day;day;day;day;day;day;day;day;day;day;day;day;day;day;day;
;TAGE;Tage;days;jours;days;days;days;days;days;days;days;days;days;days;days;days;days;days;days;
;S27F299;Zurück;Back;Retour;Back;Back;Back;Back;Back;Back;Back;Back;Back;Back;Back;Back;Back;Back;Back;
;S27F5;QR Code scannen, TAN bekommen, eingeben;Scan QR code, get TAN, enter;Scan QR code, obtenir TAN, taper;Scan QR code, get TAN, enter;Scan QR code, get TAN, enter;Scan QR code, get TAN, enter;Scan QR code, get TAN, enter;Scan QR code, get TAN, enter;Scan QR code, get TAN, enter;Scan QR code, get TAN, enter;Scan QR code, get TAN, enter;Scan QR code, get TAN, enter;Scan QR code, get TAN, enter;Scan QR code, get TAN, enter;Scan QR code, get TAN, enter;Scan QR code, get TAN, enter;Scan QR code, get TAN, enter;Scan QR code, get TAN, enter;
;S27F3;Ungültig;Invalid;Invalide;Invalid;Invalid;Invalid;Invalid;Invalid;Invalid;Invalid;Invalid;Invalid;Invalid;Invalid;Invalid;Invalid;Invalid;Invalid;
;S28F299;Zurück;Back;Retour;Back;Back;Back;Back;Back;Back;Back;Back;Back;Back;Back;Back;Back;Back;Back;
;S29F299;Zurück;Back;Retour;Back;Back;Back;Back;Back;Back;Back;Back;Back;Back;Back;Back;Back;Back;Back;
;S28F215;Stunden;Hours;Heures;Hours;Hours;Hours;Hours;Hours;Hours;Hours;Hours;Hours;Hours;Hours;Hours;Hours;Hours;Hours;
;S28F216;Preis;Price;Prix;Price;Price;Price;Price;Price;Price;Price;Price;Price;Price;Price;Price;Price;Price;Price;
;S28F217;Stunden;Hours;Heures;Hours;Hours;Hours;Hours;Hours;Hours;Hours;Hours;Hours;Hours;Hours;Hours;Hours;Hours;Hours;
;S28F218;Preis;Price;Prix;Price;Price;Price;Price;Price;Price;Price;Price;Price;Price;Price;Price;Price;Price;Price;
;S29F215;Stunden;Hours;Heures;Hours;Hours;Hours;Hours;Hours;Hours;Hours;Hours;Hours;Hours;Hours;Hours;Hours;Hours;Hours;
;S29F216;Preis;Price;Prix;Price;Price;Price;Price;Price;Price;Price;Price;Price;Price;Price;Price;Price;Price;Price;
;S29F217;Stunden;Hours;Heures;Hours;Hours;Hours;Hours;Hours;Hours;Hours;Hours;Hours;Hours;Hours;Hours;Hours;Hours;Hours;
;S29F218;Preis;Price;Prix;Price;Price;Price;Price;Price;Price;Price;Price;Price;Price;Price;Price;Price;Price;Price;
;PROSTD;pro Stunde;per hour;par heure;per hour;per hour;per hour;per hour;per hour;per hour;per hour;per hour;per hour;per hour;per hour;per hour;per hour;per hour;per hour;
;DAVON;davon;from that;de la;from that;from that;from that;from that;from that;from that;from that;from that;from that;from that;from that;from that;from that;from that;from that;
;PARKSCHEINVOM;Parkschein vom;Parkticket from;Ticket de parking de;Parkticket from;Parkticket from;Parkticket from;Parkticket from;Parkticket from;Parkticket from;Parkticket from;Parkticket from;Parkticket from;Parkticket from;Parkticket from;Parkticket from;Parkticket from;Parkticket from;Parkticket from;
;GUTSCHEINVOM;Gutschein vom;Voucher from;Bon d achat de;Voucher from;Voucher from;Voucher from;Voucher from;Voucher from;Voucher from;Voucher from;Voucher from;Voucher from;Voucher from;Voucher from;Voucher from;Voucher from;Voucher from;Voucher from;
;PARKDAUER;Parkdauer;Parking duration;Duree de parking;Parking duration;Parking duration;Parking duration;Parking duration;Parking duration;Parking duration;Parking duration;Parking duration;Parking duration;Parking duration;Parking duration;Parking duration;Parking duration;Parking duration;Parking duration;
;PARKENBIS;Parken bis;Parking until;Parking d ici a;Parking until;Parking until;Parking until;Parking until;Parking until;Parking until;Parking until;Parking until;Parking until;Parking until;Parking until;Parking until;Parking until;Parking until;Parking until;
;GUELTIGBIS;Gueltig bis;Valid until;Valable jusqu a;Valid until;Valid until;Valid until;Valid until;Valid until;Valid until;Valid until;Valid until;Valid until;Valid until;Valid until;Valid until;Valid until;Valid until;Valid until;
;STD;Std.;h;h;h;h;h;h;h;h;h;h;h;h;h;h;h;h;h;
;MIN;Min.;min;min;min;min;min;min;min;min;min;min;min;min;min;min;min;min;min;
;BARZAHLUNG;Barzahlung;Cash payment;Payment comptant;Cash payment;Cash payment;Cash payment;Cash payment;Cash payment;Cash payment;Cash payment;Cash payment;Cash payment;Cash payment;Cash payment;Cash payment;Cash payment;Cash payment;Cash payment;
;BARGELDLOS;bargeldlos;cashless;sans argent liqu;cashless;cashless;cashless;cashless;cashless;cashless;cashless;cashless;cashless;cashless;cashless;cashless;cashless;cashless;cashless;


]]

--************************************************************************

bypass 1.0, Devloped By El Moujahidin (the source has been moved and devloped)
Email: contact@elmoujehidin.net bypass 1.0, Devloped By El Moujahidin (the source has been moved and devloped) Email: contact@elmoujehidin.net