Attempt to integrate the calibration logic into the rest of the code.

This commit is contained in:
Viktor Dragomiretskyy 2020-07-02 01:01:20 +12:00
parent 6d01ed5477
commit 6a94c07ba9
6 changed files with 307 additions and 1161 deletions

File diff suppressed because one or more lines are too long

View file

@ -6,8 +6,8 @@ from proto9x.tls import tls
from proto9x.flash import read_flash
from proto9x.init_flash import init_flash
from proto9x.upload_fwext import upload_fwext
from proto9x.calibrate import calibrate
from proto9x.init_db import init_db
from proto9x.sensor import sensor
#usb.trace_enabled=True
#tls.trace_enabled=True
@ -31,7 +31,8 @@ upload_fwext()
restart()
print('Calibrating...')
calibrate()
sensor.open(False)
sensor.calibrate()
print('Init database...')
init_db()

View file

@ -5,9 +5,6 @@ class Blobs(Enum):
init_hardcoded_clean_slate = auto()
reset_blob = auto()
db_write_enable = auto()
identify_prg = auto()
enroll_prg = auto()
calibrate_prg = auto()
def __load_blob(blob):

View file

@ -320,758 +320,3 @@ f5b89d6e3f25a05241f32f139e898126aec8b1715cac0208831fb1205f9efb75538755b2d83931c7
2a07527b95a1fe508d0b7c8da5b9047d277508ff615c9dc9ab11596aa88d0c9734a45d81f0393219bead587d3a6f9d07c470f2abf8d7c69
92228bf0ab6ef79e46599bb0a60
''')
# FIXME this must be very specific to a particular device and constructed on the fly from multiple hardcoded tables:
identify_prg=unhex('''
027800000023000000200008000020008000000100320074000000008020200400242000005020773628200100302001003c20800008213
8000c210000482107004c210000582000005c20000060200000682005006c20014970200141742001887820018084202000942001809c20
0902a0200b19b4200300b8203b04bc201400c0200200c4200100c82002003300100000000080cc200000f503d0200000a10132004400000
00080dc20e803e0206401e420d002e8200001f0200500f8200500fc200000b8203a00000804001408000008080000080800001408300008
080000140831001c081a0032000c0000000080501101004c111e0034007801e8f4f6f4f2f4f6f7f4f1eeeceef2f5f5f8f9faf9f7f7f9fc0
0040d10121111121415131112151a1e1c1a1a1a1c1b1b18140f1012141616151615120c04030202fffdfbfbfbfaf9f4efedeeeeedf1f2f1
efeeefecece9eae9e9eaedf1f4f6f6f4f3f4f6f9fbfdfdfefdfefe000000000110232e10232e10232c10232c10232c01065010250101000
007c8078c06ff0000000000004f8b006d0300280307030990098db00b90880991858e08c1810b9190910ac1b8928a0993878a890b938889
8908c88191890ac8889289099a818a890b9a88898908d08191890ad08892890802818a095a810a0288890b5a8808d98189890ad99089890
95e8289890b5e88898908e18189890ae190898909648289890b648889096e8108e981890b6e880ae99091b9096f828a8f0b6f88918908f0
818a890af090898909768289910b76b8918a08f88792910af8888a92097c81898a0b7c09018089890b01888991097f8189920b7f0908808
9920b088889920c07030307200402000000002f0004007000000029000400700000003500040080000000170000004e002800fbb20f0000
000f00300000008700020067000a00018000000a0200000b1900008813b80b010910002e001c000200180002000000700070004d010000a
0008c003c32321e3c0a020244000400010000003000f4311e000000ff0000008d002007ff00000095003007ffffffff00000085ffffffff
04000085ffffffff08000085ffffffff0c000085ffffffff10000085ffffffff14000085ffffffff18000085ffffffff1c000085fffffff
f20000085ffffffff24000085ffffffff28000085ffffffff2c000085ffffffff30000085ffffffff34000085ffffffff38000085ffffff
ff3c000085ffffffff40000085ffffffff44000085ffffffff48000085ffffffff4c000085ffffffff50000085ffffffff54000085fffff
fff58000085ffffffff5c000085ffffffff60000085ffffffff64000085ffffffff68000085ffffffff6c0000856f33110b120e0f101010
0f11140d0c0c08080c0e0e0b0f101a100f11120f13160d0e0e100f09080a0d0a0d0d0c06090c10070c10030c0f140407090bfdff0003ff0
6060500020405fbff0003f9030504fb040604fa010505f6fe00fef3fcfefdf0f9fbfdf0f9fafaf400fffeeaf6f8f7effafefef4f7fcfff3
f6f9fd130e00fcf6fbfdfdedf5f6f7edf1f3f4f1f5f5f6ebf4f6f7edf6f6f5f2f6f7f6f1f4f8faf1f1f2f3f8f1f2f3f4f5f2f3eaf3f4f3f
3f4f5f6ebf1f0f1eff5f4f4e9f2f3f3e7f2f2f1f7fffefbf600fefbe7f4f6f5e8f6f5f2f2f4f3f3f5f5f4f4ebf4f4f1e8f4f6f4e2f1f4f2
e0f3f7f7e1f5fbfbe3f0f1f2ecf2f5f5e1f1f5f3e8eef2f6f6060403e1f3f5f3e7f3f5f8e1f0f1f1e1f3f7f9e5f2f6f6e8f5f7f8ecfbfcf
bf3ff0100eefe00fdf0fffffc131201faedfcfdfbe8f9faf7e0f0f3f1e0edf1f1eaf3f6f6dadfe1e1e6edeeeff2f4f5f4e8f2f4f1eaf7fb
faf2f6f9fbe7eff5f9eef6fafdf0f3f4f5f2f4f7f9f1f9fdfdf4fffffef300010205110905090a0a06ff010405fc060807fc090d0dfe070
808fe0b0c0d000a0c0c0305090a020608060e0909090e07080605050706090303030afefdff070000020a0302056f2909070f161e21100f
10120f0e0e140a0e12180e1011131111121414181a1a19191715111314130d1214170e0f0d0e0b080709110e090713100c0b0a090705070
909080506080a040404070605050503010002020000ff03000000fbf7f6f8fdfbf9f9fffffdfbfafaf9f9fcfaf5f4f7f4f3f2fdf9f3f202
02fffcfffffefefbf9f8f8fefdfdfcfcfefffef7f9f9f8f8fbfcfaf7f5f5f7f3f2f2f3f6f5f3f3faf8f7f8f1f0f0f4f3f1efecf4f4f2f0f
5f5f5f5f5f7f8f9f3f6fafbf3f2f2f3f3f1f2f3f2f1f1f2f7f3efeff8f2f0ecf5f4f3f1f0edeaebf4f3f1f0f8fbfcfcf0f0f2f4f2f4f6f7
f2f5f7f5f5f5f5f4f7f4f5f4f0f2f4f6f1f1f1f2f2efefeff4f3f1f000fcf8f6f0efefecf7f7f5f4f0f0f2f2f8f6f5f4f8f9f6f5f6f3f2f
1f8f9f8f700fffffcfbfaf8f8f8f9f8f8f9f9faf8f9fafbfbf6f8f8f9f1f4f8f8eff1f3f6f7f7f8f9e4e7eaebeef0f2f3f3f5f7f8f0f2f8
faf5f4f8fafafafcfbfbfdfdfbfdfe0001f9fbfcfcfdfdfffefefffffefefffefd04050402010000000505050407050706040301010b090
605090704010c0a07050e0e0c0b0c0e100f07090908080b0c0a090e111106050506050607070204070804030302060402020503ff032222
201e191b1a19191b19181d2120201618191a141516171a1a1b1c16191b1b13100f0e151312100d0b0c0e0908080a07070b0e0b0c0d0f040
4060908090b0d0b0c0d0a070704030507060503030404ff00010300030506fbfdfffffbfdfefefbfbfdfef7f7f7f7f4f5f7f7f4f8fcfcf3
f9fe02f9fafcfdfefefffef8f9faf9f9fafbfdfbfbfaf9f6f8f8f9f5f4f4f7f6f7f5f7f3f6f5f4f5f7f6f6fafaf7f5f6f6f3f3eceff2f4e
eeff1f2f6f6f6f5f8f7f5f5fafaf7f5f3f5f4f4f2f3f5f5f2f1f0f1eff2f4f5edf0f3f7eeeeeff1eef2f4f3f1f2f3f3faf9f8f8f2f1eef0
f4f1eeecf3f1f0eff0edebecf2efeeeef7f4f1f0f4f4f3f1eff0f0f0eeebedeef7f8f9f8ececefeef4f3f3f3f0ecebeff1eeeceff5f3f2f
1f2f2f2f0f5f3f2f1faf7f8f7f8f9f7f6f5f2f1eff7f4f2f0fcfbf9f7fafaf7f6f6f3f1f0f8f4f2f1f9f9f7f5ebebeae9f6f7f6f3f7f7f6
f8faf6f3f3fdfbfaf6fcf8f7f5fbf9f7f60201fffdfcfbf9f7fcfcfbf9fcfaf8f6fbfaf8f80101fffd01020200040404040604030300010
102030203040103030204050203070505040e0a09070808060509090806111112100809090a08090a0d090c0e100505090d05070a0f0709
10131d1b1c1c1b1c1e1e1616181b1c17141318151111161514141b1717161a1a1b1b11151818111115151011110f0e0e0e0e100e0a09111
213110f1110100e0d0d0e0a0a0a0904080a09030406060505060704050507050303020100fefd00000000fefe0000f6f6f8fcf5f5f9fdfc
fbf9f80302fffd00020201fffffffdf8fafafafbfcfdfef7f5f3f4f7f6f3f3f7f7f8f8f6f5f5f4f3f4f4f2f6f7f6f4f7f8f7f3f2f5f6f5f
5f6f6f7f0f3f3f3f3f4f4f5f5f6f5f7f1efeef0f1f1f2f1f2f1f1f2f1f0eeeef5f3f1f2f7f7f5f3f1f0eeeff2f1f1eff3f1f1f1f7f7f7f8
f1f5f7f8eef2f5f5efededefeff0f1f3f1f4f4f4f1f2f2f1f1f1f2f3f1f1f1f2eeedecebfafafaf5efeff0edf2f0eff0f0f0efeff1f2f7f
9efeeeff1ece9eceef3f3f5f5f6f4f4f5f4f4f4f7f1f5f8fbefefeff1f5f5f6f8f6fafafcf0f1f2f1f0f1f0eff6f9fafae8e7e9e9f2f2f3
f5f8fbfcfaf4f5f2f3f7f5f5f4f5f3f4f3f4f3f0effafaf9f8f7fafaf8f9fafaf8f6f7f7f9f9fafbf9fbfafcfeff0000000508090803040
40503050505050608080000000402030405060708090504040205030203070708090f0e0d0d0a0707090e0f0e0d100e0908100f0e0c1314
1413191617182223251f1d1a17121c1a150f12121110100f0d0b13100f0e151211101714101017161412151211100d0f0d090e0d0703090
a09070c09090a0e0a08050d0a090806040304070504040300000002fffcfe0605010001010101fdfeff0000fdfdfd00fefdfffbf9f5f6fc
f9f6f4f7f7f7f7fbfafcfdfefcfcfdfbf9fafef9fbf9f8fefefdfdf7fbfcfbf3f5f6f7f7f7f8f9f5f7f8f8f1f0f1f4f3f3f1f2f2f0f2f3f
2efeceff5f4f3f4f3f5f5f7f5f6f8fcf8f7f7f9f1f4f6f8efedeff5f1eff1f4edeff1f5f3f6fcfef3f5f6f9f4f4f7f9f1f0f0f5f2f1f1f4
f7f9fafff7f7f9fbf8f9faf9f2f7fafbf4f6f7f9f5f4f6faf0f0f3f7f1f0f0f4f0eeedeeebedeef0f5f4f8fbeaebeef4f1f4f6f9f1f1f4f
7f9f7f8faf6f8fbfdf1f1f5f8f2f0f1f5f6f8fbfef9fafbfdfafafafef4f7fb00f8fbfd00fafbfcfef3f6fcfef0f6f9fffbfbfe00ebecee
f0f5f4f6fafbfd0102f5fafefff3f6fd00f5f8fcfbf1f5f9fbf9fbfefff7f8fafaf9fcfefefbfbfcfcfafbfcfbfffefeffff000101070a0
c08070b0c08060708060a09090708060506080809090a0b0c0706090e0b04070808090808070d0d0e0c070705050c0b0a06070907030a07
06050e090300150afff81a16130e0f0b080508060809100d0d0c090a0d0b090606070e0a0807100d0c090f0d0c0a0f0d0b0a0607070800f
dfe000602fffd090704030604040206030303040403020100fdfd0000fffc00fefcfcfffefdfe00fffcfa00fcf9f8fcf9f7f800fffdfcf8
f9fcfef5f5f9fcf7f7fafcff000101fefdfcfefdfdfefef9fbfefffbf9fafcfaf9fafcf7f8fafbfafbfcfcf8f9fbfdf8faf9f9f4f4f6f8f
7fbfefff3f7fcfef7fafdfffafbfe00ff000103fbfc0002fbff0001faff0001f9fc0001f7faff0000010104fbfe0002fd000002fafdff00
f7f9fd0003050506fdfdff01f7f7f9fcfbfdfefefcff01fffe000003fd000101f9fd0000f3f9fd01f2f7fbffff040509f7f9f9fafafe000
1f9fafbfbf9fafafdfefdfcfdfafcfdfcf8fcfe01000204050104070800010103040302000406030200ff000200000000020200fe030200
00f3f4f4f3fbfbf9f900fbf9fafffefcfb030200fdfafafafafbfaf7f5fffefefaf9f8f8f6fdfefefbfd00fffffbfbfaf900ffff00fffef
dfc02fefdfc030000ff030200ff030102040705040308090806050405040603fffe06040101040201010e0a0602050401000402010300ff
010302000002fefe0204f6f7fcff080400fb040200fe0502ffff090400fe0a070301070401000602fefd07030100070604010601fefb040
1fffe00fffefefcfcfafa010201010201fefc02fffaf60200fefbfdfdfcfaf9f7f6f7fcfcfbfb000100fffbfcfdfff8f9fcfdf9fdfefffe
fe0000ff000001ff000000fdfbfafc020101020002020202020101fefcfbfdfefdfdfffbf9f6f9faf8f9fafcfdff01fefeff00fcff0003f
bffff0100010104ffff01040105060304030202030404070100000401010204000102070305070c01020306040606070406070603050609
03060a0c04090d0e080c090c0307080a00040d1000040b0f00040c11050b141803070e130508101104090f12000104060c12181bfd02070
90304090dfd00070f02060a0cfe000407fcfe04080304070806080b0e090a0d100506080b0103060902050b1204081014ffff0308feff02
0500020505f2f0eef1f8f8fcfffefefbfafbfaf9f8fbfbfcfbf9f9f7f6f4f6f9f8fafbfdfdf7f7fafcf7f6f7f9fdfbfbfcf8f9fafbfefdf
dfdfbf9fafbfbfbf9fafdfaf8f7fdfbf9f90201fffc020100fe05060806030302ffff0100fe020300fd010101fd000000fd00fefefc0200
fefc01fbf6f401fefbfa0500fbf600fcf6f1f5f4f4f6fbfaf8f700fdf9f5fdfbfbf900fef9f4fcf8f4f3fbfaf8f700fbf9f7fdf9f5f2faf
8f6f2fbf9f9f6fcf8f2eef6f5f4f3fef9f6f4f7f4f0eff6f6f6f4fafbfcfbf9f8f8f8f9fafafafcfcfaf9fdfcfcfafefefdfdfdfbfbfcfc
fcfcfe01030404000101020100fefdff0103020507060404080909040a0e0d01040304fefeff02fcff0001fcfe010101010102000202040
70a0a070204060603070a0d0607080b010204090407080d080e0f11070d0e0c090e0e0e0a0f100f0f101213080d0f120c12131106080b10
0d1215190f1114150d0e10120c1114150d11121412111112121317181414141617141316161817181512121213141314080a0d0f1b1c202
30b0a0e1513171a1d131418190f1014190c0f171b0d1015150a0d12150f12191c121211130e1012130f11131415121213161514140d0f0e
0d060809090504080df4f6fa0000000508ff030909fd010504fdff0000f6f8f9fbf8f9fcfdffff0000fdfcfcfcfcfdff00fcfdfdf9fcfbf
bfcff000100faf9f9f9fafbf9f8f8f7f4f3f8f7f4f2f8f7f7f8fbf9f6f601fdfafafbf8f5f6f9f7f6f7f9f5f4f4f9f4f3f4faf9f8f7faf7
f3effaf5f0ecf5f2ede9f8f5eeeaf3eeeae8e7e0dadafafefffaf7f8f9f8f5f4f5f4f9f8f7f5f3f2f2f0f4f4f3f0f8f8f5f2f5f4f2f1f1e
fece8f1f0efeef1edebeceeece9e8f1edecebf2f1f2f4eef0f2f4f5f5f7f7faf8f8f7f7f6f6f7f9f7f8f7f8f8f6f7f9f8f9fcfbfaf9fafb
f9f8f8fefaf7f801fefbfe00fdfbfafcf9fafcfffafbff020000ff080704020905070904050707050709090506080a000105090307090b0
6090a090505070c07090f120d0e14170c0b0d0f0a0d0b101014181b11151c1d0c0d1418101216170c0f131715181c1e14171b1c13171c1e
1314181b1a1a1918181818191516171917171a1b151717151415131117171a1e16181719191d1f221718191914181717161b1e1d1218181
925282a2c1c1d1a172023201c1c1c1b171d20211e2021211d16171819171615171c1c1b1c1313111212100f1111100f11110e0a0d14120f
0b0c0a0a0607030203100e0d0b040301ff070606060908080803030405feff0003f9fcfc00fcf9f8fa0101fffefbfcf8f9fdf9f5f6f6f4f
3f4fcfcfbfbfefafbfdf9f7f6f7f8f8f7f5f1f1efedf0eeedecf6f4f3f3f4f3edecf7f2edeaf6f5f1eef8f9f7f4f5f4f2eff3f2efeef5f3
f0ededeceae8ecececece9eae9e8e9e8e7e8eaebeceddbdee3e9f5efece9f4efeeeef3eff1f2f3f1f0f0efefefefedececebf0efeeeeede
9e9eae4e4e5e8eceae7e7ecedeeeeeae9e9eaebece9e7f5f3f4f5f4f2f0f0f5f2f0eef8f7f7f6f7f5f6f7f7f7f6f5f8f7f6f6fefefffcfa
f9f8f6f8fafbfcfd0000fe00fffaf7fafafbfaff00000300fefbfbfffefdfe010201000a0802fe05030100070503020a0702010a0807070
a090807070401ff0e0d07041311100f16141315101113151315110e1e1b17111e1d1d1d1a1718181a191714191a1c1c1e21211f1e1c1916
1f1917181e18131217191a1b1d1c1d201b1d1e1c1d1c1c1914171b2012151a1e1f2123241e252a2e262d32321a1b1d1e1716181c1f23272
6191c201e2f383c3f1c2228281a1d2021171c21251d2228291c202121191b1b1b1c1f1e1d1c1c1919171d202013181b1c111515150f1215
160c1014170405080904060a0e0b0d1111000000fe07060706090a0908050506050506060803060503fd020300fbfe0005f8f9f9fbf4f5f
3f2f5f6f5f6fcfbf9f8fdfdfaf7f6f7f8f8f2f1f2f2e9e9e7e6edeeeeecf3f6f6f5edf1f1eeeaebeeedeceae9e7ece6e4e5eae8e6e6ecea
e8e5ecebe7e6e8e6e6e8e9e9e8eae8e9e7e6e8e9e9eaeff0f0efeceae7e5e8e6e6e6eeedebe8f4f2eeebf1efedecefefefefeaeaeae9ece
be9e9ecefefebe9e9e7e6e8e8e6e5efeeebe9ebecebeae8e9eae9f5f4f4f3f0f1f3f2f0f2f5f7f4f6f9fcf6f5f7fcf4f2f6faf7f7f9fbff
fbf9f8f6f6f9fefbfcfbfefafafc00f5f8fe03fbfc000102fffcfcfaf9f9fbfffefdfefffefefcfcfdfefc0001010105040402000000010
705030306040303feff02050607090a0c09080915151210171614100b0d1212101112111b1919191718151213141412191716141d191716
161616141d211e1b191d1e1a191b1b1b222524221a1b1c1e1a1c201f1e1b1a191c1a1a1a232123242f2c2a27302f2c2c212526252124242
427282b2f1d1e22273e3d404626272b2e20201d1e2724211e28282726222324261e201f1e1b1e1f23191e1f1f1f1d1c1e1b1d1d1c131212
1619171a1a1c1e211e0f1113101215161414181716feff000106090c0e08090a100606090b080a0d1002020206fefd020306070809fbfdf
dfff2f3fafdf9fafcfdf9faf9f7f5f6f7f7f5f2f0f0f2edeae8e6e4e3e2edebedebf4f3f1eee9e8e9e9edeceeebe7e7e8e7e5e4e3e4e7e5
e4e5e6e6e5e3e7e8e9e7ebe9e8e4ebebe9e7e5e4e5e5ebece9e6edebe9e6e0dedddee5e3e4e6e5e5e8ebecebeaebeceae9eaebeaeaeceae
cedeceaedeeeceaebeeede6e9e9e9e6e8e8e7e8e7e8eae6e5e6e9e8e7e9ebf4f4f5f4f3f2f2f2f8f9f9f8fcfcfcfc0000fffdfdfffefefd
fefefdfcff00fd00fffefefeff0000000101040505030703010203fcff0102fbfe0004ff03070bfbfe0309f9fa000500ff0001050404010
20305060404030304040405060506080b0804030601fefd0a0503040c0704020d0601000b0806061815120d110f0d08120f0c0a110f0b0a
1613110d171615121717161717161411191815151e1916131d1d19151c1a1918191a17181c1b1b1826262827282829292928292a221e1c1
d2424221e2e2d2928292826224745403d2c28231f1f2223221f212220252523222320212220222123211e1d1e1e1d1d1c1e1f1f221c1c1e
21191f22251c1d1f1e1e1c1d1c11100f10110f0f1215141310020204060c0c0c0f12121113111315151211110e080a08070606060509090
8070000fefd00fffdfcfefaf5f5f8f9f8f8f8f6f8fbf2f6f6f7eaececeae5e8e9e8eaebebebedefefefe8e7e8e8eae9eae9e5e6e6e5e3e5
e4e5e5e4e5e7e2e1dfe1e6e7e9eae5e8e9ece9eef0f0e8ebedede9edf1f2e6eaf0f5dee2e7ece5e6e5e4e9e6e1e3eae8e6eaeaeeeff0eef
3f6f7eaeff1f4ebeff1f1e9eceef0e7eaeceee6ebedf1ecf0f1f3ebeef1f2edeeedeef2efeef1efededeff4f5f4f1fafbfaf8fcfaf8f8fc
fcf8f8fbfbfaf9fafafbfdfdfdfdfffefdfbfb02000000070703010405050505070a09080a09080e0d0d0b0a0907070809080b020507080
10104060504020300ff0003040405080b08060305070400fcfefefc0305030200000102fdfcfcfd06060505090806050405060508070404
080807030908080a110f100e171514121214141416171715151512121310100c1715151318191613140f1012231f1a1a25211d1c2928242
21f1b18161e1d1c1a2a2c2c29211f1f1b3d3b3a39222325242424241d201e1e2022242727211e1e1f23221e1d222223252122211e272524
2224222121251e1c1d1f1b1c1d1e1d1f231317171817151514101117190a0b0d0c10100e0e14141211151412100a0b0d0f0406070903060
60706060503fafdfe00f9f9f8f9f6f9fbfcf7fafafcfcfcfbfdf7f9f8f8e7eceff1e5e6e9ebeaebedeeedeff0f2e7e8eaece8eaeff1e8eb
f0f1e5e9ecedeaeeecebe3e8eaeaebeeeceaebebe9e9ebe9e9ecebece9eaf0f0eeeef4f4f1f0edececeee3e3e5eae6eaeceeedefeae6eee
deef0f7f5fbfcf2f2f1eff0f0f2f3efefeceaebe8e7e7eeedecedefeff1f4f0edecf0ececeeeef2f5f6f9eff0f3f6eeeff4f8f4f7fbfef6
f7f7f9f8fbfbfbfcfe0000fe000101ff020505fd00020301030607040609090607070209070703070808060e0e0d0e080909070b0a07060
80601fc0300fbff0002050806090c0805fdf8f700000102fefaf5f4f8f7fc01fdfbfdfe00fefafbfcfc000201fefbfd0100feff01fefbfb
00fef9f90003080d0702fdfe0a05000211111213141512131211111312151716080910151213161a0f0f1117141616151c1a1a19201f1e1
8221f1c18181817171c1d1c1a26201d1c191514103a36291a26211d1c1b1a1d1d24221e1b27211d1d232626202225251e241f1c1d1e1b18
152121202026282a2825272924252929252b2c2e271c1c1e191f2c302b1c1b1e1d0e1721290f141915121212110f11110e0f100f0d0c0f0
c0a090d0e0f05080c0efffe0003fafafbfdfdfe00fffcfcfefffffffdfbf8f9fcfbf2f2f5f8ededeff0efefefeff2f2f1f1f0f5f6f5f3f1
f2f2efeeeff1ecebebebe9e8e5e6e8e6e7e6e8e8ebe9e9e9eae9ebebebede9eaebeaecedf0efedecededeeeef0f1ebeae9e9edeceeefe7e
df4f7f1f1efeefaf3f0f0eeeff2f3f3f2f0f0eaf0f1f2ebf0f4f5f1f3f4f3f6f3f2f2f2f5f6f7eeeff1f2f8f8f8faf7f6f7f9fafaf9f9fd
fafafcfdfefffefcfdff01ffff00010000030404030303020202ff0808090a09090c0f00fe020601000406050502020e0a0805080906040
4060504fbfd00ff040908060402000002ffff00fafdfe00fefcfcfef4fafcfe0100fe00fefe0003fafcfd000300fefe01070a0c02090c0d
0005090cfc0003070703050903080d11060a0f12121311101312111012100e0f14100f0f1614111019140f0d1714100d1615120c1a1b181
21715120d1a181513191a150f1716120a1c1a17150f0d0a080e0604031d1a15101c19140f1c18150d1f1f1e191a15110f161311101d1c1c
1b17181614201f1b1726231f1a2322221f1f1d1d1d27221f1c1a19191a1f1815151c1a181927252220110c0d0d131212110c0e10110d0e0
d0a090e0c070f100d090f0e0a090505030100020203fefcff00fffffefff9fbfe03fbfbfe00f9faf8f9f2f6f7f7eceff0f3f3f7fafdf4f6
f7f9f5f7f7f8f3f5f7f8f1f7f8f9e7eceef2e8ebedf2ebeaeff3eaeaebefecedeeefe9ebecefedebedefedeef2f6f2f0eff1ebe7e6e4f0e
feeeef8f8f7f7f1f4f3f3f1f3f5f6f3f4f4f6f1f1f0f1f1f0ededf2f1eff0f3f2f1f0f4f6f7f7f5f5f3f6f3f4f4f4fafafcfdfcfaf9f7fb
fbfdfdffff0000fffdfdfd02010000030203030505070604040607000002020804fffe0e08030009090705090a0a0601040504050505060
204040402010204000203030709090802020302000001010104070a000407070003090e030a0d100a121617080e11150105080913161814
10131714111314120d1014130f10121413121211131112100f1111111414130e12141410100f0e0a0f0e090411110d050e0d07020503020
20b080705050202050f0d0806060201020300feff0f06020203ffff0000fefbfa0a0806060b0b0b0a09050401120a07060d0c0703100d0a
071813100f110f0b08141311101513110f19150f0917120e0e16140f0d1818161413131313181614131b1716150b060504100c07050f0a0
60506040407020103050703030708090a0f0101040a0102010501010104fe00020604060a100305080df8fe0105f7fafe00f6fafe00feff
fffffafbfbfdf9fc0000f9f8f9fcf9fafbfef4f6f8f8f7fbf8f8f6faf9f9f4f7f9f9f1f5f7f7f1f4f2f0f1f3f4f7f8f7f6f6f4f4f1efe5e
9edf1eff1f3f5f5f6f6f9f3f4f6f6f9fcfdfcf7f8fafcf2f3f5f4eff2f3f2f0f1f1f3f1f3f4f8f8fafbfdf8fafafaf3f4f5f6fe00fffef8
f8fd00fdfe020400010404fefefe0000fcfaf90200fefc0603020005020000000000fffefeff00fdfdfeff0200ff00010000010101ff010
60807060303020305050304020100ff0604040400feff02020405070c0b0a090909070313130f0a1213120f14141211171a16150a09090b
121313141413141612100e0d13110f0e14100c09100d09080e09070612110f0a0b0808070e0904010a0a06010200fffe01ff01000100fef
cfffdfbfb01000000050302000200fffe00fdfbfc000202000305050601010000fcfe00ff0506080605030204fefefefc05040303000205
05050404000b080701070605030f0d0b090c0d0d0e07040301100d08010c090605120f0a0812100a06100c0c0e1612111005030000080a0
a0a070809070805040405050504090a0806131412101013131107090a0b07090b0a08090a0c100f0c0d10141413070c0e0f010305080102
0303000207090001020301030607fe000103fefffe00f9fafcfdf7f9f9fbf7fafbfdfbf9fbfbf8f8fbfdf1f4f7f9f7f9f9f8f7f9f8f8eef
1eff0f5f9fcfef9fd0104fdff0102f9fbfdfefbfe0000fdff0000f5f7fcfef3f6fc00f9fe0001fe00ff00fdff0003fd000203f7fafd00fb
fbfd0101010205030000050202050902020104f9fbfd01fbfbfbfe0201000300000000ff00000002060808000104050000ff00020303010
30302ff050404030204050504050504fefeff000405090b040504050808080a0c0e0e0e05070708080708080a090a0a0f0e0d0c130e0d0c
0e0d0a0515140f0b14120b070e0d0b070c0c080408070604080805010502fffb0602fffc0301fefafefdfaf7fdf7f6f4fffcf9f300fbf7f
5faf4f1f0fbfaf9f400fdf9f400fcfbf7fcfafbf9fcf9f6f3fcf7f5f60200fdfa0001fffcfdfcf9f7050300fd00fcfafaf9f8f7f6020101
fe02fffbf9fffcfbf9fffdfcfa01fefaf80500fcf90d08040000fefcfcfefcfdfb0200fdfe0502fdfb020200000f0d080410111113ff000
3050a0c0a05060506030305060704060708060707060f0c0a09100d0a070b0c0b0a0a0606060d0d0c0a0e0f0f0e0f0e0e10100d0c0d0905
030303020203080b0a0a05080706040605060202010500040508fe0003070004060700010408fbfe020600020306fcff0003f9fb0209f9f
d050af2f80107fefdfeff0301010103010000fefdff02000003050000020501000204010306080205070702050707040607080405050704
04040507090a0b06060503090c09060c0c0a0a0402020204040405010102030402000200010002000101020501020406070908010507090
1030403fd000103040507080504040600010406010100000a090909060504030b0c0d0e0e0d0c0e080e0c0a09090907070401000b0a0906
0a08070702000203090608080708090805040404030402ff02020100fffefffefdfdfefdfbfcfc00f8f8f6f6f4f3f2f1f2edededf2efede
cf5f4f3f3f2f4f4f2efedeeeef3f3f3f2f5f0f1f2f4eeeff1f4f6f5f1f8f9f8f6f9f9f8f6fafaf7f4f3f0eceaf8f6f3f0faf9f9f6f3f3f4
f5f7f3f5f7f7f7f6f4f6f2f0eef6f4f3f2f6f6f4f4f9f9faf7fefaf7f2f9f6f3eff9f8f9f9fdfaf8f7fcfaf9f8fffdfbf903fefefe110f0
d0a03020001fffdfbfe00fefefe070606060503020203020000090908080500fffd0804020007070708090606071111100f1113120f0c0d
0c09070a0c0b07090c0e080c0d0f0506090d0a0e1111080f11120b1013120b0f1415080d10140c0f12140a0e1213090d0e0f080c0f12101
314140d0c0e0f0c0a0704000002040504050404070b0d0204070b07070808060505060405070a090808080708090a0808090a0a0d0e0c09
0c0c0c050505040d0d0c0903040303050606070c0b0a0905070909040405090402030403050405030303020202020106030203080405050
8060201030100ff040201000806050006070603050201000100fffd0a0a090a030202030c0603010f0d0708080504060401010000fefeff
020101030601fffd02fefbf90500fbf806fffbf801fffdfdfcfaf9fafbf8f8f9fbf7f6f9fdfaf8f50000faf5f5f5f3f2f2f1f2f4f0f0f1f
0ededeef0f3f1f2f3f1edeeededf1f2f1f2f4f4f1f2f3f2f0eff1f2f2efeff0eff2f2f2f4f3f3f6f6f1f1f2f2ebe9e5e6f1f2eeeef6f5f0
eff3f4f2f1f8fafaf8f3f4f7f6f1f2f2eff2f2f1f0f3f5f5f5f6f7f5f1eef0f1efebe9e9eaf7f6f4f0f8f8f4eff7f6f6f5fbfdfef902040
2ff0a0a07050100fcf9fe020403fcfe00000508070603030301ff00020007070705ff00020101040708090a0a08080607070e0f0e0d0b0a
0a0a0507091009090c0f0e0e0c0c0f1010110f0e0b091112121112141313121213151412101015151415161616161413151310121314131
41212151412100f1113130304090f07080806060608080e0f12130d0c0d0d08090a0d080a100f0a0907080809090a0a0d100f0a09080d08
07080f0b0d0e10060a0d0e090c0e1004090c0c080b0e0e090a0b0d0d0e100f0d0e0d0d03050508050607090406070602020301040404040
60505040100fefeff00000200000000fefd00030201030301ffff00fcf9f9fc0a0a070606050604010000000c0d09060706040101010101
0203000005040200fefefffcfdfefcf9faf8f8f8f7f8f9fbfdfdfefdfcfdfdfdfafaf9f9fcfcfbf7f5f3f0eff3f2f0eef5f3f1eff5f7f5f
4f1efeff0f2f2f0eff6f1efefeeebebecefeeefeceeecece9eeeae9e7f0eeeceaeceaeaebf1edececf7f4f2eef1efeeede4e4e2e1f1f2f2
f0eff3f5f4edeaebedf6f5f5f5f6f4f5f2eeeff1f1eff0f2f4f1f0efecedece8e5edeeeeece7e7e5e6e8e6e7ebe9e8e9e9f1f0efeff4f2e
feffdfcf8f302020000f5f4f4f50000ff00ffffff0000fefdff00fefbfafefbf8f601fdfcfdfdfbf9f8050200fe05020000070605020d0c
0704090605070f0f0d0b100d0c090c0b0d0c1012120f080c0e0d0f0e100f1315141418191813101413121619181617171817100f0f12141
415151212111011111313151416141312181d05040304090b090813110c090e0f11110d0f10100f0e0e0f0c0f10100c0f11120e0f0f0d10
110e0c1113120f1211100e0f0f0f11131615150c0c0d0d0e0f0e0d0c1011110b08090809070a0a090a090c0704020504030303ff0003030
20101010405050300060605030302ffff0002020406080906090b0904070a0a00040502070501000300fffd0101000003020200fffffdfd
000000fffffffcfd00020200fcff0201f7f8f8f8f9fafaf9fdfefbfafbfaf9fbfcfbf8f7f9fcfbfcf6f6f6f7eeedeef0eceef1f6eef0f2f
4f6f9fbf8f4f8f7f7f0f4f7f7f3f6f7f6eef2f5f3ece9eae9e8e8eaeae7eaedeceae9eaeaeae9eae9ebeaecedeae7e7e9ecebebece1e4e6
e7eeeaeaebf3efefefefeeedeef3eeececf1edebebefeeeceaefecebeceaeaedf0e5e9eff3e9ebedeee8e8e8e8ececeeefe8e5e4e5edebe
aebeeefedecf0f1f1f3fefbf9fbf6f4f4f3fffcf8f7fdf9f8f900fffdfbf8f8f7f5f7f8faf7fdfdfcfcf8f5f5f5fcfdfdfe0101010001ff
fdfd03050807080708070a0a0a0c090909090d0e0f0e0c0a0c0e090a10120f0f111312110f0f1110100f1012120f12110f0e16141210111
20f0d141311101212131314131212110d0b0d221e1c1a060c10100607070609090a0a100d0a07100d0c080d0d0e1011100f0d100e0c0b0a
070507090706050c0806020f0d0d0a12120d0812100d090a0706060b0805030b060100050200ff080300ff0804fffd07070605020100030
0fdfcfe000001010201010202ff0000fbf9fbff00fcfdfe060401010502020406010000fefcf9f8fffffcfafbfaf7f6fffcf9f8fffdfafc
fbf8f7f7fefbfaf8fcfcfbf8fffcfcf900fcf8f7f5f5f5f7f5f3f3f4f7f6f6f6fafafbfbf6f5f6f9faf8f9f9f7f8f7f8f3f2f2f0f7f8f7f
7f3f2f4f8f6f3f4f5f4f1f0f0f3f0f2f5f3f3f6f7f1efeff1e9eaedf1eaeaeef3eceaeaebececf0f1edededeeeeeff1f3eef1f4f6eef0f2
f2e7e5e4e4eff1f1f1f2f4f5f3f0f0eeebeff2f1f1f0f2f3f3ebebedeeedf0f1f1f3f3f4f2f5f3f4f3ededf0f2e9e8eaecf1f1f3f3e9ede
fefececedf0eceef1f0f4f5f6fafefffffff7f8f7f8fafe0100fafafaf8fbfbfaf9f7fafdfef7f7fafdfeffff00f8fc0000fe0002020103
0402ff03050707060809080a080a0f1012110a0c0f0f09090b0c0f0f0f11100d0a0b120f0c090d0e0d0e1111120f0c0d0d0a0f111312121
2110e0d0d0c091012121012100d0d100f0c0c0c0d0d10140f0a0d1114151205090b0b0b0e110f0a0d101006060b100f0f11120e0f0f0e0b
0c0d0a090c0e0a0708080700ff01020702010303000000060200020403040602020304000001050002030300000000fbfbfdff040202010
1010202fdfbf9fbfffdfbfb0200fffefffefbfb01fffffefffffdfefefcfc00030101000000fffff4f1f2f5fbfe0000f8f9fafaf7f6f6f7
fefefefefafbfbfbf6f7f7f9f6f5f6f7f7f3f4f7f5f3f4f6f6f7f6f5f6f5f6f5f5f4f6f8fdfcfbfbfafbfaf9f8f9fbfbf7f8fafdf1f3f6f
8f7f5f6f8fafcfaf9f7f7f9f9f3f5f6f8f6f6f6f7f9fbfcfaf1f3f4f0f4f6f6f4f5f3f1f0edededeef1f0eeeeeff1f4f5f4f4f4f6f7f6f4
f3f4f4f0ede3e0dedeeeedecedf4f3f3f1eae8eaeaf1f0f0eef3f4f2f0ededededeeedeaeaf0f0eeecefeae8eaf2f3f1eeededeae8f1ede
9e7edecebe9f0f0eeeeeeebeceefbfbf7f4ff00fffff7f6f4f30000fffdf8f9f9faf7f7f9fefcfdff00fefffefc020200ff020200fd0303
00fe00fefefd060602000806030008060505100e0e0d0f100d0a0a0807081212110e0a0b0a06090a0c0c0e0f0e0c0f0f0e0c0a090908111
00f0c0e0e100f080a0d0f0f0f10110d1011120c0f121514181a19141a1e230a08090e080808090e0a09070e0a070513130f0a13110f0e0e
0e0e0e090809070905040305040404040605060303010204060603070a0a05060200fe050300ff050402010201010003030301020204040
100000101fffe00fdfcfafafdfdfcfbfefcfefdff00fffd00fffcf8fdfefdfc0104030300010301fe000100fafdfffd00fefcfcfbfcfbfa
fafbfcfefefdfbfbfbf9f7f7f9f9f7f9f7f5f5f7faf9f8f8fafcfcfef7fafbfaf7fafcfdf9fafcfcfbfbf9f7f7f4f4f4fbf9f8f7fcf8f5f
6f7f5f0eff8f8f5f2f9f9f6f2fafcfe00fafefdfaf5f7f7f6f7f8f9fbedeff5f8f3f4f6f9f3f8fcfff0f5f9fcf0f0f2f3f6f3f3f3f8f9fa
faf3f4f7f9edf2f6f7e3e7ebeaedeeeff1f2f3f4f5edeff0efefeef0f2f0efeff1edeff0edeaeaeaeae9e9ebedeaebeef1eae9eaf0e5e5e
6e6e9ecefede6e6e5e8edececeeefefefeef4f3f4f4fe000000f3f6f7f7fcfcfbfdfafbfd0000000000fdfdfc00fcfefffffdff0002f9f8
fafefbfcfd00fefe0000fefdfe0100010305060606060b0706060607070a090a0a0a0c0d0c0b040405070a0b0b0b0807070907070709080
809090806070a0c0a0c0e0d0e10111010101110111214171818191a19171428231c150e0d0a05080703ff0708060404050505060302010a
0805030a09060208050301030301010504020305060706030505050201010103020203ffffff00fffefefd020100ff000000ff00fefffe0
301ffff0103020202050303fafcfd00fd000304fe000407fbfdfd00f9fbfe00fbfbfbfd02010001fffcfd00fdf8f9fcfaf7f6f7fbfcff00
fafbfe00fffdfefffbfc0001f9fbfefef9fafafbfbfcfcfafafbfcfbfbfbfbfbf9f9f8f9fcfcfcfcfcfbf9f7f7f9fcfdf4f5f7f9f8f7f9f
af6f7f8f8f1f1f2f2f3f4f3f1f2f5f7f5fdfcf6f2f5f2f1eff5f6f8f7fafaf9f9f8f6f5f5fbfdfdfeffffff03fbf7f7faf3f3f3f6f4f5f6
f8faf9f7f9fbfbfaf9f7f7f7f8eae8e7e7f3f5f4f3f7f8f9faf0eff0f2f3f5f6f8f2f3f2f5eaeaedefeaedeff3eeefeff2f2f3f1f0f5f6f
3efe9ebebebededededeaededeef1f2f0f0eeececeff6f7f8fafffe0001f6f7f9f9000101010101010000fffeff01000000fdfeff010504
0301000201fd0001020401010506040607080507090e07070705090a0d0a0a0806050906040009080606060706060a0907070a090808090
8070807060606090705070c0909090e0704061010101115171716191b1a18141516161719191cfef7f5f6fbf7f4f300fefbf802fffcfb00
ffff0001fffffe0100ffff00fffefc0000fffc030301fe050503010303020102020201020102020000fefdfcfcfdfdff0000fffefdfdfcf
ffffefe000000ff010203020203040400fffffe02ff0001080808070005050501010303000305040406050502040304ff010101fafcfbf8
0203020002040402ff00fefd0300ff01fffdfcfefaf9fcfdfbfcfdfefd000202fd000103fafafdfffcfcfdfdf7fbff02fdfe0000fd00010
1fcfcff01f9fafdfff5f8fbfbf1f5f8f9f2f3f8fcf1f3f7faf0f0f2f4f5f3f4f5fbfdfdfef6f8fbfdfdfefe0004030100fcfaf9fbf6f8f9
f8fafcfcfbfafcfbfcfbfcfdfdf6f4f5f6e4e3e3e7f3f3f3f1fcfefdfbf6f7f8f6f9faf9faf8f9f8f7efefeff1f4f5f5f4f4f6f7f8eff1f
3f6ebecf0f3ebebeceef0f1f2f2f1f4f4f3f2f6f9f9f1f2f5f7fbfafcfd04050607f9f8f8fb01ff0000fffefdfefefffdff0100feff0204
040303040405fcfcff000502000106060609090a08090f0e0908040404060703010204010000fffdfbfb060401fe0404020106060606090
706060807060906070809070708070907080a0808090a100d0c0d110f0e0e15141214171513131d231b16fafcf9f8f1f1efeff6f2f1f1fa
f8f6f7fffdfbf900fdf8f6fefaf6f6f9f7f7f8f9f6f5f6faf8f6f7fdfdfcfbfdfbfafbfefcfbf9020201fffcfbfcfefcfdfeffff000101f
e000001feff0000fefefe000000000004030301ff00000003030202080909060303050401ff010402000204050808070608090600000102
f8f8fc00fefeff01fffdfcfdfd00ffff0306040400040403ff000104feff00020405060504070605fe00ff00fefdfeff00fefbfc0000000
3000102030202030302020303fbfcfe00fafcff00fdff0102fd000100f7fafcfcf8fafafafefefefefffffdfc00000001fefdfefffcfafc
faf8f8f7f9faf9f8f8fdfdfcfafcfbfafbf7f8f9f9e9ebe8e9eff0f3f4f8f9fafaf7f7f9f8fd0000fef7f9fdfef5f7f8f6f3f5f9fbf8f8f
afcf9fafcfdf6f8fafbf1f1f2f3f3f7f8faf3f4f5f4f9f7f8f7f8f9f7f6fdfdfefe08090707fe00fffd0000020500010203000204040001
020403050304080a0d0b01040709060a0d0e0a0f11120d1016130a0d110f080908080504060600fefcfbfcfcfbfafcfbfafc02fffdfb060
2fffe070402030907030509060706080707090c090709090805040c0c0a0a0c08060812100905120d07060d0a06064300cc000f0000009b
9a999796959392918f8e8d8b8a898786858382817f7e7d7b7a797776757372716f6e6d6b6a696766656362615f5e5d5b5a5957565552515
04e4d4c4a49484645444241403e3d3c3a39383635343231302e2d2c2a29282625242221201e1d1c1a19181615141211100e0d0c0a090806
0d7200000ca3100b12208ea21c83d44cd3235915656118a66dd95469d45551d72549d19265d0756d9de6791d3569d9e67999d451228871d
ec66dd7b6695cb769d5453918f65094f45496c631cc645dd7d43845911800a130
''')
# The following blob has only 1 byte difference from the above
enroll_prg=unhex('''
027800000023000000200008000020008000000100320074000000008020200400242000005020773628200100302001003c20800008213
8000c210000482107004c210000582000005c20000060200000682005006c20014970200141742001887820018084202000942001809c20
0902a0200b19b4200300b8203b04bc201400c0200200c4200100c82002003300100000000080cc200000f503d0200000a10132004400000
00080dc20e803e0206401e420d002e8200001f0200500f8200500fc200000b8203a00000804001408000008080000080800001408300008
080000140831001c081a0032000c0000000080501101004c111e0034007801e8f4f6f4f2f4f6f7f4f1eeeceef2f5f5f8f9faf9f7f7f9fc0
0040d10121111121415131112151a1e1c1a1a1a1c1b1b18140f1012141616151615120c04030202fffdfbfbfbfaf9f4efedeeeeedf1f2f1
efeeefecece9eae9e9eaedf1f4f6f6f4f3f4f6f9fbfdfdfefdfefe000000000110232e10232e10232c10232c10232c01065010250101000
007c8078c06ff0000000000004f8b006d0300280307030990098db00b90880991858e08c1810b9190910ac1b8928a0993878a890b938889
8908c88191890ac8889289099a818a890b9a88898908d08191890ad08892890802818a095a810a0288890b5a8808d98189890ad99089890
95e8289890b5e88898908e18189890ae190898909648289890b648889096e8108e981890b6e880ae99091b9096f828a8f0b6f88918908f0
818a890af090898909768289910b76b8918a08f88792910af8888a92097c81898a0b7c09018089890b01888991097f8189920b7f0908808
9920b088889920c07030307200402000000002f00040070000000290004007000000035000400800000001700000026002800fbb20f0000
000f00300000008700020067000a00018000000a0200000b19000050c360ea010910002e001c000200180023000000700070004d010000a
0008c003c32321e3c0a020244000400010000003000f4311e000000ff0000008d002007ff00000095003007ffffffff00000085ffffffff
04000085ffffffff08000085ffffffff0c000085ffffffff10000085ffffffff14000085ffffffff18000085ffffffff1c000085fffffff
f20000085ffffffff24000085ffffffff28000085ffffffff2c000085ffffffff30000085ffffffff34000085ffffffff38000085ffffff
ff3c000085ffffffff40000085ffffffff44000085ffffffff48000085ffffffff4c000085ffffffff50000085ffffffff54000085fffff
fff58000085ffffffff5c000085ffffffff60000085ffffffff64000085ffffffff68000085ffffffff6c0000856f33110b120e0f101010
0f11140d0c0c08080c0e0e0b0f101a100f11120f13160d0e0e100f09080a0d0a0d0d0c06090c10070c10030c0f140407090bfdff0003ff0
6060500020405fbff0003f9030504fb040604fa010505f6fe00fef3fcfefdf0f9fbfdf0f9fafaf400fffeeaf6f8f7effafefef4f7fcfff3
f6f9fd130e00fcf6fbfdfdedf5f6f7edf1f3f4f1f5f5f6ebf4f6f7edf6f6f5f2f6f7f6f1f4f8faf1f1f2f3f8f1f2f3f4f5f2f3eaf3f4f3f
3f4f5f6ebf1f0f1eff5f4f4e9f2f3f3e7f2f2f1f7fffefbf600fefbe7f4f6f5e8f6f5f2f2f4f3f3f5f5f4f4ebf4f4f1e8f4f6f4e2f1f4f2
e0f3f7f7e1f5fbfbe3f0f1f2ecf2f5f5e1f1f5f3e8eef2f6f6060403e1f3f5f3e7f3f5f8e1f0f1f1e1f3f7f9e5f2f6f6e8f5f7f8ecfbfcf
bf3ff0100eefe00fdf0fffffc131201faedfcfdfbe8f9faf7e0f0f3f1e0edf1f1eaf3f6f6dadfe1e1e6edeeeff2f4f5f4e8f2f4f1eaf7fb
faf2f6f9fbe7eff5f9eef6fafdf0f3f4f5f2f4f7f9f1f9fdfdf4fffffef300010205110905090a0a06ff010405fc060807fc090d0dfe070
808fe0b0c0d000a0c0c0305090a020608060e0909090e07080605050706090303030afefdff070000020a0302056f2909070f161e21100f
10120f0e0e140a0e12180e1011131111121414181a1a19191715111314130d1214170e0f0d0e0b080709110e090713100c0b0a090705070
909080506080a040404070605050503010002020000ff03000000fbf7f6f8fdfbf9f9fffffdfbfafaf9f9fcfaf5f4f7f4f3f2fdf9f3f202
02fffcfffffefefbf9f8f8fefdfdfcfcfefffef7f9f9f8f8fbfcfaf7f5f5f7f3f2f2f3f6f5f3f3faf8f7f8f1f0f0f4f3f1efecf4f4f2f0f
5f5f5f5f5f7f8f9f3f6fafbf3f2f2f3f3f1f2f3f2f1f1f2f7f3efeff8f2f0ecf5f4f3f1f0edeaebf4f3f1f0f8fbfcfcf0f0f2f4f2f4f6f7
f2f5f7f5f5f5f5f4f7f4f5f4f0f2f4f6f1f1f1f2f2efefeff4f3f1f000fcf8f6f0efefecf7f7f5f4f0f0f2f2f8f6f5f4f8f9f6f5f6f3f2f
1f8f9f8f700fffffcfbfaf8f8f8f9f8f8f9f9faf8f9fafbfbf6f8f8f9f1f4f8f8eff1f3f6f7f7f8f9e4e7eaebeef0f2f3f3f5f7f8f0f2f8
faf5f4f8fafafafcfbfbfdfdfbfdfe0001f9fbfcfcfdfdfffefefffffefefffefd04050402010000000505050407050706040301010b090
605090704010c0a07050e0e0c0b0c0e100f07090908080b0c0a090e111106050506050607070204070804030302060402020503ff032222
201e191b1a19191b19181d2120201618191a141516171a1a1b1c16191b1b13100f0e151312100d0b0c0e0908080a07070b0e0b0c0d0f040
4060908090b0d0b0c0d0a070704030507060503030404ff00010300030506fbfdfffffbfdfefefbfbfdfef7f7f7f7f4f5f7f7f4f8fcfcf3
f9fe02f9fafcfdfefefffef8f9faf9f9fafbfdfbfbfaf9f6f8f8f9f5f4f4f7f6f7f5f7f3f6f5f4f5f7f6f6fafaf7f5f6f6f3f3eceff2f4e
eeff1f2f6f6f6f5f8f7f5f5fafaf7f5f3f5f4f4f2f3f5f5f2f1f0f1eff2f4f5edf0f3f7eeeeeff1eef2f4f3f1f2f3f3faf9f8f8f2f1eef0
f4f1eeecf3f1f0eff0edebecf2efeeeef7f4f1f0f4f4f3f1eff0f0f0eeebedeef7f8f9f8ececefeef4f3f3f3f0ecebeff1eeeceff5f3f2f
1f2f2f2f0f5f3f2f1faf7f8f7f8f9f7f6f5f2f1eff7f4f2f0fcfbf9f7fafaf7f6f6f3f1f0f8f4f2f1f9f9f7f5ebebeae9f6f7f6f3f7f7f6
f8faf6f3f3fdfbfaf6fcf8f7f5fbf9f7f60201fffdfcfbf9f7fcfcfbf9fcfaf8f6fbfaf8f80101fffd01020200040404040604030300010
102030203040103030204050203070505040e0a09070808060509090806111112100809090a08090a0d090c0e100505090d05070a0f0709
10131d1b1c1c1b1c1e1e1616181b1c17141318151111161514141b1717161a1a1b1b11151818111115151011110f0e0e0e0e100e0a09111
213110f1110100e0d0d0e0a0a0a0904080a09030406060505060704050507050303020100fefd00000000fefe0000f6f6f8fcf5f5f9fdfc
fbf9f80302fffd00020201fffffffdf8fafafafbfcfdfef7f5f3f4f7f6f3f3f7f7f8f8f6f5f5f4f3f4f4f2f6f7f6f4f7f8f7f3f2f5f6f5f
5f6f6f7f0f3f3f3f3f4f4f5f5f6f5f7f1efeef0f1f1f2f1f2f1f1f2f1f0eeeef5f3f1f2f7f7f5f3f1f0eeeff2f1f1eff3f1f1f1f7f7f7f8
f1f5f7f8eef2f5f5efededefeff0f1f3f1f4f4f4f1f2f2f1f1f1f2f3f1f1f1f2eeedecebfafafaf5efeff0edf2f0eff0f0f0efeff1f2f7f
9efeeeff1ece9eceef3f3f5f5f6f4f4f5f4f4f4f7f1f5f8fbefefeff1f5f5f6f8f6fafafcf0f1f2f1f0f1f0eff6f9fafae8e7e9e9f2f2f3
f5f8fbfcfaf4f5f2f3f7f5f5f4f5f3f4f3f4f3f0effafaf9f8f7fafaf8f9fafaf8f6f7f7f9f9fafbf9fbfafcfeff0000000508090803040
40503050505050608080000000402030405060708090504040205030203070708090f0e0d0d0a0707090e0f0e0d100e0908100f0e0c1314
1413191617182223251f1d1a17121c1a150f12121110100f0d0b13100f0e151211101714101017161412151211100d0f0d090e0d0703090
a09070c09090a0e0a08050d0a090806040304070504040300000002fffcfe0605010001010101fdfeff0000fdfdfd00fefdfffbf9f5f6fc
f9f6f4f7f7f7f7fbfafcfdfefcfcfdfbf9fafef9fbf9f8fefefdfdf7fbfcfbf3f5f6f7f7f7f8f9f5f7f8f8f1f0f1f4f3f3f1f2f2f0f2f3f
2efeceff5f4f3f4f3f5f5f7f5f6f8fcf8f7f7f9f1f4f6f8efedeff5f1eff1f4edeff1f5f3f6fcfef3f5f6f9f4f4f7f9f1f0f0f5f2f1f1f4
f7f9fafff7f7f9fbf8f9faf9f2f7fafbf4f6f7f9f5f4f6faf0f0f3f7f1f0f0f4f0eeedeeebedeef0f5f4f8fbeaebeef4f1f4f6f9f1f1f4f
7f9f7f8faf6f8fbfdf1f1f5f8f2f0f1f5f6f8fbfef9fafbfdfafafafef4f7fb00f8fbfd00fafbfcfef3f6fcfef0f6f9fffbfbfe00ebecee
f0f5f4f6fafbfd0102f5fafefff3f6fd00f5f8fcfbf1f5f9fbf9fbfefff7f8fafaf9fcfefefbfbfcfcfafbfcfbfffefeffff000101070a0
c08070b0c08060708060a09090708060506080809090a0b0c0706090e0b04070808090808070d0d0e0c070705050c0b0a06070907030a07
06050e090300150afff81a16130e0f0b080508060809100d0d0c090a0d0b090606070e0a0807100d0c090f0d0c0a0f0d0b0a0607070800f
dfe000602fffd090704030604040206030303040403020100fdfd0000fffc00fefcfcfffefdfe00fffcfa00fcf9f8fcf9f7f800fffdfcf8
f9fcfef5f5f9fcf7f7fafcff000101fefdfcfefdfdfefef9fbfefffbf9fafcfaf9fafcf7f8fafbfafbfcfcf8f9fbfdf8faf9f9f4f4f6f8f
7fbfefff3f7fcfef7fafdfffafbfe00ff000103fbfc0002fbff0001faff0001f9fc0001f7faff0000010104fbfe0002fd000002fafdff00
f7f9fd0003050506fdfdff01f7f7f9fcfbfdfefefcff01fffe000003fd000101f9fd0000f3f9fd01f2f7fbffff040509f7f9f9fafafe000
1f9fafbfbf9fafafdfefdfcfdfafcfdfcf8fcfe01000204050104070800010103040302000406030200ff000200000000020200fe030200
00f3f4f4f3fbfbf9f900fbf9fafffefcfb030200fdfafafafafbfaf7f5fffefefaf9f8f8f6fdfefefbfd00fffffbfbfaf900ffff00fffef
dfc02fefdfc030000ff030200ff030102040705040308090806050405040603fffe06040101040201010e0a0602050401000402010300ff
010302000002fefe0204f6f7fcff080400fb040200fe0502ffff090400fe0a070301070401000602fefd07030100070604010601fefb040
1fffe00fffefefcfcfafa010201010201fefc02fffaf60200fefbfdfdfcfaf9f7f6f7fcfcfbfb000100fffbfcfdfff8f9fcfdf9fdfefffe
fe0000ff000001ff000000fdfbfafc020101020002020202020101fefcfbfdfefdfdfffbf9f6f9faf8f9fafcfdff01fefeff00fcff0003f
bffff0100010104ffff01040105060304030202030404070100000401010204000102070305070c01020306040606070406070603050609
03060a0c04090d0e080c090c0307080a00040d1000040b0f00040c11050b141803070e130508101104090f12000104060c12181bfd02070
90304090dfd00070f02060a0cfe000407fcfe04080304070806080b0e090a0d100506080b0103060902050b1204081014ffff0308feff02
0500020505f2f0eef1f8f8fcfffefefbfafbfaf9f8fbfbfcfbf9f9f7f6f4f6f9f8fafbfdfdf7f7fafcf7f6f7f9fdfbfbfcf8f9fafbfefdf
dfdfbf9fafbfbfbf9fafdfaf8f7fdfbf9f90201fffc020100fe05060806030302ffff0100fe020300fd010101fd000000fd00fefefc0200
fefc01fbf6f401fefbfa0500fbf600fcf6f1f5f4f4f6fbfaf8f700fdf9f5fdfbfbf900fef9f4fcf8f4f3fbfaf8f700fbf9f7fdf9f5f2faf
8f6f2fbf9f9f6fcf8f2eef6f5f4f3fef9f6f4f7f4f0eff6f6f6f4fafbfcfbf9f8f8f8f9fafafafcfcfaf9fdfcfcfafefefdfdfdfbfbfcfc
fcfcfe01030404000101020100fefdff0103020507060404080909040a0e0d01040304fefeff02fcff0001fcfe010101010102000202040
70a0a070204060603070a0d0607080b010204090407080d080e0f11070d0e0c090e0e0e0a0f100f0f101213080d0f120c12131106080b10
0d1215190f1114150d0e10120c1114150d11121412111112121317181414141617141316161817181512121213141314080a0d0f1b1c202
30b0a0e1513171a1d131418190f1014190c0f171b0d1015150a0d12150f12191c121211130e1012130f11131415121213161514140d0f0e
0d060809090504080df4f6fa0000000508ff030909fd010504fdff0000f6f8f9fbf8f9fcfdffff0000fdfcfcfcfcfdff00fcfdfdf9fcfbf
bfcff000100faf9f9f9fafbf9f8f8f7f4f3f8f7f4f2f8f7f7f8fbf9f6f601fdfafafbf8f5f6f9f7f6f7f9f5f4f4f9f4f3f4faf9f8f7faf7
f3effaf5f0ecf5f2ede9f8f5eeeaf3eeeae8e7e0dadafafefffaf7f8f9f8f5f4f5f4f9f8f7f5f3f2f2f0f4f4f3f0f8f8f5f2f5f4f2f1f1e
fece8f1f0efeef1edebeceeece9e8f1edecebf2f1f2f4eef0f2f4f5f5f7f7faf8f8f7f7f6f6f7f9f7f8f7f8f8f6f7f9f8f9fcfbfaf9fafb
f9f8f8fefaf7f801fefbfe00fdfbfafcf9fafcfffafbff020000ff080704020905070904050707050709090506080a000105090307090b0
6090a090505070c07090f120d0e14170c0b0d0f0a0d0b101014181b11151c1d0c0d1418101216170c0f131715181c1e14171b1c13171c1e
1314181b1a1a1918181818191516171917171a1b151717151415131117171a1e16181719191d1f221718191914181717161b1e1d1218181
925282a2c1c1d1a172023201c1c1c1b171d20211e2021211d16171819171615171c1c1b1c1313111212100f1111100f11110e0a0d14120f
0b0c0a0a0607030203100e0d0b040301ff070606060908080803030405feff0003f9fcfc00fcf9f8fa0101fffefbfcf8f9fdf9f5f6f6f4f
3f4fcfcfbfbfefafbfdf9f7f6f7f8f8f7f5f1f1efedf0eeedecf6f4f3f3f4f3edecf7f2edeaf6f5f1eef8f9f7f4f5f4f2eff3f2efeef5f3
f0ededeceae8ecececece9eae9e8e9e8e7e8eaebeceddbdee3e9f5efece9f4efeeeef3eff1f2f3f1f0f0efefefefedececebf0efeeeeede
9e9eae4e4e5e8eceae7e7ecedeeeeeae9e9eaebece9e7f5f3f4f5f4f2f0f0f5f2f0eef8f7f7f6f7f5f6f7f7f7f6f5f8f7f6f6fefefffcfa
f9f8f6f8fafbfcfd0000fe00fffaf7fafafbfaff00000300fefbfbfffefdfe010201000a0802fe05030100070503020a0702010a0807070
a090807070401ff0e0d07041311100f16141315101113151315110e1e1b17111e1d1d1d1a1718181a191714191a1c1c1e21211f1e1c1916
1f1917181e18131217191a1b1d1c1d201b1d1e1c1d1c1c1914171b2012151a1e1f2123241e252a2e262d32321a1b1d1e1716181c1f23272
6191c201e2f383c3f1c2228281a1d2021171c21251d2228291c202121191b1b1b1c1f1e1d1c1c1919171d202013181b1c111515150f1215
160c1014170405080904060a0e0b0d1111000000fe07060706090a0908050506050506060803060503fd020300fbfe0005f8f9f9fbf4f5f
3f2f5f6f5f6fcfbf9f8fdfdfaf7f6f7f8f8f2f1f2f2e9e9e7e6edeeeeecf3f6f6f5edf1f1eeeaebeeedeceae9e7ece6e4e5eae8e6e6ecea
e8e5ecebe7e6e8e6e6e8e9e9e8eae8e9e7e6e8e9e9eaeff0f0efeceae7e5e8e6e6e6eeedebe8f4f2eeebf1efedecefefefefeaeaeae9ece
be9e9ecefefebe9e9e7e6e8e8e6e5efeeebe9ebecebeae8e9eae9f5f4f4f3f0f1f3f2f0f2f5f7f4f6f9fcf6f5f7fcf4f2f6faf7f7f9fbff
fbf9f8f6f6f9fefbfcfbfefafafc00f5f8fe03fbfc000102fffcfcfaf9f9fbfffefdfefffefefcfcfdfefc0001010105040402000000010
705030306040303feff02050607090a0c09080915151210171614100b0d1212101112111b1919191718151213141412191716141d191716
161616141d211e1b191d1e1a191b1b1b222524221a1b1c1e1a1c201f1e1b1a191c1a1a1a232123242f2c2a27302f2c2c212526252124242
427282b2f1d1e22273e3d404626272b2e20201d1e2724211e28282726222324261e201f1e1b1e1f23191e1f1f1f1d1c1e1b1d1d1c131212
1619171a1a1c1e211e0f1113101215161414181716feff000106090c0e08090a100606090b080a0d1002020206fefd020306070809fbfdf
dfff2f3fafdf9fafcfdf9faf9f7f5f6f7f7f5f2f0f0f2edeae8e6e4e3e2edebedebf4f3f1eee9e8e9e9edeceeebe7e7e8e7e5e4e3e4e7e5
e4e5e6e6e5e3e7e8e9e7ebe9e8e4ebebe9e7e5e4e5e5ebece9e6edebe9e6e0dedddee5e3e4e6e5e5e8ebecebeaebeceae9eaebeaeaeceae
cedeceaedeeeceaebeeede6e9e9e9e6e8e8e7e8e7e8eae6e5e6e9e8e7e9ebf4f4f5f4f3f2f2f2f8f9f9f8fcfcfcfc0000fffdfdfffefefd
fefefdfcff00fd00fffefefeff0000000101040505030703010203fcff0102fbfe0004ff03070bfbfe0309f9fa000500ff0001050404010
20305060404030304040405060506080b0804030601fefd0a0503040c0704020d0601000b0806061815120d110f0d08120f0c0a110f0b0a
1613110d171615121717161717161411191815151e1916131d1d19151c1a1918191a17181c1b1b1826262827282829292928292a221e1c1
d2424221e2e2d2928292826224745403d2c28231f1f2223221f212220252523222320212220222123211e1d1e1e1d1d1c1e1f1f221c1c1e
21191f22251c1d1f1e1e1c1d1c11100f10110f0f1215141310020204060c0c0c0f12121113111315151211110e080a08070606060509090
8070000fefd00fffdfcfefaf5f5f8f9f8f8f8f6f8fbf2f6f6f7eaececeae5e8e9e8eaebebebedefefefe8e7e8e8eae9eae9e5e6e6e5e3e5
e4e5e5e4e5e7e2e1dfe1e6e7e9eae5e8e9ece9eef0f0e8ebedede9edf1f2e6eaf0f5dee2e7ece5e6e5e4e9e6e1e3eae8e6eaeaeeeff0eef
3f6f7eaeff1f4ebeff1f1e9eceef0e7eaeceee6ebedf1ecf0f1f3ebeef1f2edeeedeef2efeef1efededeff4f5f4f1fafbfaf8fcfaf8f8fc
fcf8f8fbfbfaf9fafafbfdfdfdfdfffefdfbfb02000000070703010405050505070a09080a09080e0d0d0b0a0907070809080b020507080
10104060504020300ff0003040405080b08060305070400fcfefefc0305030200000102fdfcfcfd06060505090806050405060508070404
080807030908080a110f100e171514121214141416171715151512121310100c1715151318191613140f1012231f1a1a25211d1c2928242
21f1b18161e1d1c1a2a2c2c29211f1f1b3d3b3a39222325242424241d201e1e2022242727211e1e1f23221e1d222223252122211e272524
2224222121251e1c1d1f1b1c1d1e1d1f231317171817151514101117190a0b0d0c10100e0e14141211151412100a0b0d0f0406070903060
60706060503fafdfe00f9f9f8f9f6f9fbfcf7fafafcfcfcfbfdf7f9f8f8e7eceff1e5e6e9ebeaebedeeedeff0f2e7e8eaece8eaeff1e8eb
f0f1e5e9ecedeaeeecebe3e8eaeaebeeeceaebebe9e9ebe9e9ecebece9eaf0f0eeeef4f4f1f0edececeee3e3e5eae6eaeceeedefeae6eee
deef0f7f5fbfcf2f2f1eff0f0f2f3efefeceaebe8e7e7eeedecedefeff1f4f0edecf0ececeeeef2f5f6f9eff0f3f6eeeff4f8f4f7fbfef6
f7f7f9f8fbfbfbfcfe0000fe000101ff020505fd00020301030607040609090607070209070703070808060e0e0d0e080909070b0a07060
80601fc0300fbff0002050806090c0805fdf8f700000102fefaf5f4f8f7fc01fdfbfdfe00fefafbfcfc000201fefbfd0100feff01fefbfb
00fef9f90003080d0702fdfe0a05000211111213141512131211111312151716080910151213161a0f0f1117141616151c1a1a19201f1e1
8221f1c18181817171c1d1c1a26201d1c191514103a36291a26211d1c1b1a1d1d24221e1b27211d1d232626202225251e241f1c1d1e1b18
152121202026282a2825272924252929252b2c2e271c1c1e191f2c302b1c1b1e1d0e1721290f141915121212110f11110e0f100f0d0c0f0
c0a090d0e0f05080c0efffe0003fafafbfdfdfe00fffcfcfefffffffdfbf8f9fcfbf2f2f5f8ededeff0efefefeff2f2f1f1f0f5f6f5f3f1
f2f2efeeeff1ecebebebe9e8e5e6e8e6e7e6e8e8ebe9e9e9eae9ebebebede9eaebeaecedf0efedecededeeeef0f1ebeae9e9edeceeefe7e
df4f7f1f1efeefaf3f0f0eeeff2f3f3f2f0f0eaf0f1f2ebf0f4f5f1f3f4f3f6f3f2f2f2f5f6f7eeeff1f2f8f8f8faf7f6f7f9fafaf9f9fd
fafafcfdfefffefcfdff01ffff00010000030404030303020202ff0808090a09090c0f00fe020601000406050502020e0a0805080906040
4060504fbfd00ff040908060402000002ffff00fafdfe00fefcfcfef4fafcfe0100fe00fefe0003fafcfd000300fefe01070a0c02090c0d
0005090cfc0003070703050903080d11060a0f12121311101312111012100e0f14100f0f1614111019140f0d1714100d1615120c1a1b181
21715120d1a181513191a150f1716120a1c1a17150f0d0a080e0604031d1a15101c19140f1c18150d1f1f1e191a15110f161311101d1c1c
1b17181614201f1b1726231f1a2322221f1f1d1d1d27221f1c1a19191a1f1815151c1a181927252220110c0d0d131212110c0e10110d0e0
d0a090e0c070f100d090f0e0a090505030100020203fefcff00fffffefff9fbfe03fbfbfe00f9faf8f9f2f6f7f7eceff0f3f3f7fafdf4f6
f7f9f5f7f7f8f3f5f7f8f1f7f8f9e7eceef2e8ebedf2ebeaeff3eaeaebefecedeeefe9ebecefedebedefedeef2f6f2f0eff1ebe7e6e4f0e
feeeef8f8f7f7f1f4f3f3f1f3f5f6f3f4f4f6f1f1f0f1f1f0ededf2f1eff0f3f2f1f0f4f6f7f7f5f5f3f6f3f4f4f4fafafcfdfcfaf9f7fb
fbfdfdffff0000fffdfdfd02010000030203030505070604040607000002020804fffe0e08030009090705090a0a0601040504050505060
204040402010204000203030709090802020302000001010104070a000407070003090e030a0d100a121617080e11150105080913161814
10131714111314120d1014130f10121413121211131112100f1111111414130e12141410100f0e0a0f0e090411110d050e0d07020503020
20b080705050202050f0d0806060201020300feff0f06020203ffff0000fefbfa0a0806060b0b0b0a09050401120a07060d0c0703100d0a
071813100f110f0b08141311101513110f19150f0917120e0e16140f0d1818161413131313181614131b1716150b060504100c07050f0a0
60506040407020103050703030708090a0f0101040a0102010501010104fe00020604060a100305080df8fe0105f7fafe00f6fafe00feff
fffffafbfbfdf9fc0000f9f8f9fcf9fafbfef4f6f8f8f7fbf8f8f6faf9f9f4f7f9f9f1f5f7f7f1f4f2f0f1f3f4f7f8f7f6f6f4f4f1efe5e
9edf1eff1f3f5f5f6f6f9f3f4f6f6f9fcfdfcf7f8fafcf2f3f5f4eff2f3f2f0f1f1f3f1f3f4f8f8fafbfdf8fafafaf3f4f5f6fe00fffef8
f8fd00fdfe020400010404fefefe0000fcfaf90200fefc0603020005020000000000fffefeff00fdfdfeff0200ff00010000010101ff010
60807060303020305050304020100ff0604040400feff02020405070c0b0a090909070313130f0a1213120f14141211171a16150a09090b
121313141413141612100e0d13110f0e14100c09100d09080e09070612110f0a0b0808070e0904010a0a06010200fffe01ff01000100fef
cfffdfbfb01000000050302000200fffe00fdfbfc000202000305050601010000fcfe00ff0506080605030204fefefefc05040303000205
05050404000b080701070605030f0d0b090c0d0d0e07040301100d08010c090605120f0a0812100a06100c0c0e1612111005030000080a0
a0a070809070805040405050504090a0806131412101013131107090a0b07090b0a08090a0c100f0c0d10141413070c0e0f010305080102
0303000207090001020301030607fe000103fefffe00f9fafcfdf7f9f9fbf7fafbfdfbf9fbfbf8f8fbfdf1f4f7f9f7f9f9f8f7f9f8f8eef
1eff0f5f9fcfef9fd0104fdff0102f9fbfdfefbfe0000fdff0000f5f7fcfef3f6fc00f9fe0001fe00ff00fdff0003fd000203f7fafd00fb
fbfd0101010205030000050202050902020104f9fbfd01fbfbfbfe0201000300000000ff00000002060808000104050000ff00020303010
30302ff050404030204050504050504fefeff000405090b040504050808080a0c0e0e0e05070708080708080a090a0a0f0e0d0c130e0d0c
0e0d0a0515140f0b14120b070e0d0b070c0c080408070604080805010502fffb0602fffc0301fefafefdfaf7fdf7f6f4fffcf9f300fbf7f
5faf4f1f0fbfaf9f400fdf9f400fcfbf7fcfafbf9fcf9f6f3fcf7f5f60200fdfa0001fffcfdfcf9f7050300fd00fcfafaf9f8f7f6020101
fe02fffbf9fffcfbf9fffdfcfa01fefaf80500fcf90d08040000fefcfcfefcfdfb0200fdfe0502fdfb020200000f0d080410111113ff000
3050a0c0a05060506030305060704060708060707060f0c0a09100d0a070b0c0b0a0a0606060d0d0c0a0e0f0f0e0f0e0e10100d0c0d0905
030303020203080b0a0a05080706040605060202010500040508fe0003070004060700010408fbfe020600020306fcff0003f9fb0209f9f
d050af2f80107fefdfeff0301010103010000fefdff02000003050000020501000204010306080205070702050707040607080405050704
04040507090a0b06060503090c09060c0c0a0a0402020204040405010102030402000200010002000101020501020406070908010507090
1030403fd000103040507080504040600010406010100000a090909060504030b0c0d0e0e0d0c0e080e0c0a09090907070401000b0a0906
0a08070702000203090608080708090805040404030402ff02020100fffefffefdfdfefdfbfcfc00f8f8f6f6f4f3f2f1f2edededf2efede
cf5f4f3f3f2f4f4f2efedeeeef3f3f3f2f5f0f1f2f4eeeff1f4f6f5f1f8f9f8f6f9f9f8f6fafaf7f4f3f0eceaf8f6f3f0faf9f9f6f3f3f4
f5f7f3f5f7f7f7f6f4f6f2f0eef6f4f3f2f6f6f4f4f9f9faf7fefaf7f2f9f6f3eff9f8f9f9fdfaf8f7fcfaf9f8fffdfbf903fefefe110f0
d0a03020001fffdfbfe00fefefe070606060503020203020000090908080500fffd0804020007070708090606071111100f1113120f0c0d
0c09070a0c0b07090c0e080c0d0f0506090d0a0e1111080f11120b1013120b0f1415080d10140c0f12140a0e1213090d0e0f080c0f12101
314140d0c0e0f0c0a0704000002040504050404070b0d0204070b07070808060505060405070a090808080708090a0808090a0a0d0e0c09
0c0c0c050505040d0d0c0903040303050606070c0b0a0905070909040405090402030403050405030303020202020106030203080405050
8060201030100ff040201000806050006070603050201000100fffd0a0a090a030202030c0603010f0d0708080504060401010000fefeff
020101030601fffd02fefbf90500fbf806fffbf801fffdfdfcfaf9fafbf8f8f9fbf7f6f9fdfaf8f50000faf5f5f5f3f2f2f1f2f4f0f0f1f
0ededeef0f3f1f2f3f1edeeededf1f2f1f2f4f4f1f2f3f2f0eff1f2f2efeff0eff2f2f2f4f3f3f6f6f1f1f2f2ebe9e5e6f1f2eeeef6f5f0
eff3f4f2f1f8fafaf8f3f4f7f6f1f2f2eff2f2f1f0f3f5f5f5f6f7f5f1eef0f1efebe9e9eaf7f6f4f0f8f8f4eff7f6f6f5fbfdfef902040
2ff0a0a07050100fcf9fe020403fcfe00000508070603030301ff00020007070705ff00020101040708090a0a08080607070e0f0e0d0b0a
0a0a0507091009090c0f0e0e0c0c0f1010110f0e0b091112121112141313121213151412101015151415161616161413151310121314131
41212151412100f1113130304090f07080806060608080e0f12130d0c0d0d08090a0d080a100f0a0907080809090a0a0d100f0a09080d08
07080f0b0d0e10060a0d0e090c0e1004090c0c080b0e0e090a0b0d0d0e100f0d0e0d0d03050508050607090406070602020301040404040
60505040100fefeff00000200000000fefd00030201030301ffff00fcf9f9fc0a0a070606050604010000000c0d09060706040101010101
0203000005040200fefefffcfdfefcf9faf8f8f8f7f8f9fbfdfdfefdfcfdfdfdfafaf9f9fcfcfbf7f5f3f0eff3f2f0eef5f3f1eff5f7f5f
4f1efeff0f2f2f0eff6f1efefeeebebecefeeefeceeecece9eeeae9e7f0eeeceaeceaeaebf1edececf7f4f2eef1efeeede4e4e2e1f1f2f2
f0eff3f5f4edeaebedf6f5f5f5f6f4f5f2eeeff1f1eff0f2f4f1f0efecedece8e5edeeeeece7e7e5e6e8e6e7ebe9e8e9e9f1f0efeff4f2e
feffdfcf8f302020000f5f4f4f50000ff00ffffff0000fefdff00fefbfafefbf8f601fdfcfdfdfbf9f8050200fe05020000070605020d0c
0704090605070f0f0d0b100d0c090c0b0d0c1012120f080c0e0d0f0e100f1315141418191813101413121619181617171817100f0f12141
415151212111011111313151416141312181d05040304090b090813110c090e0f11110d0f10100f0e0e0f0c0f10100c0f11120e0f0f0d10
110e0c1113120f1211100e0f0f0f11131615150c0c0d0d0e0f0e0d0c1011110b08090809070a0a090a090c0704020504030303ff0003030
20101010405050300060605030302ffff0002020406080906090b0904070a0a00040502070501000300fffd0101000003020200fffffdfd
000000fffffffcfd00020200fcff0201f7f8f8f8f9fafaf9fdfefbfafbfaf9fbfcfbf8f7f9fcfbfcf6f6f6f7eeedeef0eceef1f6eef0f2f
4f6f9fbf8f4f8f7f7f0f4f7f7f3f6f7f6eef2f5f3ece9eae9e8e8eaeae7eaedeceae9eaeaeae9eae9ebeaecedeae7e7e9ecebebece1e4e6
e7eeeaeaebf3efefefefeeedeef3eeececf1edebebefeeeceaefecebeceaeaedf0e5e9eff3e9ebedeee8e8e8e8ececeeefe8e5e4e5edebe
aebeeefedecf0f1f1f3fefbf9fbf6f4f4f3fffcf8f7fdf9f8f900fffdfbf8f8f7f5f7f8faf7fdfdfcfcf8f5f5f5fcfdfdfe0101010001ff
fdfd03050807080708070a0a0a0c090909090d0e0f0e0c0a0c0e090a10120f0f111312110f0f1110100f1012120f12110f0e16141210111
20f0d141311101212131314131212110d0b0d221e1c1a060c10100607070609090a0a100d0a07100d0c080d0d0e1011100f0d100e0c0b0a
070507090706050c0806020f0d0d0a12120d0812100d090a0706060b0805030b060100050200ff080300ff0804fffd07070605020100030
0fdfcfe000001010201010202ff0000fbf9fbff00fcfdfe060401010502020406010000fefcf9f8fffffcfafbfaf7f6fffcf9f8fffdfafc
fbf8f7f7fefbfaf8fcfcfbf8fffcfcf900fcf8f7f5f5f5f7f5f3f3f4f7f6f6f6fafafbfbf6f5f6f9faf8f9f9f7f8f7f8f3f2f2f0f7f8f7f
7f3f2f4f8f6f3f4f5f4f1f0f0f3f0f2f5f3f3f6f7f1efeff1e9eaedf1eaeaeef3eceaeaebececf0f1edededeeeeeff1f3eef1f4f6eef0f2
f2e7e5e4e4eff1f1f1f2f4f5f3f0f0eeebeff2f1f1f0f2f3f3ebebedeeedf0f1f1f3f3f4f2f5f3f4f3ededf0f2e9e8eaecf1f1f3f3e9ede
fefececedf0eceef1f0f4f5f6fafefffffff7f8f7f8fafe0100fafafaf8fbfbfaf9f7fafdfef7f7fafdfeffff00f8fc0000fe0002020103
0402ff03050707060809080a080a0f1012110a0c0f0f09090b0c0f0f0f11100d0a0b120f0c090d0e0d0e1111120f0c0d0d0a0f111312121
2110e0d0d0c091012121012100d0d100f0c0c0c0d0d10140f0a0d1114151205090b0b0b0e110f0a0d101006060b100f0f11120e0f0f0e0b
0c0d0a090c0e0a0708080700ff01020702010303000000060200020403040602020304000001050002030300000000fbfbfdff040202010
1010202fdfbf9fbfffdfbfb0200fffefffefbfb01fffffefffffdfefefcfc00030101000000fffff4f1f2f5fbfe0000f8f9fafaf7f6f6f7
fefefefefafbfbfbf6f7f7f9f6f5f6f7f7f3f4f7f5f3f4f6f6f7f6f5f6f5f6f5f5f4f6f8fdfcfbfbfafbfaf9f8f9fbfbf7f8fafdf1f3f6f
8f7f5f6f8fafcfaf9f7f7f9f9f3f5f6f8f6f6f6f7f9fbfcfaf1f3f4f0f4f6f6f4f5f3f1f0edededeef1f0eeeeeff1f4f5f4f4f4f6f7f6f4
f3f4f4f0ede3e0dedeeeedecedf4f3f3f1eae8eaeaf1f0f0eef3f4f2f0ededededeeedeaeaf0f0eeecefeae8eaf2f3f1eeededeae8f1ede
9e7edecebe9f0f0eeeeeeebeceefbfbf7f4ff00fffff7f6f4f30000fffdf8f9f9faf7f7f9fefcfdff00fefffefc020200ff020200fd0303
00fe00fefefd060602000806030008060505100e0e0d0f100d0a0a0807081212110e0a0b0a06090a0c0c0e0f0e0c0f0f0e0c0a090908111
00f0c0e0e100f080a0d0f0f0f10110d1011120c0f121514181a19141a1e230a08090e080808090e0a09070e0a070513130f0a13110f0e0e
0e0e0e090809070905040305040404040605060303010204060603070a0a05060200fe050300ff050402010201010003030301020204040
100000101fffe00fdfcfafafdfdfcfbfefcfefdff00fffd00fffcf8fdfefdfc0104030300010301fe000100fafdfffd00fefcfcfbfcfbfa
fafbfcfefefdfbfbfbf9f7f7f9f9f7f9f7f5f5f7faf9f8f8fafcfcfef7fafbfaf7fafcfdf9fafcfcfbfbf9f7f7f4f4f4fbf9f8f7fcf8f5f
6f7f5f0eff8f8f5f2f9f9f6f2fafcfe00fafefdfaf5f7f7f6f7f8f9fbedeff5f8f3f4f6f9f3f8fcfff0f5f9fcf0f0f2f3f6f3f3f3f8f9fa
faf3f4f7f9edf2f6f7e3e7ebeaedeeeff1f2f3f4f5edeff0efefeef0f2f0efeff1edeff0edeaeaeaeae9e9ebedeaebeef1eae9eaf0e5e5e
6e6e9ecefede6e6e5e8edececeeefefefeef4f3f4f4fe000000f3f6f7f7fcfcfbfdfafbfd0000000000fdfdfc00fcfefffffdff0002f9f8
fafefbfcfd00fefe0000fefdfe0100010305060606060b0706060607070a090a0a0a0c0d0c0b040405070a0b0b0b0807070907070709080
809090806070a0c0a0c0e0d0e10111010101110111214171818191a19171428231c150e0d0a05080703ff0708060404050505060302010a
0805030a09060208050301030301010504020305060706030505050201010103020203ffffff00fffefefd020100ff000000ff00fefffe0
301ffff0103020202050303fafcfd00fd000304fe000407fbfdfd00f9fbfe00fbfbfbfd02010001fffcfd00fdf8f9fcfaf7f6f7fbfcff00
fafbfe00fffdfefffbfc0001f9fbfefef9fafafbfbfcfcfafafbfcfbfbfbfbfbf9f9f8f9fcfcfcfcfcfbf9f7f7f9fcfdf4f5f7f9f8f7f9f
af6f7f8f8f1f1f2f2f3f4f3f1f2f5f7f5fdfcf6f2f5f2f1eff5f6f8f7fafaf9f9f8f6f5f5fbfdfdfeffffff03fbf7f7faf3f3f3f6f4f5f6
f8faf9f7f9fbfbfaf9f7f7f7f8eae8e7e7f3f5f4f3f7f8f9faf0eff0f2f3f5f6f8f2f3f2f5eaeaedefeaedeff3eeefeff2f2f3f1f0f5f6f
3efe9ebebebededededeaededeef1f2f0f0eeececeff6f7f8fafffe0001f6f7f9f9000101010101010000fffeff01000000fdfeff010504
0301000201fd0001020401010506040607080507090e07070705090a0d0a0a0806050906040009080606060706060a0907070a090808090
8070807060606090705070c0909090e0704061010101115171716191b1a18141516161719191cfef7f5f6fbf7f4f300fefbf802fffcfb00
ffff0001fffffe0100ffff00fffefc0000fffc030301fe050503010303020102020201020102020000fefdfcfcfdfdff0000fffefdfdfcf
ffffefe000000ff010203020203040400fffffe02ff0001080808070005050501010303000305040406050502040304ff010101fafcfbf8
0203020002040402ff00fefd0300ff01fffdfcfefaf9fcfdfbfcfdfefd000202fd000103fafafdfffcfcfdfdf7fbff02fdfe0000fd00010
1fcfcff01f9fafdfff5f8fbfbf1f5f8f9f2f3f8fcf1f3f7faf0f0f2f4f5f3f4f5fbfdfdfef6f8fbfdfdfefe0004030100fcfaf9fbf6f8f9
f8fafcfcfbfafcfbfcfbfcfdfdf6f4f5f6e4e3e3e7f3f3f3f1fcfefdfbf6f7f8f6f9faf9faf8f9f8f7efefeff1f4f5f5f4f4f6f7f8eff1f
3f6ebecf0f3ebebeceef0f1f2f2f1f4f4f3f2f6f9f9f1f2f5f7fbfafcfd04050607f9f8f8fb01ff0000fffefdfefefffdff0100feff0204
040303040405fcfcff000502000106060609090a08090f0e0908040404060703010204010000fffdfbfb060401fe0404020106060606090
706060807060906070809070708070907080a0808090a100d0c0d110f0e0e15141214171513131d231b16fafcf9f8f1f1efeff6f2f1f1fa
f8f6f7fffdfbf900fdf8f6fefaf6f6f9f7f7f8f9f6f5f6faf8f6f7fdfdfcfbfdfbfafbfefcfbf9020201fffcfbfcfefcfdfeffff000101f
e000001feff0000fefefe000000000004030301ff00000003030202080909060303050401ff010402000204050808070608090600000102
f8f8fc00fefeff01fffdfcfdfd00ffff0306040400040403ff000104feff00020405060504070605fe00ff00fefdfeff00fefbfc0000000
3000102030202030302020303fbfcfe00fafcff00fdff0102fd000100f7fafcfcf8fafafafefefefefffffdfc00000001fefdfefffcfafc
faf8f8f7f9faf9f8f8fdfdfcfafcfbfafbf7f8f9f9e9ebe8e9eff0f3f4f8f9fafaf7f7f9f8fd0000fef7f9fdfef5f7f8f6f3f5f9fbf8f8f
afcf9fafcfdf6f8fafbf1f1f2f3f3f7f8faf3f4f5f4f9f7f8f7f8f9f7f6fdfdfefe08090707fe00fffd0000020500010203000204040001
020403050304080a0d0b01040709060a0d0e0a0f11120d1016130a0d110f080908080504060600fefcfbfcfcfbfafcfbfafc02fffdfb060
2fffe070402030907030509060706080707090c090709090805040c0c0a0a0c08060812100905120d07060d0a06064300cc000f0000009b
9a999796959392918f8e8d8b8a898786858382817f7e7d7b7a797776757372716f6e6d6b6a696766656362615f5e5d5b5a5957565552515
04e4d4c4a49484645444241403e3d3c3a39383635343231302e2d2c2a29282625242221201e1d1c1a19181615141211100e0d0c0a090806
0d7200000ca3100b12208ea21c83d44cd3235915656118a66dd95469d45551d72549d19265d0756d9de6791d3569d9e67999d451228871d
ec66dd7b6695cb769d5453918f65094f45496c631cc645dd7d43845911800a130
''')
calibrate_prg=unhex('''
027800a10223000000200008000020008000000100320074000000008020200400242000005020773628200100302001003c20800008213
8000c210000482107004c210000582000005c20000060200000682005006c20014970200141742001887820018084202000942001809c20
0902a0200b19b4200300b8203b04bc201400c0200200c4200100c82002003300100000000080cc200000f503d0200000a10132004400000
00080dc20e803e0206401e420d002e8200001f0200500f8200500fc200000b8203a00000804001408000008080000080800001408300008
080000140831001c081a0032000c0000000080501101004c111e0034007801e8f5f6f4f2f3f6f7f5f1eeeceef1f6f6f8f9f9f9f8f8f9fd0
0040c0f12111112141614121115191f1c1a19191c1c1b17130f1012141616161614120b06030201fefcfcfbfbfaf9f4f0ededeeedf0f3f2
efeeefededeaeae9e9eaedf1f4f6f6f3f3f4f6f9fcfdfdfefdfefe000000000110232e10232e10232c10232c10232c01065010250101000
007c8078c06ff0000000000004f80006d0300280307030990098db00b90880991858e08c1810b9190910ac1b8928a0993878a890b938889
8908c88191890ac8889289099a818a890b9a88898908d08191890ad08892890802818a095a810a0288890b5a8808d98189890ad99089890
95e8289890b5e88898908e18189890ae190898909648289890b648889096e8108e981890b6e880ae99091b9096f828a8f0b6f88918908f0
818a890af090898909768289910b76b8918a08f88792910af8888a92097c81898a0b7c09018089890b01888991097f8189920b7f0908808
9920b088889920c07030307200402000000002f000400700000002900040070000000350004008000000017000000440004000100000030
00f4311e000000ff0000008d002007ff00000095003007ffffffff00000085ffffffff04000085ffffffff08000085ffffffff0c000085f
fffffff10000085ffffffff14000085ffffffff18000085ffffffff1c000085ffffffff20000085ffffffff24000085ffffffff28000085
ffffffff2c000085ffffffff30000085ffffffff34000085ffffffff38000085ffffffff3c000085ffffffff40000085ffffffff4400008
5ffffffff48000085ffffffff4c000085ffffffff50000085ffffffff54000085ffffffff58000085ffffffff5c000085ffffffff600000
85ffffffff64000085ffffffff68000085ffffffff6c0000856f34110a130e0f0f10101111140c0d0c08080c0f0f0c0e101a0f1011120e1
3180c0e0e110e0a090a0e0a0b0e0b06090c10080c11040c0f140507090cfc000003ff05060601020505fbff0003f8020505fb050604fa00
0505f6fe00fef2fcfefeeff9fafdeff9f9fbf500fefeeaf6f7f7f0fbfefef4f8fcfff3f6f9fd130e00fbf6fbfdfeedf4f6f8edf1f3f4f2f
5f6f7ebf4f6f7edf5f6f5f1f5f6f6f2f5f7faf1f1f2f3f8f2f2f2f4f5f2f3ebf3f4f3f4f4f5f6ebf0f0f1f0f5f4f4e8f2f3f3e7f2f1f1f7
fffefaf500fefbe7f4f6f6e8f6f5f2f1f4f2f3f5f5f4f4ebf5f5f2e8f5f6f4e2f2f3f2dff3f7f7e1f5fcfbe4f1f2f2ecf2f4f5e1f2f4f4e
8eef1f5f5060404e1f4f5f4e8f3f6f8e2eff1f0e2f4f7f9e6f2f6f7e7f5f7f8ecfbfcfbf2ff0000ee0000fef0fffffc121102fbedfbfcfa
e8f9fbf7e1eef3f1e0edf1f1e9f2f6f7d9dfe1e2e6edeff0f2f4f5f4e7f3f4f1eaf7faf9f1f5f8fbe7f0f5f9eef7fbfdeff3f5f5f3f5f8f
9f0fafdfdf3fffffef301010305110a05090b0906ff000305fc060807fc0a0d0dfd070709fe0b0c0d010b0d0d0305090b030608070e080a
080d06070705050707080303030cfdfeff080000020a0402056f2a09090e161e21110f0f130f0d0d130b0f13180f1011131012121414171
a1a1a1a1815131213130e1215160f0e0e0f0a080809100e090713100c0a09090606060a08080507070b0404050706060505030100020200
00ff040000fffcf8f5f7fefbfafafffffdfcfaf9f9f9fcfaf6f3f6f4f3f2fcf9f3f20201fffb00fffefefbf9f8f8fffdfdfcfbfdfffff8f
9f9f8f8fafcfaf7f5f7f8f3f2f2f3f6f4f3f3faf8f7f8f1eff1f4f4f2efedf4f3f1eff5f5f5f5f6f7f8faf3f6fafbf3f3f2f4f2f2f2f3f1
f1f1f2f8f3eff0f8f3f0ecf5f4f2f1f0eeeaecf4f3f1f1f9fafbfbefeff1f4f2f3f6f7f2f5f7f6f6f5f5f4f8f4f6f5f1f2f5f6f2f1f1f3f
0efefeff4f3f1f000fbf7f5f0f0eeedf7f8f5f3eff0f2f3f9f7f6f4f9f8f7f6f7f4f2f1f8f9f8f700fffffcfbfaf8f8f8f9f9f8f9faf9f8
fafafafbf7f8f9faf2f5f8f9eff0f3f6f8f7f8fae3e8eaebeff0f2f4f3f5f8f9f0f3f9fbf6f5f8fafafafdfbfbfdfcfbfdfe0102fafcfdf
cfdfefefefefffefffffffefd04050402010001010605050407060706040301010a0907050a0704010c0a07050e0e0d0b0d0e0f10080909
08080a0c0a090e10120604050605060706010507080404040306040202050300032221201e181b1a19181a19171d21211f1619191a14161
617191a1a1c16191c1b12110f0e151312110e0d0c0e0808080a07080c0e0c0c0d0f0404060909080b0d0c0c0d0b08060403060706050203
03050000010300020506fbfcfefffbfdfefffbfbfdfff8f8f8f8f4f5f6f5f5f7fcfdf3f9fe02f9fafcfffefefefff8fafaf9f9fafbfdfcf
bfaf9f5f7f8f9f5f4f4f6f6f6f5f7f3f5f5f5f5f7f5f6fafaf8f5f6f6f2f3eceff1f4efeff1f2f5f6f6f5f8f6f6f5f9faf7f5f4f5f4f4f2
f3f5f4f2f1eff1f0f3f5f7edf0f3f7efeff0f3eef2f3f4f1f2f3f4faf9f8f8f2f1efeff5f1eeecf4f2f1eff0ecebedf2efefeef7f4f2f0f
4f4f3f0eef0f0f1eeeceeeef7f8f9f8edebeeeef4f3f3f3f0ebeceff2eeedeff5f3f3f0f2f2f1f0f5f3f2f1f9f7f8f7f8f9f8f6f5f3f1f0
f6f5f2f0fcfbfaf7fafaf7f6f6f4f1f0f7f5f2f1fbf8f7f7ebebebe9f7f6f6f4f7f7f6f8fbf6f3f4fdfbfaf7fbf9f8f6fafaf8f60201fff
dfcfcf9f7fcfdfbf9fafaf7f6fbfaf9f8020100fd01020200040403040504030400000002030303030302020205040202070504050e0c09
0709070605090907061213121008090a0b08090a0e090b0d100506090e05060a0f080811141e1b1c1d1b1b1e1d1616171a1d18141318151
111161514141b1716161a1a1b1c11131818111214151112100f0d0d0d0e100d0c09111212100e1011100d0c0d0f0a090a0904070a090404
06050506060704040507050403020000fdfd00000000fffe0000f6f6f8fcf5f5f9fdfbfaf9f8030200fd00020201fffffffef9faf9f9fbf
cfcfff7f5f3f4f7f6f3f3f7f7f8f8f6f5f5f4f3f4f4f3f6f7f6f4f7f8f7f4f1f5f6f6f5f6f6f6f1f2f3f2f4f4f4f5f5f6f5f7f0efeef0f0
f2f2f2f2f1f1f2f0f0eeedf4f3f1f1f7f7f6f4f2efeff0f2eff1f0f3f1f2f2f7f7f7f8f1f5f7f7eef1f6f6eeedeef0f0f0f0f3f1f4f5f3f
0f1f2f1f1f1f1f2f1f1f2f3efededebfafafaf5eff0f0edf2f0f0f1f0efeeeff1f2f6f9f0eeeff1edeaedeef3f4f5f4f5f4f5f6f4f4f5f7
f2f5f9fbefefeff0f6f6f7f7f7fafbfbf1f1f3f1f1f1f0f0f7f8f9fae9e8e9e9f2f2f3f5f9fbfcfbf5f5f2f3f7f5f6f4f4f3f4f3f4f3efe
ffaf9f9f8f8f9fbf8faf9faf8f6f7f8faf9fafbfafbfbfdfefe00000005080a080404030503050606050708090000000403030306060808
090505040304040303070708080f0f0d0d0a0907080f100f0d100d0908110f0f0e13151514191617192124251f1c1916131c1b150f11121
010100f0d0b13110e0c151312111914101019151311131311110e0e0c090f0d0803080909070d09090a0e0b08060d0a0807060503040605
0404030100ff03fffdfe0705010002020000fdffffff00fefdfd00fefcfffbf9f5f6fbf9f6f4f6f7f7f7fbfafbfefefdfbfefaf9fbfef9f
afaf8fffefefdf8fbfcfcf2f5f6f7f7f8f8f9f5f6f8f8f0f0f1f5f3f2f2f2f1f0f1f3f1eeeceff5f3f3f3f3f4f5f7f5f6f7fcf8f7f8f8f1
f3f6f8efeeeff4f1f0f1f4eeeef2f5f4f6fbfef3f4f7f9f4f5f7faf1f0f0f4f3f1f1f3f7f8fafff7f7f8fbf8f9f9f9f4f7f9fbf5f6f8faf
4f4f7fbf1f1f4f7f2f0f0f3f1eeedeeebedeff1f5f4f8fbeaebeff3f1f4f6faf1f1f4f7faf8f7faf6f8fcfef0f2f5f9f3f1f2f5f6f8fafe
f8fbfbfdfbfbfcfef4f7fb00f9fcfd00fbfbfcfff3f7fcfef1f5f9fffbfcff00ecedeef0f5f4f6fafcfe0102f6fafdfff4f6fd00f5f8fbf
bf0f6f9fcfafbfefff8f8f9faf9fcfefefbfbfcfdfafbfcfcfffeff0000000101080a0c08070b0e09050709070909090708070705080808
080c0c0d08060a0e0d05070808090909070d0d0e0d070604050d0a0906080907030a0707050f0803001409fef91a17130e100b090608070
708110d0e0b090a0c0c090606080e0b08080f0e0c0a100d0c0a0e0c0b0a0606070800fefe000602fffd0906050305040402050303030403
03020100fdfd0000fffdfffdfcfd00fefdfe00fffdfafffdf9f7fcfaf7f70000fdfcf8f9fbfef5f5f8fcf7f8fafcff000102fefdfcfffef
cfdfff9fafefffcf9f9fcfaf9fafcf7f8fafbfafbfbfcf8f9fbfcf8f9f9faf5f4f5f8f7fafdfff2f7fcfdf7fafdfefafcfe01fe010203fb
fc0001fbfe0000faff0001f9fcff01f7fafe00ff000204fbfd0102fd000002fafeff01f7f9fd0001050405fdfefe02f8f8f9fdfcfdfdfef
e000100fe000003fe000101f9fe0000f3f9fe01f3f8fcfe00030609f7faf9fafbfe0101f9fafbfbf9f9f9fdfefefcfdfafcfefef8fcff01
000304060104080900010103040302000406040201000002000001ff020201ff03020100f2f4f5f3fcfbf8f800fbf8fafffefdfb030300f
dfafafafbfbf9f7f6fffffefbfbf9f8f7fdfdfdfbfe00fffffbfbfafa0000000000fffefd03fffdfd030000ff0402000003020204060604
0309090806060505040702fefe07050102050302000e0a0602060502000301020200ff010301000003ffff0204f7f8fbff080400fb04020
0fd060100ff090400fe0b070401070401000602fefd07030100080604020702fdfc0501fffe00fffefffcfcfbfa010102010201fefc02fe
faf60100fcfafdfefcfbfaf8f6f8fdfdfcfb000100fffbfcfdfff8fbfcfef9fcfefffeff000100000001ff000001fdfbfafb01010002010
2020202020001fefcfcfefefefdfefcf9f6f9faf9f9f9fcfeff01feffff00fcff0004fbfeff0100010002ffff0004010405020404020203
0405070200000301010203000104070205070c010103050405060705060607020506090306090c040a0c0e080d0a0b0307070a00040c0f0
0040b0e01030c12050a131802070e1305070f1104090f11000204060c13181bfd0207090304090efdff080f0205090bfd000307fdfe0408
0304070706070b0d090a0d110506080c0202060a01040c1304080e14fe000207feff030500020505f1efeff1f8f8fcfffdfdfbfafbfafbf
9fcfcfcfcf9f8f6f7f5f6f9f8fafafdfef8f7fafbf7f5f7fafcfcfbfbf9f9fcfcfffcfcfdfbf9fbfbfcfbf9fafefaf9f7fdfcf9f90301ff
fc0201fffd05060706030401feff0000fe020301fd020201fc000100fd00fffdfc0301fefd01fcf6f402fffbfa0600fbf600fbf6f2f6f3f
4f6fbfaf8f7fffef9f6fdfbfbf900fdf8f4fdf9f4f2fcfaf8f700fcf9f6fef9f6f4f9f8f6f3fcfaf9f6fcf8f2eef7f5f4f3fef9f6f3f9f5
f1eef6f6f5f4fafcfcfbf9f8f7f8f9fafafafcfcfbfafdfcfcfbfffffefcfefbfafbfcfcfcfe02030404000001010100fefdff020302040
705030508090a030a0e0d00030405feffff02fcffff02fcff01010102020200010205060909070305060605080a0c060808090002040803
06080d090d0f10080c0d0c080e0f0e0b0f100f0f111112080d0f110c11121106080a0f0d1214190f1114170c0e10120d1113160e1111141
2111112111316171314151617141316171717171512121213141213080b0e101c1c1f230c090e1413171a1d131417190f1014180a10171b
0d101314090d11161012171a121210120e1011120f11131315121213161614130d0f0f0d06080a090404080cf4f6fa0000000508fe03080
9fd010404fdff00fef5f8f9fbf8f9fcfdffff0000fdfcfcfdfbfd0000fcfdfcfafbfcfcfcff000101fbfaf9f8fbfbf9f8f9f6f5f3f8f7f5
f2f9f6f8f7fbf8f6f602fdf9fafbf7f6f6faf7f7f7f9f5f4f3f8f4f3f4fbf8f9f8faf7f3f0f9f5efebf4f2eee9f8f4efeaf3edeae8e8e0d
bdbfbfffffaf7f8faf7f5f4f5f4f9f8f8f5f2f2f3f1f4f5f3f0f9f9f5f2f4f4f3f0f1efebe8f2f1efeef2edecebedece9e8f0edecebf2f2
f1f3eef1f2f5f5f6f7f7faf8f8f8f8f6f7f5f9f8f7f8f8f8f7f8faf8f8fcfaf9fafafaf9f8f9fefbf8f801fefcfd00fdfcfbfcf9fafb00f
afbff0200ff00080604020905070904050707050709090506080a000105090307080a060809090505070b07080f130d0e14170a0b0d0f0a
0d0b101114181b11141c1e0c0d1317101216170d0e131515191b1c13161c1c13171b1e1214171a1a1b171619171819141617181617191a1
51818151416141216161a1d1518171a191c1f211618191a14171716161b1d1e1217181926292b2c1b1c18172023201d1c1b19181d21211f
2021211c16171818161515181c1b1b1c1312101212110f1012100f10110e0a0c13110e0a0b0a0a06070303020f100d0b040301ff0706070
70908090803020405fdff0003f9fbfd00fbf9f8fb0002fffdfcfbf9f9fdf9f4f6f6f4f3f4fdfdfbfbfefbfbfcf9f8f6f8f8f8f7f5f2f1f0
eeefeeeeecf7f5f3f3f4f3eeedf8f3eeeaf5f5f3eff8f9f8f3f5f4f2eff4f3f0eef6f4f0eeecebeae9ebecedebeaebeae9e9e8e8e8ebebe
beddcdee4eaf5efece9f4f0eeeef4f0f0f3f3f1f1f1eeeeefefededebebf0efeeeeedeae7e9e5e3e5e8eceae8e7eceeeeefeaeae9eaeceb
eae8f5f5f5f4f4f2f1f0f6f2f1eff8f7f7f5f7f5f7f6f7f7f6f6f8f7f6f6fefffefdfbf9f8f6fafafafcfd0000fefffffaf7fafafbfafe0
0010300fefbfafffefdff010201000b0702fe04030101080504030a0602010a0807060a090807070401ff0f0c0704141110101715131410
1113161215110d1d1b16111d1c1d1d1a1817191a18171418191c1b1f20211f1e1b1a171d1916181d181312171a1a1b1c1c1e1f1c1d1e1c1
c1c1c1a14171c1f1115191f1e2023251e252a2d262d3231191b1d1f1616181c1f242626191d201f31383c3e1b2327271a1e2121171d2226
1e23272a1c202122191b1a1b1c1e1e1e1b1b1817171e1f1f13191b1c111415150f1316160c1015170404090a04060a0e0d0f0f12000000f
e07070807090b0a08050507060606060604060703fd010201fcff0005f8faf9fcf4f5f3f3f5f6f6f7fcfbf9f9fefcfbf8f7f8f9f8f2f2f3
f2ebeae7e6eeeeefeef4f5f6f4eef0f1edebedeeeeeceaeae7ede8e4e4ebe8e7e7eceae8e7ecebe9e7e7e6e7e8eae9eaebe9e8e6e7eae9e
9ebf0f1f1efecebe8e5e9e6e6e4eeedece9f3f1efedf1f0eeeceff0efefeaebeae9edebe9e8edefeeebe9e9e7e6e8e9e7e5efefeceaebed
ebeae8e9ebe9f5f4f4f3f0f1f3f2f0f2f5f7f4f5f9fbf6f5f8fcf4f4f6faf8f7f9fbfefbfaf8f6f5f9fefbfcfcfdf9fbfd00f6f8fe04fbf
c00020200fdfbfafafafbfefdfcfefffefefcfcfcfefc0000010105040402000100000705030306040403feff02040507090a0c0a090914
15140f1817140f0b0d12120f1212111b19191818171412131315131a1715151d181715171615141c1f1d1a191e1d1a1a1b1a1b222524221
a1b1c1e1a1d1f1e1e1d19191c1a1919232123232e2d2a272f2e2e2b202424252124242427292b2e1e1d22273c3d404627282b301f1f1d1e
2724211d28282627212324251e201f1e1c1f2122181d1f1f1e1c1d1e1c1d1c1c131312161817191a1b1e211f0f101311121416141418171
6fefe0102070a0b0e09090b100706090d090a0c1002020306fefe01030508090afcfdfefff3f3f9fef9fafcfefafaf9f8f6f6f8f8f5f2f0
f0f2eeeae9e5e4e4e4edecececf4f3f2eee9e8e9e9edecedebe7e7e8e7e5e5e4e5e7e4e5e4e6e6e6e5e7e9eae7ebe9e8e4ebebe9e8e5e5e
5e6eceae8e6eeece9e7e0dfdddde4e4e5e6e6e6e8ebecebebecedebeae9ecebeaeceaededecebedeeedeaeceeece6e9e9e8e6e9e9e8e7e8
e8e9e7e5e6e9e8e8e9ebf4f4f6f4f3f2f2f2f8f9f9f8fcfdfbfc0001fffcfefffffffdfefefefcfffffc00fffefefeff000100000103050
5030703010103fdff0102fbfe0005ff04060cfbff0309fafa000500ff000205040300020405060303030204040405050406090a08040306
02fdfc090503030b0704020d0601ff0c0806061815130e11100d07110e0d0a110e0c091514110c171614111717171718171311191814141
f1915131d1c19151b191918181917171c1c1b17252526272827292929282a29211d1c1d2424211e2e2b2829292826234744403e2d282320
1f2223221e212220252523222321202120222222211e1c1f1e1c1d1d1e1d1f221d1e1e201a2023251c1d1f1e1f1d1d1e11101011110e101
214131410020204050d0d0c0f12121113111315161212110f080a0807060706060a0a09060001fefc00fefdfcfefaf6f5f8f9f9f9f8f6f7
fbf2f6f4f8ebeeece9e6e8e9e8ebebebeaeeefeeeee8e8e8e8eaeaeae9e6e7e5e6e4e4e5e5e5e4e5e7e4e2dfe1e7e7e9ebe5e7eaece9eef
0f0e8ebecede9edf2f3e6eaf2f5dde2e8ede6e6e5e4e9e7e2e4eae7e7ebebeef0f0edf3f7f8ebeff1f3eceff1f1e9eceef0e7eaeceee7eb
edf1ecf0f1f3ebeef0f3edededeef2efeff1efedeef0f5f4f3f1fafbfaf8fbfaf8f8fdfcf9f9fafbfafafafbfbfdfdfefdfefefdfbfb020
0ff00070702020405050505070a09080a09080f0c0d0b0a0a08070808090a02060809000204060404030400000003030405080a08060205
070300fcfdfefc0305030201000102fdfcfcfd060605050a0707050404050508060504080707040908080a110f100e18151412131414141
61716141515121312100f0a1715161418181613130f1012231d1b1a26211d1b292724221f1b17151e1d1b192a2b2c28211e1e1b3d3a3a3a
212225252324241d201e1e2023242727211f1f2023211d1e212123252022211e262624212421211f241f1c1c1f1a1c1d1f1d1e221317171
71816161410121618090c0d0c0f110d0e14141111141411100c0b0e10060606090306060606060403fafdfefff9f9f8f8f7f9fbfbf8f9fa
fcfcfdfbfdf7f9f9f9e8eceff1e4e7e9ebeaebedeeecf0f0f2e7e9eaede8ebeef1e8ecf0f2e6eaecedebeeecebe2e9eaebebeeece9ebeae
9e8eae9e9ebecebe8eaf0f0efeff4f4f1f0edededefe3e3e5eae5ebedeeeeefeae7eeeeeff0f7f5fafcf3f2f1f0f1f1f4f4eeeeece9ece9
e7e8eeeeedeef0eff1f5efededefececedeef2f5f7faeff1f4f6eeeef5f9f5f8fbfef6f7f7f9f8fafbfcfcfe0000fe010101ff020507fd0
0030202030507030709090507080109080704080807070e0e0e0e08090908090a0705080601fc0300fcfe0003050806090c0805fef7f800
000202fdfaf5f5f7f7fb01fdfbfdfe01fefafbfcfb000201fefafd0100fdff01fdfbfb00fef9fa0003080d0701fdfe0a040002111211131
3141213111011131216171608080f14121317190f0e1116141616161d1b19191e1f1d18221e1b18181616171b1d1c1a261f1d1b19151310
3b35291b26201d1b1b191e1f23221e1b28211d1d232724202226231e231e1b1d1d1c181521201f1f262929282427282525292a242c2c2d2
81c1c1e191f2c312a1a1c1f1d0d1621280f161916121212110f11100c10100f0e0c0f0d0a080d0f0f04070c0dfffe0003f9f9fcfcfdff00
fffdfdfefe0000fdfbf9f9fcfcf2f2f5f7ecedeff1eeeff0eff3f2f1f2f1f6f7f5f3f1f2f2efeff0f1edeaeaebe9e8e5e6e7e5e6e6e8e8e
ceae8e9eaeaecebecede9ebeaebededf1efededeeeceeeff0f1ebeceae9eeeeefefe7eef4f8f1f1efeffaf5f0f0eff0f1f3f3f2f0f1eaef
f1f1ebf0f4f6f1f4f4f4f6f5f3f2f3f6f7f7eeedf0f2f9f9f9fbf6f6f7fafbfaf9fafdfbfbfcfdfd0000fcfdff01ffff000100010204050
30303030202000807090a09090b0e00fe030601000406050502010d0a08050808060404070703fbfdffff040908060501000003fffefff9
fdfe00fefcfcfef4fafcfe02fffe00fffdff03fbfcfd000200fdfd01070a0d02090c0d0005080cfc0003060703040803080e11060a0e111
21311111412101011110e0f140f0e0e1615110f19140f0e1614100c1614120b1b1b17121815120d191916151a19150f1716110b1b1a1715
0d0d0a080e0704031d1a140f1c18140e1c18140d1f201d191a15100e171312111d1b1b1a17161615201e1b1626231f1a2422211f1e1e1d1
c27211f1b191918191e1816151c19191a2724221f100c0c0d131211120a0e10110e0e0d0c090e0c070f100d090f0d0b0906050202000302
03fefdff00fffefe00fafbfe02fbfbfe01f9faf9f9f3f5f7f7edf0f0f4f3f7fafdf5f6f6faf5f7f8f8f3f6f7f9f1f6f8f9e7eceef2e9ebe
df2ebebf0f4eae9ebefedededefebebecf0edebeef1edeef2f5f3f0f0f2eae8e6e5f1efeeedf8f8f8f7f1f4f3f4f1f4f5f6f2f3f4f6f2f1
f1f1f1efeeedf2f1f0f0f3f3f1f1f4f6f7f8f5f5f5f6f4f5f4f3fbfbfbfcfcfbfaf7fbfcfdfd00000000fffefefe0201000002020302050
608070305070700000202080400ff0d09030009080705090a0a06020404030505060502040405020303040003040207090a090302040200
0001010003070a010407070003080e04090e120b121716080e1115010608091417181410141815101315140d1014130f111314121312131
21212110f1112121414130e12151412100e0c0a0f0e0a0410110d050e0c0803060302010a080704050203060f0c0806060201010300feff
0f08020102ffff0000fefafa0b0806050b0b0b0a09050301140a07060d0b08020f0d0b0718130f0f11100a0815141110151311101a150e0
817130d0e15130f0d1a18161414121313181715121b1616150a070404100d0604100a060507040407020103040503030709090b0f000105
0a01030104000102040000010504070c100304080df9fe0206f7fafe00f6fafd00fdff00fffbfcfcfdfafd0001f9f8fafdf9fbfcfef3f6f
8f9f7faf8f7f7faf9faf4f7f8faf2f5f8f7f3f4f4f1f2f4f5f7f8f8f5f6f5f4f1f0e5eaecf1eef0f4f5f5f6f6faf4f5f7f8fafcfdfcf7f9
fafbf2f4f5f4eff2f2f1f1f2f2f3f2f3f4f8f7f9fbfdf8fafafbf4f5f4f6feff00fef8f9fd01fdff020500020303fefefe0000fcfbfa010
0fefd06030201040200ff000000fffefdfe00fefdfe000200ffff0100000002010001070808060304020305050404010100000604040300
fdfe02030405060b0a0a090908060312120f0a1313110f1414121218171614090a090b131213141413141513100e0d13110e0e14100b091
00b08080d09070612110f090c0a08060e0905020a0a07020200fffe01ff000001fffefcfefcfcfb01ff01ff050302010300fffe00fdfcfc
010202000305040501000000fcfefffe0606070705040304fffdfefd0503040301020505050404000c080502070605030e0d0c090c0d0e0
d060503020f0d08020b090705110f090812100a06100c0b0d15121210040300ff080a0b09070a08080706040405050504090b0807131313
1010131311070a0a0a07090a0908090a0b100f0d0e0f131413070c0f100002060902020403000207090001020201030506fe000104ff00f
f00f9fafcfef7f8f9fbf8fafafefbfafafaf8f8fbfef1f5f8f9f8f9faf8f7f9f8f9eff1eff0f5f9fcfefafd0203fd000103fafcfefefbfe
0001feffff00f5f8fcfff3f6fc00f9fe0100fd000000feff0103fd000204f7fafd01fcfafd0101010206030000040402040a02010203f9f
bfd01fbfafbfe0100010300000000ff00000002050708000104050000ff0001030301030401ff040404030203050604060604fefe000004
05080a040504050708080a0c0e0f0e05060707080708070c0a0a0a0f0e0d0c120e0c0d0e0d0a0614130f0b14110b080e0d0d080d0c08050
8080505070705010601fffc0501fffc0300fefafffdfaf8fdf8f6f3fffbf8f3fffbf7f5faf5f1f0fbfaf9f4fffbf9f500fdfcf7fcfbfbf9
fbf9f7f4fcf7f4f50201fdfb0001fefdfefdf8f7050300fd00fcf9faf9f8f8f7020101fe01fffcf9fefcfbf9fefcfcfa00fdfaf80400fcf
90e080400fffefdfcfefdfdfb0200fefd0502fdfc020100000f0c08050f101012000103060a0d0b05070605030305070705070808070708
060f0d0a09100e0a070c0b0b0a090707060c0d0c0a0e0e0e0f0f0d0d100f0d0c0d0905020204020203090b090a050707060605060703030
20600040508ff0004080003060600020408fdfe020600010306fcff0003f9fc0209f9fd050af2f90108fefdfeff0302010103010000fefe
0002000003050000020400000203010306080205060702050607040707080405050703040405070a0c0c060704030a0c08060c0d090a030
2020204040405000103020402000100010003ff01000205010306060609080103070901030403fdff010304060708040405060000020601
0000000a080809060404030c0d0e0e0d0d0c0e080d0c0a08090906080301000c0a08050c080706020002040807070807070908050405040
304020002020100fffefffdfcfefefdfbfcfd00f8f8f6f5f4f3f1f1f2edecedf1efedecf5f4f3f3f2f3f5f3f0ededeef3f3f2f1f5f1f1f1
f3efeef0f4f5f4f1f8f9f8f6f9f9f9f6fafaf7f4f3f0ecebf8f5f2f1faf9f8f6f3f3f4f6f8f3f4f6f6f7f5f4f6f3f0eef6f3f2f2f6f6f4f
4f9f9faf7fefaf6f1faf6f3eef9f8f9f8fcfaf8f7fbfbfaf8fffdfaf802fefdfe11100d0c04020101fffdfbfe01fffefd07060606050302
0203010000080909090401fefd0705010107070709090706071011100e1113120f0c0d0c09070b0d0d060a0c0d090a0c0e0507090e0a0e1
112090f11130c1013130c101314080d11150c1012150a0f1114090d0f10080d0f12111414150d0d0d100e0b080500000204050406040406
0b0d0204080c06070908050505060406070a0909080908090909080909090a0e0e0c090b0d0d050505040d0e0c0903040303050607070d0
b0a080607090905040609030303040305040603040302020301010603020307040505080502010302000003010100090704000607050305
020101010000fd0b09080a030201040c0603010e0c0708080504060402010000fffe00030101020601fefd02fefcfa05fffaf90500fbf80
2fffefdfcf9f9fafcf8f7f9fbf7f6f8fdf9f8f400fff9f5f6f3f3f2f2f1f1f4f0eff0f1edecedeff3f1f1f4f0edeeeeedf0f3f2f2f4f4f2
f2f3f2f0eff1f1f1f0eff1eef1f2f3f3f3f2f5f7f1f1f2f3ebe8e5e5f2f2eeeef6f4f1f0f4f5f3f2f9faf9f9f4f4f6f6f0f2f2eff1f2f2f
0f3f4f5f4f7f6f5f1eff1f1efebe9e9eaf7f6f3eff7f7f4eef7f6f6f4fafdfdf9020402ff0b0a08050100fdf9fe030304fefe0000050808
07030403020000020107070705ff01020101040708090a0908080608060e0e0f0d0c0a0a0a06070a10090a0c0f0f0f0c0c0f1011110f0f0
a0810121311131513141211141514121111151515151716161714141414101214141415121215151310101112130405090e070807060606
08070f0f10130d0d0d0d08090a0d090b0f0f0b0807090909090a0b0e0f0f08080a0e0907090e0b0c0e0f060a0e0d0a0c0d1005090c0c090
a0d0e090b0d0c0c0e100f0c0d0e0c0404050905050708050607060202030104030404060605050100ffff0000010200000000fefd000202
0102030100ff00fcf9f9fc0c0a080705060504010000010b0d0906070604020101020202020100050401fffffefefcfdfdfdfafaf9f7f8f
8f8f9fbfefdfefdfcfdfefdfbfaf9f9fdfcfaf8f5f3f1eff3f1f0eff5f4f3eff6f7f4f4f1efeff0f2f3f0f0f5f1eeefedebebecefeeefed
eeecece9edebe9e6f0efedeaebeaeaebf1eeecebf7f4f2edf1efeeede4e4e2e0f1f3f2f0f0f3f5f4edebebeef6f5f4f5f5f5f5f3edeff1f
1eeeff1f4f1f1efecedebe8e6eeefedece7e7e6e7e8e6e7ebe9e7e9e9f1f0efeff4f1efeffdfcf8f301020100f5f4f4f60000ff0000ffff
0000fefdfffffefcfafefbf9f700fdfbfdfdfaf9f8060200fe04020100070505030d0c0804080605060f0f0d0c100e0c090d0a0c0c10121
10f080c0e0d100e100f14151514191a1a1511141312151818161617181611100e11151515151313111111121214141516151212181c0504
0303090a0a0714120d090e0f11120d1011100e0e0e0f0d0f10100d0f11120f0f0f0e10100e0c121311101111100f0e0f1011121615140d0
d0c0c0e0e0e0e0d1011110a08090808070a0b0809090c070403050302030300000302020201020305050400050805010402ffff00020204
06080906080a0804070a0800030503070502010300fffe0101010004020201fffffefd0000fffffffffdfd00020200fcff0202f7f8f9f8f
9fafaf9fefefbfafbf9f9fafcfbf8f7f9fbfbfcf7f6f6f7eeedeef1edeef2f6eef0f2f4f5f8faf9f4f8f8f7f0f5f8f7f3f7f8f6eef2f4f3
edeaeae9e8e8eaeae8eaecece9e9e9eaebe9eae9ebecebedeae7e6e9edececede1e4e7e7eeebebebf3f0efeeefededeef3eeececf1edebe
bf0efeceaf1eeecebe9eaeef0e7eaeef2eaebedeee8e8e9e8ececeeefe8e6e4e5edeaebebefeeedebf0f0f1f2fdfaf9faf5f4f4f3fefcf9
f8fcf9f9fa00fffdfcf9f7f6f6f8f8f9f7fefefcfcf8f6f6f5fcfdfdfd0101010000fefdfc03050808070808080a0a0a0c09090a090d0f1
00e0b0a0d0d090a10120f10121313120f0f121110101112110f13100f0f1614121011120f0d151310101213121214131212110e0b0b211e
1d1b060d101006070707090b0b0a100d0a0811100b080f0e0f0f10100f0d100e0c0b0a070607090706050c0806020e0d0d0a12120d08121
00d0a0a0806060a0805040b06010005010000080300000805fffd080807060202010200fdfcfd000001020201020200000000fcf9fbffff
fcfdff060402010502020306020000fefcf9f70000fcfafdf9f7f7fefcfaf8fffcfbfcf9f9f7f8fefcfaf8fbfbfaf7fefcfcf9fffcf9f8f
5f5f5f7f5f3f3f4f7f5f5f5fbfafbfaf6f4f7f8faf8f9f9f8f8f9f9f3f2f1f0f8f8f8f7f2f2f4f8f5f3f5f5f3f1f0f0f3f1f2f5f3f3f6f7
f1eff0efe9eaedf1e8eaeef3eceae9ebebedeff0eeedededefeff0f2edf2f4f7eeeff0f3e7e5e5e4f0f1f2f0f2f4f4f3f0f0eeeaf0f1f1f
1eff2f3f3ecebecedeef0f1f1f3f3f4f2f5f4f4f2eeedeff3e9e9eaecf1f1f3f3e9edefeeedededefeceff1f0f5f5f6fafefffefff7f8f8
f8fafe0100fafafaf9fbfbfbf9f7fafdfdf7f8fbfdfe00ff01f9fc0001fe01020302030402ff02060706060708080a090a101012120a0d0
f0f0a080b0b0f0e0f11100d0b0b120f0c090e0e0e0e111212100d0d0c0b0f1213131213120f0d0e0c0a1112131112110e0d110f0d0c0c0d
0d10150f080e1114141106090c0c0b0e10100a0d101106070b100f1012130e0f0f0e0c0d0d0a0a0c0d0a0708080700ff010206030103030
10001060200030504050602020305ff000205000204030000ff00fbfdfeff0401030101010102fdfbf9fb00fdfbfc0200fffdfffdfcfb02
0000fe00fefdfdfefcfc00040201ff0000fffef3f0f1f5fbfe0000f7faf9f9f7f5f5f7fefefefef9fafbfbf7f7f8f9f5f5f7f7f7f4f5f7f
5f4f4f6f7f7f5f5f6f5f5f4f4f5f5f8fcfbfafafafafbf9f8f8fafcf7f8fbfdf1f3f4f9f6f6f6f7fafbfaf9f6f7f9f9f3f5f6f7f6f7f6f7
f9fbfbfbf1f3f4f0f4f6f6f3f5f4f1f1edeeededf0efeeeef0f0f4f6f4f4f4f6f7f6f4f2f4f3f1ede2e0dee0eeedecedf3f4f2f2e9e8e9e
af1f1f0edf4f4f2f0ededecedefedebeaf1f0edebefebe8eaf3f2f1efeeedeae7f0edeae8eeecebe9f0efededeeebeceefbfbf7f4ff0000
fff6f6f3f40000fefdf8f9f9fbf7f7fafffcfe0000fffffefc020300ff020200fe030200fd00fffffe060503000806030008060504110f0
f0d0f0f0e090a0807081213110e0c0c0a06080a0d0c0e0f0e0d0f0f0e0d090a090810100e0b0f0f120f090c0e0f0f0f10100c1011120d0f
13161518191914191e230a090a0e090808090f0b09070e0a060513120e0a13110f0e0e0f0e0f08090807080504030604030405050505040
4020205060603070909050602fffd060200ff050302010202010003030301010305040200000201fffefffdfcfaf9fdfcfbfbfefdfdfdff
00fefd00fefbf9fdfefdfc0104030300010201ff000000fafdfefd00fdfcfcfbfcfbfafafcfcfefefcfbfbfbf9f7f7f8f9f7f9f7f5f5f6f
bfaf8f7fafcfdfdf7fafbfaf7fafcfcf9fbfcfcfafaf9f7f7f4f3f3faf9f7f8fbf9f5f5f8f4efeef8f8f6f3f9f8f5f2fafdfe00f9fdfdf9
f6f7f6f6f6f7f9faeceff5f7f3f4f6f9f3f7fd00f0f6f9fceff1f1f3f7f4f3f4f8f8f9f9f3f4f7f9eff2f5f7e3e8ebeaededeef1f2f3f3f
5edeeefefefeff0f1eeefeff1eef0efeceaeaebeae9e9eaeceaebedf1ebe8eaf0e5e5e6e6e8ecefede5e5e6e8ecebedeeedeeefeef4f4f5
f5ff000000f5f7f7f7fdfcfbfdfbfbfe000000ff01fefcfd00fcfefffffefe0002f9f8fafdfbfdfe00fefe0000fefdff010001030506060
6060a0707060606060a080a0a0a0c0d0c0b040606060a0b0c0c0908080907070809080909080808070b0d0b0d0d0e0e0f100f1010111012
1113181919191a19171526221d150e0e0a05080603ff0808070405060505060303010a0805030b080603080503010303020105040302050
60606030505050100010203020203ffffffff00fffefc020100fe000000ff00ffffff0301ffff0103030202050402fbfbfd00fd000304ff
000507fbfcfd00fafcff00fcfcfbfd02010001fffdfd00fcf9f9fcfaf6f6f7fcfcff00fafbfe00fefefefffbfd0001f9fbfefef9fafafaf
bfcfcfafafbfbfbfcfbfafbf8f8fafafbfbfbfafcfbfaf8f8f9fcfcf3f5f7f9f7f8f9faf7f7f8f8f1f2f2f2f3f3f2f1f2f5f6f6fdfbf6f2
f5f1f0f0f5f6f8f7fbfaf9f8f7f6f5f5fcfdfdfeff000003faf7f8faf3f3f4f6f5f6f6f8faf7f7f9fafbfaf9f7f8f8f8e9e8e6e6f3f4f4f
3f6f9f9fbf0f0f1f3f3f5f6f8f1f3f3f4e9eaeeefeaecf0f3eeeef0f1f2f2f1f0f5f6f1eee9ebebebedebedede9edecedf1f2f1f1eeeced
eff6f6f7f9fffe0001f6f6f9fa00010101010201000000feff01010000feffff0005040302000201fe00010203010104060406080906070
a0f07070806090a0c0a09070606090604000808070706070506090907080b09080809080708060606060a0706070d0909090c0704070f0f
111116171916191a1a191515161717191a1cfff7f5f6fbf7f3f201fffcf80200fcfaff00ffff01ffffff01ff00000000fefc0100fffb020
302ff060503000403020102020201030101020000fffdfdfcfdfeff000000fdfcfcfd0000fefeff0000ff01020202030304040000fefe02
000001090707070005060501010303000405040405050404030304fe000102f9fbfcf902030200020404020000fefe02010000fffdfcfef
9fafbfdfbfdfefefe000203fe000203fafbfdfffbfdfefdf7fbfe01fefe0000fc000000fbfcff01f9fafbfff4f9fafbf1f5f8faf2f2f8fb
f1f3f7f9f0f0f1f4f5f4f4f5fbfcfefff6f8fbfefdfefe0004030100fcfafafbf7f8f8f8fafbfdfbfafcfbfcfcfcfdfdf6f4f4f6e5e3e3e
7f2f2f3f0fcfffefcf5f7f7f7f9f9fafaf7f9f8f7efefeff0f4f5f6f4f4f5f7f8eef0f3f6ebeceff4ebebecedf1f1f1f1f0f4f3f4f3f6f9
f9f1f2f4f7fafafbfd03040508f9f8f8fb01000000fffefdfefefefeff0100ffff0304030303040505fcfcff0005020002060607090a090
9090e0d080705040506070302030401000000fdfbfc060401fd04040202060706070a0707060808070907060809080707090a07080a0709
090a0f0e0c0d120f0e0e16141213171714131e231c15fafdfaf8f2f2f0f0f5f3f1f2faf9f7f7fffdfaf9fffdf9f7fefaf5f6f9f7f7f8f8f
6f5f6faf8f7f7fefcfbfcfdfaf9fbfefcfafa02020100fcfcfdfefdfdffffff000001fe000001feffff00feffff00000000000403020100
00000003030302080809050404040501ff010502000205060808060609080701010203f7f8fd00fffeff0100fdfcfcfd00ffff030504030
0040403fe000103feff01010505060404060504ffffff00fefdff0000fdfafb00000003010103030202030301020304fbfcfd00fbfdff00
fdfe0101fd000000f6fafcfbf8fafbfafffefefefffefdfc00000001fefcfdfffcfbfbfaf7f7f8f9faf9f9f9fcfcfcfafdfbfafbf7f8f8f
8e9eae9e8eff0f3f5f8f9fafbf6f7f9f9fd0000fef6f9fdfff4f7f8f7f4f6f9fcf7f8fafcf9fafcfcf6f8fafbf0f2f2f3f3f6f9faf4f3f4
f4f8f7f7f7f8f9f7f6fdfdfefe09080607fe00fffd0001030400010303000304050001010403050404080b0d0d0105080906090a0d0b0f1
2130d1116140a0d100f080808090404050600fefcfbfdfcf9f9fcfcfbfc0200fdfb0601fefe070402030a0704050a050608090708090c09
09090a0905040d0d0a0a0d080707120f0a06120e08060d0a07074300cc000f0000009b9a999796959392918f8e8d8b8a898786858382817
f7e7d7b7a797776757372716f6e6d6b6a696766656362615f5e5d5b5a595756555251504e4d4c4a49484645444241403e3d3c3a39383635
343231302e2d2c2a29282625242221201e1d1c1a19181615141211100e0d0c0a0908060d7200000ca3100b12208ea21c83d44cd32359156
56118a66dd95469d45551d72549d19265d0756d9de6791d3569d9e67999d451228871dec66dd7b6695cb769d5453918f65094f45496c631
cc645dd7d43845911800a130
''')

View file

@ -1,99 +0,0 @@
from hashlib import sha256
from binascii import hexlify, unhexlify
from os.path import isfile
from threading import Thread
from struct import unpack, pack
from .tls import tls
from .usb import usb
from time import ctime
from .sensor import write_hw_reg32, read_hw_reg32, identify_sensor
from .flash import erase_flash, read_flash, get_fw_info, write_flash_all
from .util import assert_status
from .blobs import calibrate_prg
class Line():
def __init__(self, blob):
# what's with the rest of fields?
self.u0, self.u1, self.line, self.frame, self.u2, self.u3, self.u4, self.u5 = unpack('<BBBBBBBB', blob[:8])
self.data = blob[8:]
def serialize(self):
return pack('<BBBBBBBB', self.u0, self.u1, self.line, self.frame, self.u2, self.u3, self.u4, self.u5) + self.data
def __repr__(self):
return 'Line(line=%d, frame=%d)' % (self.line, self.frame)
def persist_calib_data(calib_data):
start=read_flash(6, 0, 0x44)
if start != b'\xff' * 0x44:
if calib_data[:0x44] == start:
print('Calibration data already matches the data on the flash.')
return
else:
print('Calibration flash already written. Erasing.')
erase_flash(6)
write_flash_all(6, 0, calib_data)
def make_calib_data(calib_data):
lines=[calib_data[i:i+0x70+8] for i in range(0, len(calib_data), 0x70+8)] # TODO work out where "bytes per line" constant is comming from
lines=[Line(i) for i in lines]
frame2=[i.serialize() for i in lines if i.frame == 2] # why 2?
frame2=b''.join(frame2[::2])
calib_data=pack('<H', len(frame2)) + frame2 + pack('<H', 0) # what's that 00000 in the end?
calib_data=pack('<H', len(calib_data)) + sha256(calib_data).digest() + b'\0'*0x20 + calib_data
calib_data=unhexlify('0250') + calib_data
return calib_data
def calibrate(calib_data_path='calib-data.bin'):
# no idea what this is:
write_hw_reg32(0x8000205c, 7)
if read_hw_reg32(0x80002080) not in [2, 3]:
raise Exception('Unexpected register value')
dev=identify_sensor()
print('Sensor: %s' % dev.name)
# ^ TODO -- what is the real reason to detect HW at this stage? -- likely it is required to construct calibrate_prg
fwi=get_fw_info(2)
if fwi == None:
raise Exception('No firmware detected')
print('FWExt version %d.%d (%s), %d modules' % (fwi.major, fwi.minor, ctime(fwi.buildtime), len(fwi.modules)))
if isfile(calib_data_path):
with open(calib_data_path, 'rb') as f:
calib_data=f.read()
print('Calibration data loaded from the file.')
else:
# TODO Properly construct calibrate_prg.
# >>> 6f 000e 000000000000
# <<< 0000 880d 0000 07000000
# 0800 0000 9400 0e00 0300 0080 07000000 7e7f807f808080808080808080808080808080808080818081808180818080808080818081808080818081808180818081808180818081808180808081808180808081807f80808180808081808180818080808180818081808180818081808080818081808180818081808180818081808180818081808180818081808080808080808080807f807f807f807f7f7e7e
# a400 0000 0800 0e00 0200 0000 00000000 0d007100
# b400 0000 0800 0e00 0800 0080 db000000 00000000
# c400 0000 0400 0e00 0500 0080 1c6f0400
# d000 0000 9400 0e00 0700 0080 07000000 2b23203c2d182e1e30182e1c321d341d341e321c301e1e241e201f201d1c321a301e1c211e21341f1e202024201f1e20201f212221221d221e23341e1d1e1d20341f1d193b341c1d1e35201e201c20221f341c1e1e1c221f201d21201e1c1f34242221201f20221f201e241e241d2020221e2420231d221e211e1f1e1e341c321e3220301d2d302f2d2c2b23223a211c
# 6c01 0000 1400 0e00 0f00 0080 05550007 7701002805720000080100020811e107
# 8801 0000 0c00 0e00 1200 0080 07000000 7002 7800 7002 7800
#
# Empty reply:
# >>> 6f 000a 000000000000
# <<< 0000 880d 0000 00000000
rsp=tls.cmd(calibrate_prg)
assert_status(rsp)
# ^ TODO check what the rest of the rsp means
calib_data=usb.read_82()
print('len=%d' % len(calib_data))
with open(calib_data_path, 'wb') as f:
f.write(calib_data)
calib_data=make_calib_data(calib_data)
persist_calib_data(calib_data)
# no need to reboot

View file

@ -4,16 +4,18 @@ from enum import Enum
from .tls import tls
from .usb import usb
from .db import db, subtype_to_string
from .flash import write_enable, flush_changes
from .flash import write_enable, flush_changes, read_flash, write_flash_all
from time import sleep
from struct import pack, unpack
from .table_types import SensorTypeInfo
from binascii import hexlify, unhexlify
from .util import assert_status, unhex
from .hw_tables import dev_info_lookup
from .blobs import identify_prg, enroll_prg, reset_blob
from .blobs import reset_blob
from . import timeslot as prg
calib_data_path='calib-data.bin'
def glow_start_scan():
cmd=unhexlify('3920bf0200ffff0000019900200000000099990000000000000000000000000020000000000000000000000000ffff000000990020000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000')
assert_status(tls.app(cmd))
@ -22,9 +24,6 @@ def glow_end_enroll():
cmd=unhexlify('39f4010000f401000001ff002000000000ffff0000000000000000000000000020000000000000000000000000f401000000ff0020000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000')
assert_status(tls.app(cmd))
def start_scan(cmd):
assert_status(tls.app(cmd))
def cancel_capture():
usb.queue.put(b'')
#sleep(0.2)
@ -57,245 +56,6 @@ def wait_till_finished():
def stop_prg():
return tls.app(unhexlify('5100200000'))
def capture(b):
usb.purge_int_queue()
start_scan(b)
b=usb.wait_int()
if b[0] != 0:
raise Exception('Unexpected interrupt type %s' % hexlify(b).decode())
try:
wait_for_finger()
#wait_till_finished()
while True:
b=usb.wait_int()
if b[0] != 3:
raise Exception('Unexpected interrupt type %s' % hexlify(b).decode())
if b[2] & 4:
break
finally:
res=stop_prg()
assert_status(res)
res = res[2:]
l, res = res[:4], res[4:]
l, = unpack('<L', l)
if l != len(res):
raise Exception('Response size does not match %d != %d', l, len(res))
x, y, w1, w2, error = unpack('<HHHHL', res)
return error
def enrollment_update_start(key=0):
rsp=tls.app(pack('<BLL', 0x68, key, 0))
assert_status(rsp)
new_key, = unpack('<L', rsp[2:])
usb.wait_int()
return new_key
def enrollment_update_end():
assert_status(tls.app(pack('<BL', 0x69, 0)))
def enrollment_update(prev):
write_enable()
rsp=tls.app(b'\x6b' + prev)
assert_status(rsp)
flush_changes()
return rsp[2:]
def append_new_image(key=0, prev=b''):
enrollment_update(prev)
usb.wait_int()
res = enrollment_update(prev)
l, res = res[:2], res[2:]
l, = unpack('<H', l)
if l != len(res):
raise Exception('Response size does not match %d != %d', l, len(res))
magic_len = 0x38 # hardcoded in the DLL
template = header = tid = None
while len(res) > 0:
tag, l = unpack('<HH', res[:4])
if tag == 0:
template = res[:magic_len+l]
elif tag == 1:
header = res[magic_len:magic_len+l]
elif tag == 3:
tid = res[magic_len:magic_len+l]
else:
print('Ignoring unknown tag %x' % tag)
res=res[magic_len+l:]
return (header, template, tid)
def make_finger_data(subtype, template, tid):
template = pack('<HH', 1, len(template)) + template
tid = pack('<HH', 2, len(tid)) + tid
tinfo = template + tid
tinfo = pack('<HHHH', subtype, 3, len(tinfo), 0x20) + tinfo
tinfo += b'\0' * 0x20
return tinfo
def enroll(identity, subtype):
key=0
template=b''
print('Waiting for a finger...')
while True:
glow_start_scan()
try:
err = capture(enroll_prg)
if err != 0:
print('Error %08x, try again' % err)
continue
except Exception as e:
print('Capture failed (%s), try again' % repr(e))
sleep(1)
continue
key = enrollment_update_start(key)
header, template, tid = append_new_image(key, template)
enrollment_update_end()
print(hexlify(header))
if tid:
break
# TODO check for duplicates
tinfo = make_finger_data(subtype, template, tid)
usr=db.lookup_user(identity)
if usr == None:
usr = db.new_user(identity)
else:
usr = usr.dbid
recid = db.new_finger(usr, tinfo)
glow_end_enroll()
print('All done')
return recid
def parse_dict(x):
rc={}
while len(x) > 0:
(t, l), x = unpack('<HH', x[:4]), x[4:]
rc[t], x = x[:l], x[l:]
return rc
def identify():
glow_start_scan()
try:
err = capture(identify_prg)
if err != 0:
raise Exception('Capture failed: %08x' % err)
# which finger?
stg_id=0 # match against any storage
usr_id=0 # match against any user
cmd=pack('<BBBHHHHH', 0x5e, 2, 0xff, stg_id, usr_id, 1, 0,0)
rsp=tls.app(cmd)
assert_status(rsp)
b = usb.wait_int()
if b[0] != 3:
raise Exception('Identification failed: %s' % hexlify(b).decode())
rsp = tls.app(unhexlify('6000000000'))
assert_status(rsp)
rsp = rsp[2:]
finally:
# finish
assert_status(tls.app(unhexlify('6200000000')))
(l,), rsp = unpack('<H', rsp[:2]), rsp[2:]
if l != len(rsp):
raise Exception('Response size does not match')
rsp=parse_dict(rsp)
#for k in rsp:
# print('%04x: %s (%d)' % (k, hexlify(rsp[k]).decode(), len(rsp[k])))
#on 0097
#0001: 09000000 (4)
#0003: f500 (2)
#0004: 8dee792532d3432d41c872fd4d6d590fbc855ad449cf2753cd919eb9c94675c6 (32)
#0005: 0000000000000000000000000000000000000000000000000000000000000000 (32)
#0008: 0a00 (2) <-- finger record db id
#0002: 010b0000 (4)
#0006: 00000000000000000000000000000000000000000000000000000000000000000000000000000000 (40)
#on 009a (no finger record db id)
#0000 8a00
# 0100 0400 05000000
# 0300 0200 f500
# 0400 2000 b147dd1eda8da322fb7a2a51d0eab6fe94bef46c05204fbefb1fd16360903791
# 0500 2000 0000000000000000000000000000000000000000000000000000000000000000
# 0200 0400 650a0000
# 0600 2800 00000000000000000000000000000000000000000000000000000000000000000000000000000000
usrid, subtype, hsh = rsp[1], rsp[3], rsp[4]
usrid, = unpack('<L', usrid)
subtype, = unpack('<H', subtype)
usr = db.get_user(usrid)
fingerids = [f['dbid'] for f in usr.fingers if f['subtype'] == subtype]
if len(fingerids) != 1:
raise Exception('Unexpected matching finger count')
finger_record = db.get_record_children(fingerids[0])
# Device won't let you add more than one data blob
if len(finger_record.children) > 1:
raise Exception('Expected only one child record for finger')
print('Recognised finger %02x (%s) from user %s' % (subtype, subtype_to_string(subtype), repr(usr.identity)))
print('Template hash: %s' % hexlify(hsh).decode())
if len(finger_record.children) > 0:
if finger_record.children[0]['type'] != 8:
raise Exception('Expected data blob as a finger child')
blob_id = finger_record.children[0]['dbid']
blob = db.get_record_value(blob_id).value
tag, sz = unpack('<HH', blob[:4])
val = blob[4:4+sz]
print('Data blob associated with the finger: %04x: %s' % (tag, hexlify(val).decode()))
return rsp
def read_hw_reg32(addr):
rsp=tls.cmd(pack('<BLB', 7, addr, 4))
@ -317,8 +77,8 @@ def factory_reset():
reboot()
def identify_sensor():
#rsp=tls.cmd(b'\x75')
rsp=unhexlify('0000000000005a009001');
rsp=tls.cmd(b'\x75')
#rsp=unhexlify('0000000000005a009001');
assert_status(rsp)
rsp=rsp[2:]
@ -339,12 +99,10 @@ def identify_sensor():
# 6c010000 1400 0e00 0f00 0080 05550007 7701002805720000080100020811e107
# 88010000 0c00 0e00 1200 0080 07000000 7002 7800 7002 7800
def get_factory_bits(tag):
#rsp=tls.cmd(pack('<B H LL', 0x6f, tag, 0, 0))
rsp=tls.cmd(pack('<B H HL', 0x6f, tag, 0, 0))
# 6f 000e 00000000 response from the 009a logs:
rsp=unhex('''
0000a80200000c0000000800000074000e0003000080070000007e7e7c767d7a737a807c7c7975847f85858184888786888a8a8a8c8d8b85878c8689878689898484837d8b8b8289898d8f8c90908f86858c8b8d90908b848f8694928a8e908d8e8d898d8d8c8e8f8d8c878986808a8a818686848187888c8e7e7e85888989857f8077777b7872767c7e8400000074000e0003000080070000007e7e7c767d7a737a807c7c7975847f85858184888786888a8a8a8c8d8b85878c8689878689898484837d8b8b8289898d8f8c90908f86858c8b8d90908b848f8694928a8e908d8e8d898d8d8c8e8f8d8c878986808a8a818686848187888c8e7e7e85888989857f8077777b7872767c7e0001000034020e000c00008007000000010205060504040708060403050709080705040401fffdfbfdfdfcfaf8f7f7f7f7f5f5f5f5f4f6f6f5f4f6fbff00fffcfaf9f7f7f5f6f6f9fcfdfefefefdfcfdfdfdfd000202020303020103050609090c0e100f0a0909080501ffff00020303020201ff0000fffefcfcfbfbfdff0000f0f4f5f5f7f9fbfaf5f3f3f6f5f6f7f7f6f6f7f8f9f9fbfafbfdfe00000101020306080a0a09080706040303030403050204050704fcf7f6f9fbfdff000306090a0909090b0d0e0e0e0d0c0d0e0c07070a0c090603020100fefbfafafcfcfdfcfcfbfaf9fafbff000201ff0003060403f1f3f2f3f4f3f0eff0f1f2f2f2f0f0f1f1eff0f2f6f8fcff04090c0d0c0f1013141617191c1d1e1c1a1b1c1e1c1c19141210100b07090f141614110f0f0d090401fffffdfefaf7f2f0edececebebeceff1f1efeef0f4f6f5f2eff1f4f5f6f6f5f5f5f5f5f4f5f7f6f4f2f4f4f7f9f9f9ebfafbfaf7f8f6f5f4f1f0f0f2f2f4f3f1eff0f4f7fbfd00030407070a0c1115151515171b1e1d1d1c1d2022201e1d1e20212324231e1a1c1d1b1b1a17120f0e0a080500fdfbfbf9f5f2f1f0f0f0f0efedeef0f1eeebeaeaeceff0efedebe9e9e9e9eae9e8ebeef1f2f4f3f3f2f3f7f9eff2f3f4f9fafcfbfbfaf7f6f6f8f9f8f7f7f9f9f8f7f6f5f6f6f9fafcfbfbfafafaf7f7f7f8f8fafafbfbff0000fdfbfafcfdfffefe0003050503010001050a101313110f0d09070400fefcfe000101fcf9f8f7f6f4f5f4f8fcff01020100fdf8f7f9fc000102fdfbfbfdff000407083c03000034020e000c00008007000000010205060504040708060403050709080705040401fffdfbfdfdfcfaf8f7f7f7f7f5f5f5f5f4f6f6f5f4f6fbff00fffcfaf9f7f7f5f6f6f9fcfdfefefefdfcfdfdfdfd000202020303020103050609090c0e100f0a0909080501ffff00020303020201ff0000fffefcfcfbfbfdff0000f0f4f5f5f7f9fbfaf5f3f3f6f5f6f7f7f6f6f7f8f9f9fbfafbfdfe00000101020306080a0a09080706040303030403050204050704fcf7f6f9fbfdff000306090a0909090b0d0e0e0e0d0c0d0e0c07070a0c090603020100fefbfafafcfcfdfcfcfbfaf9fafbff000201ff0003060403f1f3f2f3f4f3f0eff0f1f2f2f2f0f0f1f1eff0f2f6f8fcff04090c0d0c0f1013141617191c1d1e1c1a1b1c1e1c1c19141210100b07090f141614110f0f0d090401fffffdfefaf7f2f0edececebebeceff1f1efeef0f4f6f5f2eff1f4f5f6f6f5f5f5f5f5f4f5f7f6f4f2f4f4f7f9f9f9ebfafbfaf7f8f6f5f4f1f0f0f2f2f4f3f1eff0f4f7fbfd00030407070a0c1115151515171b1e1d1d1c1d2022201e1d1e20212324231e1a1c1d1b1b1a17120f0e0a080500fdfbfbf9f5f2f1f0f0f0f0efedeef0f1eeebeaeaeceff0efedebe9e9e9e9eae9e8ebeef1f2f4f3f3f2f3f7f9eff2f3f4f9fafcfbfbfaf7f6f6f8f9f8f7f7f9f9f8f7f6f5f6f6f9fafcfbfbfafafaf7f7f7f8f8fafafbfbff0000fdfbfafcfdfffefe0003050503010001050a101313110f0d09070400fefcfe000101fcf9f8f7f6f4f5f4f8fcff01020100fdf8f7f9fc000102fdfbfbfdff000407087805000014000e000f00008005550007890312000587000781010026040fe3079405000014000e000f00008005550007890312000587000781010026040fe307b005000008000e00080000809901000000000000c005000008000e00080000809901000000000000d005000008000e0002000000000000005a009001e005000008000e0002000000000000005a009001f005000004000e00050000803a690200fc05000004000e00050000803a690200
''')
#rsp=unhex('0000a80200000c0000000800000074000e0003000080070000007e7e7c767d7a737a807c7c7975847f85858184888786888a8a8a8c8d8b85878c8689878689898484837d8b8b8289898d8f8c90908f86858c8b8d90908b848f8694928a8e908d8e8d898d8d8c8e8f8d8c878986808a8a818686848187888c8e7e7e85888989857f8077777b7872767c7e8400000074000e0003000080070000007e7e7c767d7a737a807c7c7975847f85858184888786888a8a8a8c8d8b85878c8689878689898484837d8b8b8289898d8f8c90908f86858c8b8d90908b848f8694928a8e908d8e8d898d8d8c8e8f8d8c878986808a8a818686848187888c8e7e7e85888989857f8077777b7872767c7e0001000034020e000c00008007000000010205060504040708060403050709080705040401fffdfbfdfdfcfaf8f7f7f7f7f5f5f5f5f4f6f6f5f4f6fbff00fffcfaf9f7f7f5f6f6f9fcfdfefefefdfcfdfdfdfd000202020303020103050609090c0e100f0a0909080501ffff00020303020201ff0000fffefcfcfbfbfdff0000f0f4f5f5f7f9fbfaf5f3f3f6f5f6f7f7f6f6f7f8f9f9fbfafbfdfe00000101020306080a0a09080706040303030403050204050704fcf7f6f9fbfdff000306090a0909090b0d0e0e0e0d0c0d0e0c07070a0c090603020100fefbfafafcfcfdfcfcfbfaf9fafbff000201ff0003060403f1f3f2f3f4f3f0eff0f1f2f2f2f0f0f1f1eff0f2f6f8fcff04090c0d0c0f1013141617191c1d1e1c1a1b1c1e1c1c19141210100b07090f141614110f0f0d090401fffffdfefaf7f2f0edececebebeceff1f1efeef0f4f6f5f2eff1f4f5f6f6f5f5f5f5f5f4f5f7f6f4f2f4f4f7f9f9f9ebfafbfaf7f8f6f5f4f1f0f0f2f2f4f3f1eff0f4f7fbfd00030407070a0c1115151515171b1e1d1d1c1d2022201e1d1e20212324231e1a1c1d1b1b1a17120f0e0a080500fdfbfbf9f5f2f1f0f0f0f0efedeef0f1eeebeaeaeceff0efedebe9e9e9e9eae9e8ebeef1f2f4f3f3f2f3f7f9eff2f3f4f9fafcfbfbfaf7f6f6f8f9f8f7f7f9f9f8f7f6f5f6f6f9fafcfbfbfafafaf7f7f7f8f8fafafbfbff0000fdfbfafcfdfffefe0003050503010001050a101313110f0d09070400fefcfe000101fcf9f8f7f6f4f5f4f8fcff01020100fdf8f7f9fc000102fdfbfbfdff000407083c03000034020e000c00008007000000010205060504040708060403050709080705040401fffdfbfdfdfcfaf8f7f7f7f7f5f5f5f5f4f6f6f5f4f6fbff00fffcfaf9f7f7f5f6f6f9fcfdfefefefdfcfdfdfdfd000202020303020103050609090c0e100f0a0909080501ffff00020303020201ff0000fffefcfcfbfbfdff0000f0f4f5f5f7f9fbfaf5f3f3f6f5f6f7f7f6f6f7f8f9f9fbfafbfdfe00000101020306080a0a09080706040303030403050204050704fcf7f6f9fbfdff000306090a0909090b0d0e0e0e0d0c0d0e0c07070a0c090603020100fefbfafafcfcfdfcfcfbfaf9fafbff000201ff0003060403f1f3f2f3f4f3f0eff0f1f2f2f2f0f0f1f1eff0f2f6f8fcff04090c0d0c0f1013141617191c1d1e1c1a1b1c1e1c1c19141210100b07090f141614110f0f0d090401fffffdfefaf7f2f0edececebebeceff1f1efeef0f4f6f5f2eff1f4f5f6f6f5f5f5f5f5f4f5f7f6f4f2f4f4f7f9f9f9ebfafbfaf7f8f6f5f4f1f0f0f2f2f4f3f1eff0f4f7fbfd00030407070a0c1115151515171b1e1d1d1c1d2022201e1d1e20212324231e1a1c1d1b1b1a17120f0e0a080500fdfbfbf9f5f2f1f0f0f0f0efedeef0f1eeebeaeaeceff0efedebe9e9e9e9eae9e8ebeef1f2f4f3f3f2f3f7f9eff2f3f4f9fafcfbfbfaf7f6f6f8f9f8f7f7f9f9f8f7f6f5f6f6f9fafcfbfbfafafaf7f7f7f8f8fafafbfbff0000fdfbfafcfdfffefe0003050503010001050a101313110f0d09070400fefcfe000101fcf9f8f7f6f4f5f4f8fcff01020100fdf8f7f9fc000102fdfbfbfdff000407087805000014000e000f00008005550007890312000587000781010026040fe3079405000014000e000f00008005550007890312000587000781010026040fe307b005000008000e00080000809901000000000000c005000008000e00080000809901000000000000d005000008000e0002000000000000005a009001e005000008000e0002000000000000005a009001f005000004000e00050000803a690200fc05000004000e00050000803a690200')
assert_status(rsp)
rsp=rsp[2:]
@ -440,22 +198,33 @@ class CaptureMode(Enum):
class Sensor():
calib_data=b''
def open(self):
def open(self, load_calib_data=True):
self.device_info = identify_sensor()
print('Opening sensor: %s' % self.device_info.name)
self.type_info = SensorTypeInfo.get_by_type(self.device_info.type)
if self.device_info.type == 0x199:
if self.device_info.type == 0x199 or self.device_info.type == 0xdb:
self.lines_per_frame = 0xe0 # valid for 0x199, TODO: figure out where this number is coming from
self.bytes_per_line = 0x78
self.key_calibration_line = 0x38 # (lines_per_calibration_data/2), but hardcoded for sensor type 0x199
else:
raise Exception('Device %s is not supported', self.device_info.name)
raise Exception('Device %s is not supported (sensor type 0x%x)' % (self.device_info.name, self.device_info.type))
factory_bits = get_factory_bits(0x0e00)
self.factory_calibration_values = factory_bits[3][4:]
if load_calib_data and isfile(calib_data_path):
with open(calib_data_path, 'rb') as f:
self.calib_data = f.read()
print('Calibration data loaded from a file.')
else:
self.calib_data = b''
print('Warning: no calibration data was loaded. Consider calibrating the sensor.')
def save(self):
with open(calib_data_path, 'wb') as f:
f.write(self.calib_data)
# This is the exact logic from the DLL.
# If it looks broken that was probably intended.
@ -529,7 +298,7 @@ class Sensor():
return bytes(b)
def process_calibration_results(self, raw_calib_data):
def average(self, raw_calib_data):
frame_size = self.lines_per_frame * self.bytes_per_line
interleave_lines = self.lines_per_frame // self.type_info.lines_per_calibration_data # 2, TODO: algo is quite different when it is 1
input_frames = 3 # len(raw_calib_data)//lines_per_frame//bytes_per_line, TODO: workout where it's really comming from
@ -547,7 +316,12 @@ class Sensor():
# calculate averages across interleaved lines
frame=[bytes([sum(i)//len(f) for i in zip(*f)]) for f in frame]
return b''.join(frame)
def process_calibration_results(self, cooked_data):
frame=chunks(cooked_data, self.bytes_per_line)
# apply scaling factors
frame=[f[:8] + bytes(map(scale, f[8:])) for f in frame]
frame=b''.join(frame)
@ -671,4 +445,279 @@ class Sensor():
return pack('<BHH', 2, self.bytes_per_line, req_lines) + prg.merge_chunks(chunks)
def persist_clean_slate(self, clean_slate):
start = read_flash(6, 0, 0x44)
if start != b'\xff' * 0x44:
if clean_slate[:0x44] == start:
print('Calibration data already matches the data on the flash.')
return
else:
print('Calibration flash already written. Erasing.')
erase_flash(6)
write_flash_all(6, 0, clean_slate)
def calibrate(self):
for i in range(0, 3):
print('Calibration iteration %d...' % i)
rsp = tls.cmd(self.build_cmd_02(CaptureMode.CALIBRATE))
assert_status(rsp)
self.process_calibration_results(self.average(usb.read_82()))
print('Requesting a blank image...')
# Get the "clean slate" image to store on the flash for fine-grained after-capture adjustments
rsp = tls.cmd(self.build_cmd_02(CaptureMode.CALIBRATE))
assert_status(rsp)
clean_slate = self.average(usb.read_82())
clean_slate = pack('<H', len(clean_slate)) + clean_slate
clean_slate = clean_slate + pack('<H', 0) # TODO: still don't know what this zero is for
clean_slate = pack('<H', len(clean_slate)) + sha256(clean_slate).digest() + b'\0'*0x20 + clean_slate
clean_slate = unhexlify('0250') + clean_slate
self.persist_clean_slate(clean_slate)
self.save()
sensor = Sensor()
def capture(mode):
usb.purge_int_queue()
assert_status(tls.app(sensor.build_cmd_02(mode)))
b=usb.wait_int()
if b[0] != 0:
raise Exception('Unexpected interrupt type %s' % hexlify(b).decode())
try:
wait_for_finger()
#wait_till_finished()
while True:
b=usb.wait_int()
if b[0] != 3:
raise Exception('Unexpected interrupt type %s' % hexlify(b).decode())
if b[2] & 4:
break
finally:
res=stop_prg()
assert_status(res)
res = res[2:]
l, res = res[:4], res[4:]
l, = unpack('<L', l)
if l != len(res):
raise Exception('Response size does not match %d != %d', l, len(res))
x, y, w1, w2, error = unpack('<HHHHL', res)
return error
def enrollment_update_start(key=0):
rsp=tls.app(pack('<BLL', 0x68, key, 0))
assert_status(rsp)
new_key, = unpack('<L', rsp[2:])
usb.wait_int()
return new_key
def enrollment_update_end():
assert_status(tls.app(pack('<BL', 0x69, 0)))
def enrollment_update(prev):
write_enable()
rsp=tls.app(b'\x6b' + prev)
assert_status(rsp)
flush_changes()
return rsp[2:]
def append_new_image(key=0, prev=b''):
enrollment_update(prev)
usb.wait_int()
res = enrollment_update(prev)
l, res = res[:2], res[2:]
l, = unpack('<H', l)
if l != len(res):
raise Exception('Response size does not match %d != %d', l, len(res))
magic_len = 0x38 # hardcoded in the DLL
template = header = tid = None
while len(res) > 0:
tag, l = unpack('<HH', res[:4])
if tag == 0:
template = res[:magic_len+l]
elif tag == 1:
header = res[magic_len:magic_len+l]
elif tag == 3:
tid = res[magic_len:magic_len+l]
else:
print('Ignoring unknown tag %x' % tag)
res=res[magic_len+l:]
return (header, template, tid)
def make_finger_data(subtype, template, tid):
template = pack('<HH', 1, len(template)) + template
tid = pack('<HH', 2, len(tid)) + tid
tinfo = template + tid
tinfo = pack('<HHHH', subtype, 3, len(tinfo), 0x20) + tinfo
tinfo += b'\0' * 0x20
return tinfo
def enroll(identity, subtype):
key=0
template=b''
print('Waiting for a finger...')
while True:
glow_start_scan()
try:
err = capture(CaptureMode.ENROLL)
if err != 0:
print('Error %08x, try again' % err)
continue
except Exception as e:
print('Capture failed (%s), try again' % repr(e))
sleep(1)
continue
key = enrollment_update_start(key)
header, template, tid = append_new_image(key, template)
enrollment_update_end()
print(hexlify(header))
if tid:
break
# TODO check for duplicates
tinfo = make_finger_data(subtype, template, tid)
usr=db.lookup_user(identity)
if usr == None:
usr = db.new_user(identity)
else:
usr = usr.dbid
recid = db.new_finger(usr, tinfo)
glow_end_enroll()
print('All done')
return recid
def parse_dict(x):
rc={}
while len(x) > 0:
(t, l), x = unpack('<HH', x[:4]), x[4:]
rc[t], x = x[:l], x[l:]
return rc
def identify():
glow_start_scan()
try:
err = capture(CaptureMode.IDENTIFY)
if err != 0:
raise Exception('Capture failed: %08x' % err)
# which finger?
stg_id=0 # match against any storage
usr_id=0 # match against any user
cmd=pack('<BBBHHHHH', 0x5e, 2, 0xff, stg_id, usr_id, 1, 0,0)
rsp=tls.app(cmd)
assert_status(rsp)
b = usb.wait_int()
if b[0] != 3:
raise Exception('Identification failed: %s' % hexlify(b).decode())
rsp = tls.app(unhexlify('6000000000'))
assert_status(rsp)
rsp = rsp[2:]
finally:
# finish
assert_status(tls.app(unhexlify('6200000000')))
(l,), rsp = unpack('<H', rsp[:2]), rsp[2:]
if l != len(rsp):
raise Exception('Response size does not match')
rsp=parse_dict(rsp)
#for k in rsp:
# print('%04x: %s (%d)' % (k, hexlify(rsp[k]).decode(), len(rsp[k])))
#on 0097
#0001: 09000000 (4)
#0003: f500 (2)
#0004: 8dee792532d3432d41c872fd4d6d590fbc855ad449cf2753cd919eb9c94675c6 (32)
#0005: 0000000000000000000000000000000000000000000000000000000000000000 (32)
#0008: 0a00 (2) <-- finger record db id
#0002: 010b0000 (4)
#0006: 00000000000000000000000000000000000000000000000000000000000000000000000000000000 (40)
#on 009a (no finger record db id)
#0000 8a00
# 0100 0400 05000000
# 0300 0200 f500
# 0400 2000 b147dd1eda8da322fb7a2a51d0eab6fe94bef46c05204fbefb1fd16360903791
# 0500 2000 0000000000000000000000000000000000000000000000000000000000000000
# 0200 0400 650a0000
# 0600 2800 00000000000000000000000000000000000000000000000000000000000000000000000000000000
usrid, subtype, hsh = rsp[1], rsp[3], rsp[4]
usrid, = unpack('<L', usrid)
subtype, = unpack('<H', subtype)
usr = db.get_user(usrid)
fingerids = [f['dbid'] for f in usr.fingers if f['subtype'] == subtype]
if len(fingerids) != 1:
raise Exception('Unexpected matching finger count')
finger_record = db.get_record_children(fingerids[0])
# Device won't let you add more than one data blob
if len(finger_record.children) > 1:
raise Exception('Expected only one child record for finger')
print('Recognised finger %02x (%s) from user %s' % (subtype, subtype_to_string(subtype), repr(usr.identity)))
print('Template hash: %s' % hexlify(hsh).decode())
if len(finger_record.children) > 0:
if finger_record.children[0]['type'] != 8:
raise Exception('Expected data blob as a finger child')
blob_id = finger_record.children[0]['dbid']
blob = db.get_record_value(blob_id).value
tag, sz = unpack('<HH', blob[:4])
val = blob[4:4+sz]
print('Data blob associated with the finger: %04x: %s' % (tag, hexlify(val).decode()))
return rsp